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