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