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