01f8f647eca00f3fabf72b91fc3e0460d0bbe64c
[gnupg.git] / dirmngr / ldap-wrapper-ce.c
1 /* ldap-wrapper-ce.c - LDAP access via W32 threads
2  * Copyright (C) 2010 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <https://www.gnu.org/licenses/>.
18  */
19
20 /*
21    Alternative wrapper for use with WindowsCE.  Under WindowsCE the
22    number of processes is strongly limited (32 processes including the
23    kernel processes) and thus we don't use the process approach but
24    implement a wrapper based on native threads.
25
26    See ldap-wrapper.c for  the standard wrapper interface.
27  */
28
29 #include <config.h>
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <errno.h>
35 #include <unistd.h>
36 #include <fcntl.h>
37 #include <time.h>
38 #include <npth.h>
39 #include <assert.h>
40
41 #include "dirmngr.h"
42 #include "misc.h"
43 #include "ldap-wrapper.h"
44
45 #ifdef USE_LDAPWRAPPER
46 # error This module is not expected to be build.
47 #endif
48
49
50
51 /* Read a fixed amount of data from READER into BUFFER.  */
52 static gpg_error_t
53 read_buffer (ksba_reader_t reader, unsigned char *buffer, size_t count)
54 {
55   gpg_error_t err;
56   size_t nread;
57
58   while (count)
59     {
60       err = ksba_reader_read (reader, buffer, count, &nread);
61       if (err)
62         return err;
63       buffer += nread;
64       count -= nread;
65     }
66   return 0;
67 }
68
69
70
71
72 /* Start the reaper thread for this wrapper.  */
73 void
74 ldap_wrapper_launch_thread (void)
75 {
76   /* Not required.  */
77 }
78
79
80
81
82
83 /* Wait until all ldap wrappers have terminated.  We assume that the
84    kill has already been sent to all of them.  */
85 void
86 ldap_wrapper_wait_connections ()
87 {
88   /* Not required.  */
89 }
90
91
92 /* Cleanup all resources held by the connection associated with
93    CTRL.  This is used after a cancel to kill running wrappers.  */
94 void
95 ldap_wrapper_connection_cleanup (ctrl_t ctrl)
96 {
97   (void)ctrl;
98
99   /* Not required.  */
100 }
101
102
103 \f
104 /* The cookie we use to implement the outstream of the wrapper thread.  */
105 struct outstream_cookie_s
106 {
107   int refcount; /* Reference counter - possible values are 1 and 2.  */
108
109   /* We don't need a mutex for the conditions, as npth provides a
110      simpler condition interface that relies on the global lock.  This
111      can be used if we never yield between testing the condition and
112      waiting on it.  */
113   npth_cond_t wait_data; /* Condition that data is available.  */
114   npth_cond_t wait_space; /* Condition that space is available.  */
115
116   int eof_seen;       /* EOF indicator.  */
117   char buffer[4000];  /* Data ring buffer.  */
118   size_t buffer_len;  /* The amount of data in the BUFFER.  */
119   size_t buffer_pos;  /* The next read position of the BUFFER.  */
120   size_t buffer_read_pos;  /* The next read position of the BUFFER.  */
121 };
122
123 #define BUFFER_EMPTY(c) ((c)->buffer_len == 0)
124 #define BUFFER_FULL(c) ((c)->buffer_len == DIM((c)->buffer))
125 #define BUFFER_DATA_AVAILABLE(c) ((c)->buffer_len)
126 #define BUFFER_SPACE_AVAILABLE(c) (DIM((c)->buffer) - (c)->buffer_len)
127 #define BUFFER_INC_POS(c,n) (c)->buffer_pos = ((c)->buffer_pos + (n)) % DIM((c)->buffer)
128 #define BUFFER_CUR_POS(c) (&(c)->buffer[(c)->buffer_pos])
129 #define BUFFER_INC_READ_POS(c,n) (c)->buffer_read_pos = ((c)->buffer_read_pos + (n)) % DIM((c)->buffer)
130 #define BUFFER_CUR_READ_POS(c) (&(c)->buffer[(c)->buffer_read_pos])
131
132 static int
133 buffer_get_data (struct outstream_cookie_s *cookie, char *dst, int cnt)
134 {
135   int amount;
136   int left;
137   int chunk;
138
139   amount = cnt;
140   if (BUFFER_DATA_AVAILABLE (cookie) < amount)
141     amount = BUFFER_DATA_AVAILABLE (cookie);
142   left = amount;
143
144   /* How large is the part up to the end of the buffer array?  */
145   chunk = DIM(cookie->buffer) - cookie->buffer_pos;
146   if (chunk > left)
147     chunk = left;
148
149   memcpy (dst, BUFFER_CUR_READ_POS (cookie), chunk);
150   BUFFER_INC_READ_POS (cookie, chunk);
151   left -= chunk;
152   dst += chunk;
153
154   if (left)
155     {
156       memcpy (dst, BUFFER_CUR_READ_POS (cookie), left);
157       BUFFER_INC_READ_POS (cookie, left);
158     }
159
160   return amount;
161 }
162
163
164 static int
165 buffer_put_data (struct outstream_cookie_s *cookie, const char *src, int cnt)
166 {
167   int amount;
168   int remain;
169   int left;
170   int chunk;
171
172   remain = DIM(cookie->buffer) - cookie->buffer_len;
173
174   amount = cnt;
175   if (remain < amount)
176     amount = remain;
177   left = amount;
178
179   /* How large is the part up to the end of the buffer array?  */
180   chunk = DIM(cookie->buffer) - cookie->buffer_pos;
181   if (chunk > left)
182     chunk = left;
183
184   memcpy (BUFFER_CUR_POS (cookie), src, chunk);
185   BUFFER_INC_POS (cookie, chunk);
186   left -= chunk;
187   src += chunk;
188
189   if (left)
190     {
191       memcpy (BUFFER_CUR_POS (cookie), src, left);
192       BUFFER_INC_POS (cookie, left);
193     }
194
195   cookie->buffer_len -= amount;
196   return amount;
197 }
198
199
200 /* The writer function for the outstream.  This is used to transfer
201    the output of the ldap wrapper thread to the ksba reader object.  */
202 static gpgrt_ssize_t
203 outstream_cookie_writer (void *cookie_arg, const void *buffer, size_t size)
204 {
205   struct outstream_cookie_s *cookie = cookie_arg;
206   const char *src;
207   ssize_t nwritten = 0;
208   int res;
209   ssize_t amount = 0;
210
211   src = buffer;
212   do
213     {
214       int was_empty = 0;
215
216       /* Wait for free space.  */
217       while (BUFFER_FULL(cookie))
218         {
219           /* Buffer is full:  Wait for space.  */
220           res = npth_cond_wait (&cookie->wait_space, NULL);
221           if (res)
222             {
223               gpg_err_set_errno (res);
224               return -1;
225             }
226         }
227
228       if (BUFFER_EMPTY(cookie))
229         was_empty = 1;
230
231       /* Copy data.  */
232       nwritten = buffer_put_data (cookie, buffer, size);
233       size -= nwritten;
234       src += nwritten;
235       amount += nwritten;
236
237       if (was_empty)
238         npth_cond_signal (&cookie->wait_data);
239     }
240   while (size);  /* Until done.  */
241
242   return amount;
243 }
244
245
246 static void
247 outstream_release_cookie (struct outstream_cookie_s *cookie)
248 {
249   cookie->refcount--;
250   if (!cookie->refcount)
251     {
252       npth_cond_destroy (&cookie->wait_data);
253       npth_cond_destroy (&cookie->wait_space);
254       xfree (cookie);
255     }
256 }
257
258
259 /* Closer function for the outstream.  This deallocates the cookie if
260    it won't be used anymore.  */
261 static int
262 outstream_cookie_closer (void *cookie_arg)
263 {
264   struct outstream_cookie_s *cookie = cookie_arg;
265
266   if (!cookie)
267     return 0;  /* Nothing to do.  */
268
269   cookie->eof_seen = 1; /* (only useful if refcount > 1)  */
270
271   assert (cookie->refcount > 0);
272   outstream_release_cookie (cookie);
273   return 0;
274 }
275
276
277 /* The KSBA reader callback which takes the output of the ldap thread
278    form the outstream_cookie_writer and make it available to the ksba
279    reader.  */
280 static int
281 outstream_reader_cb (void *cb_value, char *buffer, size_t count,
282                      size_t *r_nread)
283 {
284   struct outstream_cookie_s *cookie = cb_value;
285   size_t nread = 0;
286   int was_full = 0;
287
288   if (!buffer && !count && !r_nread)
289     return gpg_error (GPG_ERR_NOT_SUPPORTED); /* Rewind is not supported.  */
290
291   *r_nread = 0;
292
293   while (BUFFER_EMPTY(cookie))
294     {
295       if (cookie->eof_seen)
296         return gpg_error (GPG_ERR_EOF);
297
298       /* Wait for data to become available.  */
299       npth_cond_wait (&cookie->wait_data, NULL);
300     }
301
302   if (BUFFER_FULL(cookie))
303     was_full = 1;
304
305   nread = buffer_get_data (cookie, buffer, count);
306
307   if (was_full)
308     {
309       npth_cond_signal (&cookie->wait_space);
310     }
311
312   *r_nread = nread;
313   return 0; /* Success.  */
314 }
315
316
317 /* This function is called by ksba_reader_release.  */
318 static void
319 outstream_reader_released (void *cb_value, ksba_reader_t r)
320 {
321   struct outstream_cookie_s *cookie = cb_value;
322
323   (void)r;
324
325   assert (cookie->refcount > 0);
326   outstream_release_cookie (cookie);
327 }
328
329
330
331 /* This function is to be used to release a context associated with the
332    given reader object.  This does not release the reader object, though. */
333 void
334 ldap_wrapper_release_context (ksba_reader_t reader)
335 {
336   (void)reader;
337   /* Nothing to do.  */
338 }
339
340
341 \f
342 /* Free a NULL terminated array of malloced strings and the array
343    itself.  */
344 static void
345 free_arg_list (char **arg_list)
346 {
347   int i;
348
349   if (arg_list)
350     {
351       for (i=0; arg_list[i]; i++)
352         xfree (arg_list[i]);
353       xfree (arg_list);
354     }
355 }
356
357
358 /* Copy ARGV into a new array and prepend one element as name of the
359    program (which is more or less a stub).  We need to allocate all
360    the strings to get ownership of them.  */
361 static gpg_error_t
362 create_arg_list (const char *argv[], char ***r_arg_list)
363 {
364   gpg_error_t err;
365   char **arg_list;
366   int i, j;
367
368   for (i = 0; argv[i]; i++)
369     ;
370   arg_list = xtrycalloc (i + 2, sizeof *arg_list);
371   if (!arg_list)
372     goto outofcore;
373
374   i = 0;
375   arg_list[i] = xtrystrdup ("<ldap-wrapper-thread>");
376   if (!arg_list[i])
377     goto outofcore;
378   i++;
379   for (j=0; argv[j]; j++)
380     {
381       arg_list[i] = xtrystrdup (argv[j]);
382       if (!arg_list[i])
383         goto outofcore;
384       i++;
385     }
386   arg_list[i] = NULL;
387   *r_arg_list = arg_list;
388   return 0;
389
390  outofcore:
391   err = gpg_error_from_syserror ();
392   log_error (_("error allocating memory: %s\n"), strerror (errno));
393   free_arg_list (arg_list);
394   *r_arg_list = NULL;
395   return err;
396
397 }
398
399
400 /* Parameters passed to the wrapper thread. */
401 struct ldap_wrapper_thread_parms
402 {
403   char **arg_list;
404   estream_t outstream;
405 };
406
407 /* The thread which runs the LDAP wrapper.  */
408 static void *
409 ldap_wrapper_thread (void *opaque)
410 {
411   struct ldap_wrapper_thread_parms *parms = opaque;
412
413   /*err =*/ ldap_wrapper_main (parms->arg_list, parms->outstream);
414
415   /* FIXME: Do we need to return ERR?  */
416
417   free_arg_list (parms->arg_list);
418   es_fclose (parms->outstream);
419   xfree (parms);
420   return NULL;
421 }
422
423
424
425 /* Start a new LDAP thread and returns a new libksba reader
426    object at READER.  ARGV is a NULL terminated list of arguments for
427    the wrapper.  The function returns 0 on success or an error code.  */
428 gpg_error_t
429 ldap_wrapper (ctrl_t ctrl, ksba_reader_t *r_reader, const char *argv[])
430 {
431   gpg_error_t err;
432   struct ldap_wrapper_thread_parms *parms;
433   npth_attr_t tattr;
434   es_cookie_io_functions_t outstream_func = { NULL };
435   struct outstream_cookie_s *outstream_cookie;
436   ksba_reader_t reader;
437   int res;
438   npth_t thread;
439
440   (void)ctrl;
441
442   *r_reader = NULL;
443
444   parms = xtrycalloc (1, sizeof *parms);
445   if (!parms)
446     return gpg_error_from_syserror ();
447
448   err = create_arg_list (argv, &parms->arg_list);
449   if (err)
450     {
451       xfree (parms);
452       return err;
453     }
454
455   outstream_cookie = xtrycalloc (1, sizeof *outstream_cookie);
456   if (!outstream_cookie)
457     {
458       err = gpg_error_from_syserror ();
459       free_arg_list (parms->arg_list);
460       xfree (parms);
461       return err;
462     }
463   outstream_cookie->refcount++;
464
465   res = npth_cond_init (&outstream_cookie->wait_data, NULL);
466   if (res)
467     {
468       free_arg_list (parms->arg_list);
469       xfree (parms);
470       return gpg_error_from_errno (res);
471     }
472   res = npth_cond_init (&outstream_cookie->wait_space, NULL);
473   if (res)
474     {
475       npth_cond_destroy (&outstream_cookie->wait_data);
476       free_arg_list (parms->arg_list);
477       xfree (parms);
478       return gpg_error_from_errno (res);
479     }
480
481   err = ksba_reader_new (&reader);
482   if (!err)
483     err = ksba_reader_set_release_notify (reader,
484                                           outstream_reader_released,
485                                           outstream_cookie);
486   if (!err)
487     err = ksba_reader_set_cb (reader,
488                               outstream_reader_cb, outstream_cookie);
489   if (err)
490     {
491       log_error (_("error initializing reader object: %s\n"),
492                  gpg_strerror (err));
493       ksba_reader_release (reader);
494       outstream_release_cookie (outstream_cookie);
495       free_arg_list (parms->arg_list);
496       xfree (parms);
497       return err;
498     }
499
500
501   outstream_func.func_write = outstream_cookie_writer;
502   outstream_func.func_close = outstream_cookie_closer;
503   parms->outstream = es_fopencookie (outstream_cookie, "wb", outstream_func);
504   if (!parms->outstream)
505     {
506       err = gpg_error_from_syserror ();
507       ksba_reader_release (reader);
508       outstream_release_cookie (outstream_cookie);
509       free_arg_list (parms->arg_list);
510       xfree (parms);
511       return err;
512     }
513   outstream_cookie->refcount++;
514
515   res = npth_attr_init(&tattr);
516   if (res)
517     {
518       err = gpg_error_from_errno (res);
519       ksba_reader_release (reader);
520       free_arg_list (parms->arg_list);
521       es_fclose (parms->outstream);
522       xfree (parms);
523       return err;
524     }
525   npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED);
526
527   res = npth_create (&thread, &tattr, ldap_wrapper_thread, parms);
528   npth_attr_destroy (&tattr);
529   if (res)
530     {
531       err = gpg_error_from_errno (res);
532       log_error ("error spawning ldap wrapper thread: %s\n",
533                  strerror (res) );
534     }
535   else
536     parms = NULL; /* Now owned by the thread.  */
537
538   if (parms)
539     {
540       free_arg_list (parms->arg_list);
541       es_fclose (parms->outstream);
542       xfree (parms);
543     }
544   if (err)
545     {
546       ksba_reader_release (reader);
547       return err;
548     }
549
550   /* Need to wait for the first byte so we are able to detect an empty
551      output and not let the consumer see an EOF without further error
552      indications.  The CRL loading logic assumes that after return
553      from this function, a failed search (e.g. host not found ) is
554      indicated right away. */
555   {
556     unsigned char c;
557
558     err = read_buffer (reader, &c, 1);
559     if (err)
560       {
561         ksba_reader_release (reader);
562         reader = NULL;
563         if (gpg_err_code (err) == GPG_ERR_EOF)
564           return gpg_error (GPG_ERR_NO_DATA);
565         else
566           return err;
567       }
568     ksba_reader_unread (reader, &c, 1);
569   }
570
571   *r_reader = reader;
572
573   return 0;
574 }