Add code for a threaded LDAP access to replace the wrapper process.
[gnupg.git] / dirmngr / dirmngr_ldap.c
1 /* dirmngr-ldap.c  -  The LDAP helper for dirmngr.
2  *      Copyright (C) 2004 g10 Code GmbH
3  *
4  * This file is part of DirMngr.
5  *
6  * DirMngr 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 2 of the License, or
9  * (at your option) any later version.
10  *
11  * DirMngr 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, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <stddef.h>
26 #include <stdarg.h>
27 #include <string.h>
28 #ifdef HAVE_SIGNAL_H
29 # include <signal.h>
30 #endif
31 #include <errno.h>
32 #include <assert.h>
33 #include <sys/time.h>
34 #include <unistd.h>
35 #ifndef USE_LDAPWRAPPER
36 # include <pth.h>
37 #endif
38
39 #ifdef HAVE_W32_SYSTEM
40 #include <winsock2.h>
41 #include <winldap.h>
42 #include <fcntl.h>
43 #include "ldap-url.h"
44 #else
45 /* For OpenLDAP, to enable the API that we're using. */
46 #define LDAP_DEPRECATED 1
47 #include <ldap.h>
48 #endif
49
50
51 #define JNLIB_NEED_LOG_LOGV
52 #include "../common/logging.h"
53 #include "../common/argparse.h"
54 #include "../common/stringhelp.h"
55 #include "../common/mischelp.h"
56 #include "../common/strlist.h"
57
58 #include "i18n.h"
59 #include "util.h"
60
61 /* With the ldap wrapper, there is no need for the pth_enter and leave
62    functions; thus we redefine them to nops.  If we are not using the
63    ldap wrapper process we need to include the prototype for our
64    module's main function.  */
65 #ifdef USE_LDAPWRAPPER
66 # define pth_enter() do { } while (0)
67 # define pth_leave() do { } while (0)
68 #else
69 # include "./ldap-wrapper.h"
70 #endif
71
72 #define DEFAULT_LDAP_TIMEOUT 100 /* Arbitrary long timeout. */
73
74
75 /* Constants for the options.  */
76 enum 
77   {
78     oQuiet        = 'q',
79     oVerbose      = 'v',
80
81     oTimeout      = 500,
82     oMulti,
83     oProxy,
84     oHost,
85     oPort,
86     oUser,
87     oPass,
88     oEnvPass,
89     oDN,
90     oFilter,
91     oAttr,
92
93     oOnlySearchTimeout,
94     oLogWithPID
95   };
96
97
98 /* The list of options as used by the argparse.c code.  */
99 static ARGPARSE_OPTS opts[] = {
100   { oVerbose,  "verbose",   0, N_("verbose") },
101   { oQuiet,    "quiet",     0, N_("be somewhat more quiet") },
102   { oTimeout,  "timeout",   1, N_("|N|set LDAP timeout to N seconds")},
103   { oMulti,    "multi",     0, N_("return all values in"
104                                   " a record oriented format")},
105   { oProxy,    "proxy",     2,
106     N_("|NAME|ignore host part and connect through NAME")},
107   { oHost,     "host",      2, N_("|NAME|connect to host NAME")},
108   { oPort,     "port",      1, N_("|N|connect to port N")},
109   { oUser,     "user",      2, N_("|NAME|use user NAME for authentication")},
110   { oPass,     "pass",      2, N_("|PASS|use password PASS"
111                                   " for authentication")},
112   { oEnvPass,  "env-pass",  0, N_("take password from $DIRMNGR_LDAP_PASS")},
113   { oDN,       "dn",        2, N_("|STRING|query DN STRING")},
114   { oFilter,   "filter",    2, N_("|STRING|use STRING as filter expression")},
115   { oAttr,     "attr",      2, N_("|STRING|return the attribute STRING")},
116   { oOnlySearchTimeout, "only-search-timeout", 0, "@"},
117   { oLogWithPID,"log-with-pid", 0, "@"},
118   { 0, NULL, 0, NULL }
119 };
120
121
122 /* A structure with module options.  This is not a static variable
123    because if we are not build as a standalone binary, each thread
124    using this module needs to handle its own values.  */
125 struct my_opt_s
126 {
127   int quiet;
128   int verbose;
129   struct timeval timeout; /* Timeout for the LDAP search functions.  */
130   unsigned int alarm_timeout; /* And for the alarm based timeout.  */
131   int multi;
132
133   estream_t outstream;    /* Send output to thsi stream.  */
134
135   /* Note that we can't use const for the strings because ldap_* are
136      not defined that way.  */
137   char *proxy; /* Host and Port override.  */
138   char *user;  /* Authentication user.  */
139   char *pass;  /* Authentication password.  */
140   char *host;  /* Override host.  */
141   int port;    /* Override port.  */
142   char *dn;    /* Override DN.  */
143   char *filter;/* Override filter.  */
144   char *attr;  /* Override attribute.  */
145 };
146 typedef struct my_opt_s *my_opt_t;
147
148
149 /* Prototypes.  */
150 static void catch_alarm (int dummy);
151 static int process_url (my_opt_t myopt, const char *url);
152
153
154
155 /* Function called by argparse.c to display information.  */
156 #ifndef USE_LDAPWRAPPER
157 static const char *
158 my_strusage (int level)
159 {
160   const char *p;
161     
162   switch(level)
163     {
164     case 11: p = "dirmngr_ldap (GnuPG)";
165       break;
166     case 13: p = VERSION; break;
167     case 17: p = PRINTABLE_OS_NAME; break;
168     case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
169     case 49: p = PACKAGE_BUGREPORT; break;
170     case 1:
171     case 40: p =
172                _("Usage: dirmngr_ldap [options] [URL] (-h for help)\n");
173       break;
174     case 41: p =
175           _("Syntax: dirmngr_ldap [options] [URL]\n"
176             "Internal LDAP helper for Dirmngr.\n"
177             "Interface and options may change without notice.\n");
178       break;
179
180     default: p = NULL;
181     }
182   return p;
183 }
184 #endif /*!USE_LDAPWRAPPER*/
185
186
187 int
188 #ifdef USE_LDAPWRAPPER
189 main (int argc, char **argv)
190 #else
191 ldap_wrapper_main (char **argv, estream_t outstream)
192 #endif
193 {
194 #ifndef USE_LDAPWRAPPER
195   int argc;
196 #endif
197   ARGPARSE_ARGS pargs;
198   int any_err = 0;
199   char *p;
200   int only_search_timeout = 0;
201   struct my_opt_s my_opt_buffer;
202   my_opt_t myopt = &my_opt_buffer;
203   
204   memset (&my_opt_buffer, 0, sizeof my_opt_buffer);
205
206 #ifdef USE_LDAPWRAPPER
207   set_strusage (my_strusage);
208   log_set_prefix ("dirmngr_ldap", JNLIB_LOG_WITH_PREFIX); 
209   
210   /* Setup I18N and common subsystems. */
211   i18n_init();
212
213   init_common_subsystems (&argc, &argv);
214
215   es_set_binary (es_stdout);
216   myopt->outstream = es_stdout;
217 #else /*!USE_LDAPWRAPPER*/
218   myopt->outstream = outstream;
219   for (argc=0; argv[argc]; argc++)
220     ;
221 #endif /*!USE_LDAPWRAPPER*/
222
223   /* LDAP defaults */
224   myopt->timeout.tv_sec = DEFAULT_LDAP_TIMEOUT;
225   myopt->timeout.tv_usec = 0;
226   myopt->alarm_timeout = 0;
227
228   /* Parse the command line.  */
229   pargs.argc = &argc;
230   pargs.argv = &argv;
231   pargs.flags= 1;  /* Do not remove the args. */
232   while (arg_parse (&pargs, opts) )
233     {
234       switch (pargs.r_opt)
235         {
236         case oVerbose: myopt->verbose++; break;
237         case oQuiet: myopt->quiet++; break;
238         case oTimeout: 
239           myopt->timeout.tv_sec = pargs.r.ret_int; 
240           myopt->timeout.tv_usec = 0;
241           myopt->alarm_timeout = pargs.r.ret_int;
242           break;
243         case oOnlySearchTimeout: only_search_timeout = 1; break;
244         case oMulti: myopt->multi = 1; break;
245         case oUser: myopt->user = pargs.r.ret_str; break;
246         case oPass: myopt->pass = pargs.r.ret_str; break;
247         case oEnvPass:
248           myopt->pass = getenv ("DIRMNGR_LDAP_PASS");
249           break;
250         case oProxy: myopt->proxy = pargs.r.ret_str; break;
251         case oHost: myopt->host = pargs.r.ret_str; break;
252         case oPort: myopt->port = pargs.r.ret_int; break;
253         case oDN:   myopt->dn = pargs.r.ret_str; break;
254         case oFilter: myopt->filter = pargs.r.ret_str; break;
255         case oAttr: myopt->attr = pargs.r.ret_str; break;
256         case oLogWithPID:
257           {
258             unsigned int oldflags;
259             log_get_prefix (&oldflags);
260             log_set_prefix (NULL, oldflags | JNLIB_LOG_WITH_PID);
261           }
262           break;
263
264         default :
265 #ifdef USE_LDAPWRAPPER
266           pargs.err = ARGPARSE_PRINT_ERROR;
267 #else
268           pargs.err = ARGPARSE_PRINT_WARNING;  /* No exit() please.  */
269 #endif
270           break;
271         }
272     }
273
274   if (only_search_timeout)
275     myopt->alarm_timeout = 0;
276
277   if (myopt->proxy)
278     {
279       myopt->host = xstrdup (myopt->proxy);
280       p = strchr (myopt->host, ':');
281       if (p)
282         {
283           *p++ = 0;
284           myopt->port = atoi (p);
285         }
286       if (!myopt->port)
287         myopt->port = 389;  /* make sure ports gets overridden.  */
288     }
289         
290   if (myopt->port < 0 || myopt->port > 65535)
291     log_error (_("invalid port number %d\n"), myopt->port);
292
293 #ifdef USE_LDAPWRAPPER
294   if (log_get_errorcount (0))
295     exit (2);
296   if (argc < 1)
297     usage (1);
298 #else
299   /* All passed arguments should be fine in this case.  */
300   assert (argc);
301 #endif
302
303 #ifdef USE_LDAPWRAPPER
304   if (myopt->alarm_timeout)
305     {
306 #ifndef HAVE_W32_SYSTEM
307 # if defined(HAVE_SIGACTION) && defined(HAVE_STRUCT_SIGACTION)
308       struct sigaction act;
309       
310       act.sa_handler = catch_alarm;
311       sigemptyset (&act.sa_mask);
312       act.sa_flags = 0;
313       if (sigaction (SIGALRM,&act,NULL))
314 # else 
315       if (signal (SIGALRM, catch_alarm) == SIG_ERR)
316 # endif
317           log_fatal ("unable to register timeout handler\n");
318 #endif
319     }
320 #endif /*USE_LDAPWRAPPER*/
321
322   for (; argc; argc--, argv++)
323     if (process_url (myopt, *argv))
324       any_err = 1;
325
326
327   /* FIXME: Do we need to release stuff?  */
328   return any_err;
329 }
330
331
332 static void
333 catch_alarm (int dummy)
334 {
335   (void)dummy;
336   _exit (10);
337 }
338
339
340 static void
341 set_timeout (my_opt_t myopt)
342 {
343 #ifdef HAVE_W32_SYSTEM
344   /* FIXME for W32.  */
345   (void)myopt;
346 #else
347   if (myopt->alarm_timeout)
348     alarm (myopt->alarm_timeout);
349 #endif
350 }
351
352
353 /* Helper for fetch_ldap().  */
354 static int
355 print_ldap_entries (my_opt_t myopt, LDAP *ld, LDAPMessage *msg, char *want_attr)
356 {
357   LDAPMessage *item;
358   int any = 0;
359
360   for (pth_enter (), item = ldap_first_entry (ld, msg), pth_leave ();
361        item;
362        pth_enter (), item = ldap_next_entry (ld, item), pth_leave ())
363     {
364       BerElement *berctx;
365       char *attr;
366
367       if (myopt->verbose > 1)
368         log_info (_("scanning result for attribute `%s'\n"),
369                   want_attr? want_attr : "[all]");
370
371       if (myopt->multi)
372         { /*  Write item marker. */
373           if (es_fwrite ("I\0\0\0\0", 5, 1, myopt->outstream) != 1)
374             {
375               log_error (_("error writing to stdout: %s\n"),
376                          strerror (errno));
377               return -1;
378             }
379         }
380
381           
382       for (pth_enter (), attr = ldap_first_attribute (ld, item, &berctx),
383              pth_leave ();
384            attr;
385            pth_enter (), attr = ldap_next_attribute (ld, item, berctx),
386              pth_leave ())
387         {
388           struct berval **values;
389           int idx;
390
391           if (myopt->verbose > 1)
392             log_info (_("          available attribute `%s'\n"), attr);
393           
394           set_timeout (myopt);
395
396           /* I case we want only one attribute we do a case
397              insensitive compare without the optional extension
398              (i.e. ";binary").  Case insensitive is not really correct
399              but the best we can do.  */
400           if (want_attr)
401             {
402               char *cp1, *cp2;
403               int cmpres;
404
405               cp1 = strchr (want_attr, ';');
406               if (cp1)
407                 *cp1 = 0;
408               cp2 = strchr (attr, ';');
409               if (cp2)
410                 *cp2 = 0;
411               cmpres = ascii_strcasecmp (want_attr, attr);
412               if (cp1)
413                 *cp1 = ';';
414               if (cp2)
415                 *cp2 = ';';
416               if (cmpres)
417                 {
418                   ldap_memfree (attr);
419                   continue; /* Not found:  Try next attribute.  */
420                 }
421             }
422
423           pth_enter ();
424           values = ldap_get_values_len (ld, item, attr);
425           pth_leave ();
426
427           if (!values)
428             {
429               if (myopt->verbose)
430                 log_info (_("attribute `%s' not found\n"), attr);
431               ldap_memfree (attr);
432               continue;
433             }
434
435           if (myopt->verbose)
436             {
437               log_info (_("found attribute `%s'\n"), attr);
438               if (myopt->verbose > 1)
439                 for (idx=0; values[idx]; idx++)
440                   log_info ("         length[%d]=%d\n",
441                             idx, (int)values[0]->bv_len);
442               
443             }
444
445           if (myopt->multi)
446             { /*  Write attribute marker. */
447               unsigned char tmp[5];
448               size_t n = strlen (attr);
449
450               tmp[0] = 'A';
451               tmp[1] = (n >> 24);
452               tmp[2] = (n >> 16);
453               tmp[3] = (n >> 8);
454               tmp[4] = (n);
455               if (es_fwrite (tmp, 5, 1, myopt->outstream) != 1 
456                   || es_fwrite (attr, n, 1, myopt->outstream) != 1)
457                 {
458                   log_error (_("error writing to stdout: %s\n"),
459                              strerror (errno));
460                   ldap_value_free_len (values);
461                   ldap_memfree (attr);
462                   ber_free (berctx, 0);
463                   return -1;
464                 }
465             }
466
467           for (idx=0; values[idx]; idx++)
468             {
469               if (myopt->multi)
470                 { /* Write value marker.  */
471                   unsigned char tmp[5];
472                   size_t n = values[0]->bv_len;
473
474                   tmp[0] = 'V';
475                   tmp[1] = (n >> 24);
476                   tmp[2] = (n >> 16);
477                   tmp[3] = (n >> 8);
478                   tmp[4] = (n);
479
480                   if (es_fwrite (tmp, 5, 1, myopt->outstream) != 1)
481                     {
482                       log_error (_("error writing to stdout: %s\n"),
483                                  strerror (errno));
484                       ldap_value_free_len (values);
485                       ldap_memfree (attr);
486                       ber_free (berctx, 0);
487                       return -1;
488                     }
489                 }
490
491               if (es_fwrite (values[0]->bv_val, values[0]->bv_len,
492                              1, myopt->outstream) != 1)
493                 {
494                   log_error (_("error writing to stdout: %s\n"),
495                              strerror (errno));
496                   ldap_value_free_len (values);
497                   ldap_memfree (attr);
498                   ber_free (berctx, 0);
499                   return -1;
500                 }
501
502               any = 1;
503               if (!myopt->multi)
504                 break; /* Print only the first value.  */
505             }
506           ldap_value_free_len (values);
507           ldap_memfree (attr);
508           if (want_attr || !myopt->multi)
509             break; /* We only want to return the first attribute.  */
510         }
511       ber_free (berctx, 0);
512     } 
513
514   if (myopt->verbose > 1 && any)
515     log_info ("result has been printed\n");
516
517   return any?0:-1;
518 }
519
520
521
522 /* Helper for the URL based LDAP query. */
523 static int
524 fetch_ldap (my_opt_t myopt, const char *url, const LDAPURLDesc *ludp)
525 {
526   LDAP *ld;
527   LDAPMessage *msg;
528   int rc = 0;
529   char *host, *dn, *filter, *attrs[2], *attr;
530   int port;
531   int ret;
532
533   host     = myopt->host?   myopt->host   : ludp->lud_host;
534   port     = myopt->port?   myopt->port   : ludp->lud_port;
535   dn       = myopt->dn?     myopt->dn     : ludp->lud_dn;
536   filter   = myopt->filter? myopt->filter : ludp->lud_filter;
537   attrs[0] = myopt->attr?   myopt->attr   : ludp->lud_attrs? ludp->lud_attrs[0]:NULL;
538   attrs[1] = NULL;
539   attr = attrs[0];
540
541   if (!port)
542     port = (ludp->lud_scheme && !strcmp (ludp->lud_scheme, "ldaps"))? 636:389;
543
544   if (myopt->verbose)
545     {
546       log_info (_("processing url `%s'\n"), url);
547       if (myopt->user)
548         log_info (_("          user `%s'\n"), myopt->user);
549       if (myopt->pass)
550         log_info (_("          pass `%s'\n"), *myopt->pass?"*****":"");
551       if (host)
552         log_info (_("          host `%s'\n"), host);
553       log_info (_("          port %d\n"), port);
554       if (dn)
555         log_info (_("            DN `%s'\n"), dn);
556       if (filter)
557         log_info (_("        filter `%s'\n"), filter);
558       if (myopt->multi && !myopt->attr && ludp->lud_attrs)
559         {
560           int i;
561           for (i=0; ludp->lud_attrs[i]; i++)
562             log_info (_("          attr `%s'\n"), ludp->lud_attrs[i]);
563         }
564       else if (attr)
565         log_info (_("          attr `%s'\n"), attr);
566     }
567
568
569   if (!host || !*host)
570     {
571       log_error (_("no host name in `%s'\n"), url);
572       return -1;
573     }
574   if (!myopt->multi && !attr)
575     {
576       log_error (_("no attribute given for query `%s'\n"), url);
577       return -1;
578     }
579
580   if (!myopt->multi && !myopt->attr
581       && ludp->lud_attrs && ludp->lud_attrs[0] && ludp->lud_attrs[1])
582     log_info (_("WARNING: using first attribute only\n"));
583
584
585   set_timeout (myopt);
586   pth_enter ();
587   ld = ldap_init (host, port);
588   pth_leave ();
589   if (!ld)
590     {
591       log_error (_("LDAP init to `%s:%d' failed: %s\n"), 
592                  host, port, strerror (errno));
593       return -1;
594     }
595   pth_enter ();
596   ret = ldap_simple_bind_s (ld, myopt->user, myopt->pass);
597   pth_leave ();
598   if (ret)
599     {
600       log_error (_("binding to `%s:%d' failed: %s\n"), 
601                  host, port, strerror (errno));
602       /* FIXME: Need deinit (ld)?  */
603       return -1;
604     }
605
606   set_timeout (myopt);
607   pth_enter ();
608   rc = ldap_search_st (ld, dn, ludp->lud_scope, filter,
609                        myopt->multi && !myopt->attr && ludp->lud_attrs?
610                        ludp->lud_attrs:attrs,
611                        0,
612                        &myopt->timeout, &msg);
613   pth_leave ();
614   if (rc == LDAP_SIZELIMIT_EXCEEDED && myopt->multi)
615     {
616       if (es_fwrite ("E\0\0\0\x09truncated", 14, 1, myopt->outstream) != 1)
617         {
618           log_error (_("error writing to stdout: %s\n"), strerror (errno));
619           return -1;
620         }
621     }
622   else if (rc)
623     {
624       log_error (_("searching `%s' failed: %s\n"), 
625                  url, ldap_err2string (rc));
626       if (rc != LDAP_NO_SUCH_OBJECT)
627         {
628           /* FIXME: Need deinit (ld)?  */
629           /* Hmmm: Do we need to released MSG in case of an error? */
630           return -1;
631         }
632     }
633
634   rc = print_ldap_entries (myopt, ld, msg, myopt->multi? NULL:attr);
635
636   ldap_msgfree (msg);
637   /* FIXME: Need deinit (ld)?  */
638   return rc;
639 }
640
641
642
643
644 /* Main processing.  Take the URL and run the LDAP query. The result
645    is printed to stdout, errors are logged to the log stream. */
646 static int
647 process_url (my_opt_t myopt, const char *url)
648 {
649   int rc;
650   LDAPURLDesc *ludp = NULL;
651
652
653   if (!ldap_is_ldap_url (url))
654     {
655       log_error (_("`%s' is not an LDAP URL\n"), url);
656       return -1;
657     }
658
659   if (ldap_url_parse (url, &ludp))
660     {
661       log_error (_("`%s' is an invalid LDAP URL\n"), url);
662       return -1;
663     }
664
665   rc = fetch_ldap (myopt, url, ludp);
666
667   ldap_free_urldesc (ludp);
668   return rc;
669 }
670