1 /* dirmngr-ldap.c - The LDAP helper for dirmngr.
2 * Copyright (C) 2004 g10 Code GmbH
3 * Copyright (C) 2010 Free Software Foundation, Inc.
5 * This file is part of GnuPG.
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.
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.
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/>.
35 #ifndef USE_LDAPWRAPPER
39 #ifdef HAVE_W32_SYSTEM
40 # include <winsock2.h>
44 # include "ldap-url.h"
46 /* For OpenLDAP, to enable the API that we're using. */
47 # define LDAP_DEPRECATED 1
52 #define JNLIB_NEED_LOG_LOGV
53 #include <gpg-error.h>
54 #include "../common/logging.h"
55 #include "../common/argparse.h"
56 #include "../common/stringhelp.h"
57 #include "../common/mischelp.h"
58 #include "../common/strlist.h"
62 #include "../common/init.h"
64 /* With the ldap wrapper, there is no need for the npth_unprotect and leave
65 functions; thus we redefine them to nops. If we are not using the
66 ldap wrapper process we need to include the prototype for our
67 module's main function. */
68 #ifdef USE_LDAPWRAPPER
69 static void npth_unprotect (void) { }
70 static void npth_protect (void) { }
72 # include "./ldap-wrapper.h"
75 #ifdef HAVE_W32CE_SYSTEM
76 # include "w32-ldap-help.h"
77 # define my_ldap_init(a,b) \
78 _dirmngr_ldap_init ((a), (b))
79 # define my_ldap_simple_bind_s(a,b,c) \
80 _dirmngr_ldap_simple_bind_s ((a),(b),(c))
81 # define my_ldap_search_st(a,b,c,d,e,f,g,h) \
82 _dirmngr_ldap_search_st ((a), (b), (c), (d), (e), (f), (g), (h))
83 # define my_ldap_first_attribute(a,b,c) \
84 _dirmngr_ldap_first_attribute ((a),(b),(c))
85 # define my_ldap_next_attribute(a,b,c) \
86 _dirmngr_ldap_next_attribute ((a),(b),(c))
87 # define my_ldap_get_values_len(a,b,c) \
88 _dirmngr_ldap_get_values_len ((a),(b),(c))
89 # define my_ldap_free_attr(a) \
92 # define my_ldap_init(a,b) ldap_init ((a), (b))
93 # define my_ldap_simple_bind_s(a,b,c) ldap_simple_bind_s ((a), (b), (c))
94 # define my_ldap_search_st(a,b,c,d,e,f,g,h) \
95 ldap_search_st ((a), (b), (c), (d), (e), (f), (g), (h))
96 # define my_ldap_first_attribute(a,b,c) ldap_first_attribute ((a),(b),(c))
97 # define my_ldap_next_attribute(a,b,c) ldap_next_attribute ((a),(b),(c))
98 # define my_ldap_get_values_len(a,b,c) ldap_get_values_len ((a),(b),(c))
99 # define my_ldap_free_attr(a) ldap_memfree ((a))
102 #ifdef HAVE_W32_SYSTEM
103 typedef LDAP_TIMEVAL my_ldap_timeval_t;
105 typedef struct timeval my_ldap_timeval_t;
108 #define DEFAULT_LDAP_TIMEOUT 100 /* Arbitrary long timeout. */
111 /* Constants for the options. */
134 /* The list of options as used by the argparse.c code. */
135 static ARGPARSE_OPTS opts[] = {
136 { oVerbose, "verbose", 0, N_("verbose") },
137 { oQuiet, "quiet", 0, N_("be somewhat more quiet") },
138 { oTimeout, "timeout", 1, N_("|N|set LDAP timeout to N seconds")},
139 { oMulti, "multi", 0, N_("return all values in"
140 " a record oriented format")},
141 { oProxy, "proxy", 2,
142 N_("|NAME|ignore host part and connect through NAME")},
143 { oHost, "host", 2, N_("|NAME|connect to host NAME")},
144 { oPort, "port", 1, N_("|N|connect to port N")},
145 { oUser, "user", 2, N_("|NAME|use user NAME for authentication")},
146 { oPass, "pass", 2, N_("|PASS|use password PASS"
147 " for authentication")},
148 { oEnvPass, "env-pass", 0, N_("take password from $DIRMNGR_LDAP_PASS")},
149 { oDN, "dn", 2, N_("|STRING|query DN STRING")},
150 { oFilter, "filter", 2, N_("|STRING|use STRING as filter expression")},
151 { oAttr, "attr", 2, N_("|STRING|return the attribute STRING")},
152 { oOnlySearchTimeout, "only-search-timeout", 0, "@"},
153 { oLogWithPID,"log-with-pid", 0, "@"},
158 /* A structure with module options. This is not a static variable
159 because if we are not build as a standalone binary, each thread
160 using this module needs to handle its own values. */
165 my_ldap_timeval_t timeout;/* Timeout for the LDAP search functions. */
166 unsigned int alarm_timeout; /* And for the alarm based timeout. */
169 estream_t outstream; /* Send output to thsi stream. */
171 /* Note that we can't use const for the strings because ldap_* are
172 not defined that way. */
173 char *proxy; /* Host and Port override. */
174 char *user; /* Authentication user. */
175 char *pass; /* Authentication password. */
176 char *host; /* Override host. */
177 int port; /* Override port. */
178 char *dn; /* Override DN. */
179 char *filter;/* Override filter. */
180 char *attr; /* Override attribute. */
182 typedef struct my_opt_s *my_opt_t;
186 #ifndef HAVE_W32_SYSTEM
187 static void catch_alarm (int dummy);
189 static int process_url (my_opt_t myopt, const char *url);
193 /* Function called by argparse.c to display information. */
194 #ifdef USE_LDAPWRAPPER
196 my_strusage (int level)
202 case 11: p = "dirmngr_ldap (@GNUPG@)";
204 case 13: p = VERSION; break;
205 case 17: p = PRINTABLE_OS_NAME; break;
206 case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
207 case 49: p = PACKAGE_BUGREPORT; break;
210 _("Usage: dirmngr_ldap [options] [URL] (-h for help)\n");
213 _("Syntax: dirmngr_ldap [options] [URL]\n"
214 "Internal LDAP helper for Dirmngr\n"
215 "Interface and options may change without notice\n");
222 #endif /*!USE_LDAPWRAPPER*/
226 #ifdef USE_LDAPWRAPPER
227 main (int argc, char **argv)
229 ldap_wrapper_main (char **argv, estream_t outstream)
232 #ifndef USE_LDAPWRAPPER
238 int only_search_timeout = 0;
239 struct my_opt_s my_opt_buffer;
240 my_opt_t myopt = &my_opt_buffer;
241 char *malloced_buffer1 = NULL;
243 memset (&my_opt_buffer, 0, sizeof my_opt_buffer);
245 early_system_init ();
247 #ifdef USE_LDAPWRAPPER
248 set_strusage (my_strusage);
249 log_set_prefix ("dirmngr_ldap", JNLIB_LOG_WITH_PREFIX);
251 /* Setup I18N and common subsystems. */
254 init_common_subsystems (&argc, &argv);
256 es_set_binary (es_stdout);
257 myopt->outstream = es_stdout;
258 #else /*!USE_LDAPWRAPPER*/
259 myopt->outstream = outstream;
260 for (argc=0; argv[argc]; argc++)
262 #endif /*!USE_LDAPWRAPPER*/
265 myopt->timeout.tv_sec = DEFAULT_LDAP_TIMEOUT;
266 myopt->timeout.tv_usec = 0;
267 myopt->alarm_timeout = 0;
269 /* Parse the command line. */
272 pargs.flags= 1; /* Do not remove the args. */
273 while (arg_parse (&pargs, opts) )
277 case oVerbose: myopt->verbose++; break;
278 case oQuiet: myopt->quiet++; break;
280 myopt->timeout.tv_sec = pargs.r.ret_int;
281 myopt->timeout.tv_usec = 0;
282 myopt->alarm_timeout = pargs.r.ret_int;
284 case oOnlySearchTimeout: only_search_timeout = 1; break;
285 case oMulti: myopt->multi = 1; break;
286 case oUser: myopt->user = pargs.r.ret_str; break;
287 case oPass: myopt->pass = pargs.r.ret_str; break;
289 myopt->pass = getenv ("DIRMNGR_LDAP_PASS");
291 case oProxy: myopt->proxy = pargs.r.ret_str; break;
292 case oHost: myopt->host = pargs.r.ret_str; break;
293 case oPort: myopt->port = pargs.r.ret_int; break;
294 case oDN: myopt->dn = pargs.r.ret_str; break;
295 case oFilter: myopt->filter = pargs.r.ret_str; break;
296 case oAttr: myopt->attr = pargs.r.ret_str; break;
299 unsigned int oldflags;
300 log_get_prefix (&oldflags);
301 log_set_prefix (NULL, oldflags | JNLIB_LOG_WITH_PID);
306 #ifdef USE_LDAPWRAPPER
307 pargs.err = ARGPARSE_PRINT_ERROR;
309 pargs.err = ARGPARSE_PRINT_WARNING; /* No exit() please. */
315 if (only_search_timeout)
316 myopt->alarm_timeout = 0;
320 malloced_buffer1 = xtrystrdup (myopt->proxy);
321 if (!malloced_buffer1)
323 log_error ("error copying string: %s\n", strerror (errno));
326 myopt->host = malloced_buffer1;
327 p = strchr (myopt->host, ':');
331 myopt->port = atoi (p);
334 myopt->port = 389; /* make sure ports gets overridden. */
337 if (myopt->port < 0 || myopt->port > 65535)
338 log_error (_("invalid port number %d\n"), myopt->port);
340 #ifdef USE_LDAPWRAPPER
341 if (log_get_errorcount (0))
346 /* All passed arguments should be fine in this case. */
350 #ifdef USE_LDAPWRAPPER
351 if (myopt->alarm_timeout)
353 #ifndef HAVE_W32_SYSTEM
354 # if defined(HAVE_SIGACTION) && defined(HAVE_STRUCT_SIGACTION)
355 struct sigaction act;
357 act.sa_handler = catch_alarm;
358 sigemptyset (&act.sa_mask);
360 if (sigaction (SIGALRM,&act,NULL))
362 if (signal (SIGALRM, catch_alarm) == SIG_ERR)
364 log_fatal ("unable to register timeout handler\n");
367 #endif /*USE_LDAPWRAPPER*/
369 for (; argc; argc--, argv++)
370 if (process_url (myopt, *argv))
373 xfree (malloced_buffer1);
377 #ifndef HAVE_W32_SYSTEM
379 catch_alarm (int dummy)
387 set_timeout (my_opt_t myopt)
389 #ifdef HAVE_W32_SYSTEM
393 if (myopt->alarm_timeout)
394 alarm (myopt->alarm_timeout);
399 /* Helper for fetch_ldap(). */
401 print_ldap_entries (my_opt_t myopt, LDAP *ld, LDAPMessage *msg, char *want_attr)
406 for (npth_unprotect (), item = ldap_first_entry (ld, msg), npth_protect ();
408 npth_unprotect (), item = ldap_next_entry (ld, item), npth_protect ())
413 if (myopt->verbose > 1)
414 log_info (_("scanning result for attribute '%s'\n"),
415 want_attr? want_attr : "[all]");
418 { /* Write item marker. */
419 if (es_fwrite ("I\0\0\0\0", 5, 1, myopt->outstream) != 1)
421 log_error (_("error writing to stdout: %s\n"),
428 for (npth_unprotect (), attr = my_ldap_first_attribute (ld, item, &berctx),
431 npth_unprotect (), attr = my_ldap_next_attribute (ld, item, berctx),
434 struct berval **values;
437 if (myopt->verbose > 1)
438 log_info (_(" available attribute '%s'\n"), attr);
442 /* I case we want only one attribute we do a case
443 insensitive compare without the optional extension
444 (i.e. ";binary"). Case insensitive is not really correct
445 but the best we can do. */
451 cp1 = strchr (want_attr, ';');
454 cp2 = strchr (attr, ';');
457 cmpres = ascii_strcasecmp (want_attr, attr);
464 my_ldap_free_attr (attr);
465 continue; /* Not found: Try next attribute. */
470 values = my_ldap_get_values_len (ld, item, attr);
476 log_info (_("attribute '%s' not found\n"), attr);
477 my_ldap_free_attr (attr);
483 log_info (_("found attribute '%s'\n"), attr);
484 if (myopt->verbose > 1)
485 for (idx=0; values[idx]; idx++)
486 log_info (" length[%d]=%d\n",
487 idx, (int)values[0]->bv_len);
492 { /* Write attribute marker. */
493 unsigned char tmp[5];
494 size_t n = strlen (attr);
501 if (es_fwrite (tmp, 5, 1, myopt->outstream) != 1
502 || es_fwrite (attr, n, 1, myopt->outstream) != 1)
504 log_error (_("error writing to stdout: %s\n"),
506 ldap_value_free_len (values);
507 my_ldap_free_attr (attr);
508 ber_free (berctx, 0);
513 for (idx=0; values[idx]; idx++)
516 { /* Write value marker. */
517 unsigned char tmp[5];
518 size_t n = values[0]->bv_len;
526 if (es_fwrite (tmp, 5, 1, myopt->outstream) != 1)
528 log_error (_("error writing to stdout: %s\n"),
530 ldap_value_free_len (values);
531 my_ldap_free_attr (attr);
532 ber_free (berctx, 0);
537 if (es_fwrite (values[0]->bv_val, values[0]->bv_len,
538 1, myopt->outstream) != 1)
540 log_error (_("error writing to stdout: %s\n"),
542 ldap_value_free_len (values);
543 my_ldap_free_attr (attr);
544 ber_free (berctx, 0);
550 break; /* Print only the first value. */
552 ldap_value_free_len (values);
553 my_ldap_free_attr (attr);
554 if (want_attr || !myopt->multi)
555 break; /* We only want to return the first attribute. */
557 ber_free (berctx, 0);
560 if (myopt->verbose > 1 && any)
561 log_info ("result has been printed\n");
568 /* Helper for the URL based LDAP query. */
570 fetch_ldap (my_opt_t myopt, const char *url, const LDAPURLDesc *ludp)
575 char *host, *dn, *filter, *attrs[2], *attr;
579 host = myopt->host? myopt->host : ludp->lud_host;
580 port = myopt->port? myopt->port : ludp->lud_port;
581 dn = myopt->dn? myopt->dn : ludp->lud_dn;
582 filter = myopt->filter? myopt->filter : ludp->lud_filter;
583 attrs[0] = myopt->attr? myopt->attr : ludp->lud_attrs? ludp->lud_attrs[0]:NULL;
588 port = (ludp->lud_scheme && !strcmp (ludp->lud_scheme, "ldaps"))? 636:389;
592 log_info (_("processing url '%s'\n"), url);
594 log_info (_(" user '%s'\n"), myopt->user);
596 log_info (_(" pass '%s'\n"), *myopt->pass?"*****":"");
598 log_info (_(" host '%s'\n"), host);
599 log_info (_(" port %d\n"), port);
601 log_info (_(" DN '%s'\n"), dn);
603 log_info (_(" filter '%s'\n"), filter);
604 if (myopt->multi && !myopt->attr && ludp->lud_attrs)
607 for (i=0; ludp->lud_attrs[i]; i++)
608 log_info (_(" attr '%s'\n"), ludp->lud_attrs[i]);
611 log_info (_(" attr '%s'\n"), attr);
617 log_error (_("no host name in '%s'\n"), url);
620 if (!myopt->multi && !attr)
622 log_error (_("no attribute given for query '%s'\n"), url);
626 if (!myopt->multi && !myopt->attr
627 && ludp->lud_attrs && ludp->lud_attrs[0] && ludp->lud_attrs[1])
628 log_info (_("WARNING: using first attribute only\n"));
633 ld = my_ldap_init (host, port);
637 log_error (_("LDAP init to '%s:%d' failed: %s\n"),
638 host, port, strerror (errno));
642 /* Fixme: Can we use MYOPT->user or is it shared with other theeads?. */
643 ret = my_ldap_simple_bind_s (ld, myopt->user, myopt->pass);
647 log_error (_("binding to '%s:%d' failed: %s\n"),
648 host, port, strerror (errno));
655 rc = my_ldap_search_st (ld, dn, ludp->lud_scope, filter,
656 myopt->multi && !myopt->attr && ludp->lud_attrs?
657 ludp->lud_attrs:attrs,
659 &myopt->timeout, &msg);
661 if (rc == LDAP_SIZELIMIT_EXCEEDED && myopt->multi)
663 if (es_fwrite ("E\0\0\0\x09truncated", 14, 1, myopt->outstream) != 1)
665 log_error (_("error writing to stdout: %s\n"), strerror (errno));
671 #ifdef HAVE_W32CE_SYSTEM
672 log_error ("searching '%s' failed: %d\n", url, rc);
674 log_error (_("searching '%s' failed: %s\n"),
675 url, ldap_err2string (rc));
677 if (rc != LDAP_NO_SUCH_OBJECT)
679 /* FIXME: Need deinit (ld)? */
680 /* Hmmm: Do we need to released MSG in case of an error? */
685 rc = print_ldap_entries (myopt, ld, msg, myopt->multi? NULL:attr);
695 /* Main processing. Take the URL and run the LDAP query. The result
696 is printed to stdout, errors are logged to the log stream. */
698 process_url (my_opt_t myopt, const char *url)
701 LDAPURLDesc *ludp = NULL;
704 if (!ldap_is_ldap_url (url))
706 log_error (_("'%s' is not an LDAP URL\n"), url);
710 if (ldap_url_parse (url, &ludp))
712 log_error (_("'%s' is an invalid LDAP URL\n"), url);
716 rc = fetch_ldap (myopt, url, ludp);
718 ldap_free_urldesc (ludp);