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 <https://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 #include <gpg-error.h>
53 #include "../common/logging.h"
54 #include "../common/argparse.h"
55 #include "../common/stringhelp.h"
56 #include "../common/mischelp.h"
57 #include "../common/strlist.h"
61 #include "../common/init.h"
63 /* With the ldap wrapper, there is no need for the npth_unprotect and leave
64 functions; thus we redefine them to nops. If we are not using the
65 ldap wrapper process we need to include the prototype for our
66 module's main function. */
67 #ifdef USE_LDAPWRAPPER
68 static void npth_unprotect (void) { }
69 static void npth_protect (void) { }
71 # include "./ldap-wrapper.h"
74 #ifdef HAVE_W32CE_SYSTEM
75 # include "w32-ldap-help.h"
76 # define my_ldap_init(a,b) \
77 _dirmngr_ldap_init ((a), (b))
78 # define my_ldap_simple_bind_s(a,b,c) \
79 _dirmngr_ldap_simple_bind_s ((a),(b),(c))
80 # define my_ldap_search_st(a,b,c,d,e,f,g,h) \
81 _dirmngr_ldap_search_st ((a), (b), (c), (d), (e), (f), (g), (h))
82 # define my_ldap_first_attribute(a,b,c) \
83 _dirmngr_ldap_first_attribute ((a),(b),(c))
84 # define my_ldap_next_attribute(a,b,c) \
85 _dirmngr_ldap_next_attribute ((a),(b),(c))
86 # define my_ldap_get_values_len(a,b,c) \
87 _dirmngr_ldap_get_values_len ((a),(b),(c))
88 # define my_ldap_free_attr(a) \
91 # define my_ldap_init(a,b) ldap_init ((a), (b))
92 # define my_ldap_simple_bind_s(a,b,c) ldap_simple_bind_s ((a), (b), (c))
93 # define my_ldap_search_st(a,b,c,d,e,f,g,h) \
94 ldap_search_st ((a), (b), (c), (d), (e), (f), (g), (h))
95 # define my_ldap_first_attribute(a,b,c) ldap_first_attribute ((a),(b),(c))
96 # define my_ldap_next_attribute(a,b,c) ldap_next_attribute ((a),(b),(c))
97 # define my_ldap_get_values_len(a,b,c) ldap_get_values_len ((a),(b),(c))
98 # define my_ldap_free_attr(a) ldap_memfree ((a))
101 #ifdef HAVE_W32_SYSTEM
102 typedef LDAP_TIMEVAL my_ldap_timeval_t;
104 typedef struct timeval my_ldap_timeval_t;
107 #define DEFAULT_LDAP_TIMEOUT 100 /* Arbitrary long timeout. */
110 /* Constants for the options. */
133 /* The list of options as used by the argparse.c code. */
134 static ARGPARSE_OPTS opts[] = {
135 { oVerbose, "verbose", 0, N_("verbose") },
136 { oQuiet, "quiet", 0, N_("be somewhat more quiet") },
137 { oTimeout, "timeout", 1, N_("|N|set LDAP timeout to N seconds")},
138 { oMulti, "multi", 0, N_("return all values in"
139 " a record oriented format")},
140 { oProxy, "proxy", 2,
141 N_("|NAME|ignore host part and connect through NAME")},
142 { oHost, "host", 2, N_("|NAME|connect to host NAME")},
143 { oPort, "port", 1, N_("|N|connect to port N")},
144 { oUser, "user", 2, N_("|NAME|use user NAME for authentication")},
145 { oPass, "pass", 2, N_("|PASS|use password PASS"
146 " for authentication")},
147 { oEnvPass, "env-pass", 0, N_("take password from $DIRMNGR_LDAP_PASS")},
148 { oDN, "dn", 2, N_("|STRING|query DN STRING")},
149 { oFilter, "filter", 2, N_("|STRING|use STRING as filter expression")},
150 { oAttr, "attr", 2, N_("|STRING|return the attribute STRING")},
151 { oOnlySearchTimeout, "only-search-timeout", 0, "@"},
152 { oLogWithPID,"log-with-pid", 0, "@"},
157 /* A structure with module options. This is not a static variable
158 because if we are not build as a standalone binary, each thread
159 using this module needs to handle its own values. */
164 my_ldap_timeval_t timeout;/* Timeout for the LDAP search functions. */
165 unsigned int alarm_timeout; /* And for the alarm based timeout. */
168 estream_t outstream; /* Send output to this stream. */
170 /* Note that we can't use const for the strings because ldap_* are
171 not defined that way. */
172 char *proxy; /* Host and Port override. */
173 char *user; /* Authentication user. */
174 char *pass; /* Authentication password. */
175 char *host; /* Override host. */
176 int port; /* Override port. */
177 char *dn; /* Override DN. */
178 char *filter;/* Override filter. */
179 char *attr; /* Override attribute. */
181 typedef struct my_opt_s *my_opt_t;
185 #ifndef HAVE_W32_SYSTEM
186 static void catch_alarm (int dummy);
188 static int process_url (my_opt_t myopt, const char *url);
192 /* Function called by argparse.c to display information. */
193 #ifdef USE_LDAPWRAPPER
195 my_strusage (int level)
201 case 11: p = "dirmngr_ldap (@GNUPG@)";
203 case 13: p = VERSION; break;
204 case 17: p = PRINTABLE_OS_NAME; break;
205 case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
206 case 49: p = PACKAGE_BUGREPORT; break;
209 _("Usage: dirmngr_ldap [options] [URL] (-h for help)\n");
212 _("Syntax: dirmngr_ldap [options] [URL]\n"
213 "Internal LDAP helper for Dirmngr\n"
214 "Interface and options may change without notice\n");
221 #endif /*!USE_LDAPWRAPPER*/
225 #ifdef USE_LDAPWRAPPER
226 main (int argc, char **argv)
228 ldap_wrapper_main (char **argv, estream_t outstream)
231 #ifndef USE_LDAPWRAPPER
237 int only_search_timeout = 0;
238 struct my_opt_s my_opt_buffer;
239 my_opt_t myopt = &my_opt_buffer;
240 char *malloced_buffer1 = NULL;
242 memset (&my_opt_buffer, 0, sizeof my_opt_buffer);
244 early_system_init ();
246 #ifdef USE_LDAPWRAPPER
247 set_strusage (my_strusage);
248 log_set_prefix ("dirmngr_ldap", GPGRT_LOG_WITH_PREFIX);
250 /* Setup I18N and common subsystems. */
253 init_common_subsystems (&argc, &argv);
255 es_set_binary (es_stdout);
256 myopt->outstream = es_stdout;
257 #else /*!USE_LDAPWRAPPER*/
258 myopt->outstream = outstream;
259 for (argc=0; argv[argc]; argc++)
261 #endif /*!USE_LDAPWRAPPER*/
264 myopt->timeout.tv_sec = DEFAULT_LDAP_TIMEOUT;
265 myopt->timeout.tv_usec = 0;
266 myopt->alarm_timeout = 0;
268 /* Parse the command line. */
271 pargs.flags= 1; /* Do not remove the args. */
272 while (arg_parse (&pargs, opts) )
276 case oVerbose: myopt->verbose++; break;
277 case oQuiet: myopt->quiet++; break;
279 myopt->timeout.tv_sec = pargs.r.ret_int;
280 myopt->timeout.tv_usec = 0;
281 myopt->alarm_timeout = pargs.r.ret_int;
283 case oOnlySearchTimeout: only_search_timeout = 1; break;
284 case oMulti: myopt->multi = 1; break;
285 case oUser: myopt->user = pargs.r.ret_str; break;
286 case oPass: myopt->pass = pargs.r.ret_str; break;
288 myopt->pass = getenv ("DIRMNGR_LDAP_PASS");
290 case oProxy: myopt->proxy = pargs.r.ret_str; break;
291 case oHost: myopt->host = pargs.r.ret_str; break;
292 case oPort: myopt->port = pargs.r.ret_int; break;
293 case oDN: myopt->dn = pargs.r.ret_str; break;
294 case oFilter: myopt->filter = pargs.r.ret_str; break;
295 case oAttr: myopt->attr = pargs.r.ret_str; break;
298 unsigned int oldflags;
299 log_get_prefix (&oldflags);
300 log_set_prefix (NULL, oldflags | GPGRT_LOG_WITH_PID);
305 #ifdef USE_LDAPWRAPPER
306 pargs.err = ARGPARSE_PRINT_ERROR;
308 pargs.err = ARGPARSE_PRINT_WARNING; /* No exit() please. */
314 if (only_search_timeout)
315 myopt->alarm_timeout = 0;
319 malloced_buffer1 = xtrystrdup (myopt->proxy);
320 if (!malloced_buffer1)
322 log_error ("error copying string: %s\n", strerror (errno));
325 myopt->host = malloced_buffer1;
326 p = strchr (myopt->host, ':');
330 myopt->port = atoi (p);
333 myopt->port = 389; /* make sure ports gets overridden. */
336 if (myopt->port < 0 || myopt->port > 65535)
337 log_error (_("invalid port number %d\n"), myopt->port);
339 #ifdef USE_LDAPWRAPPER
340 if (log_get_errorcount (0))
345 /* All passed arguments should be fine in this case. */
349 #ifdef USE_LDAPWRAPPER
350 if (myopt->alarm_timeout)
352 #ifndef HAVE_W32_SYSTEM
353 # if defined(HAVE_SIGACTION) && defined(HAVE_STRUCT_SIGACTION)
354 struct sigaction act;
356 act.sa_handler = catch_alarm;
357 sigemptyset (&act.sa_mask);
359 if (sigaction (SIGALRM,&act,NULL))
361 if (signal (SIGALRM, catch_alarm) == SIG_ERR)
363 log_fatal ("unable to register timeout handler\n");
366 #endif /*USE_LDAPWRAPPER*/
368 for (; argc; argc--, argv++)
369 if (process_url (myopt, *argv))
372 xfree (malloced_buffer1);
376 #ifndef HAVE_W32_SYSTEM
378 catch_alarm (int dummy)
386 set_timeout (my_opt_t myopt)
388 #ifdef HAVE_W32_SYSTEM
392 if (myopt->alarm_timeout)
393 alarm (myopt->alarm_timeout);
398 /* Helper for fetch_ldap(). */
400 print_ldap_entries (my_opt_t myopt, LDAP *ld, LDAPMessage *msg, char *want_attr)
405 for (npth_unprotect (), item = ldap_first_entry (ld, msg), npth_protect ();
407 npth_unprotect (), item = ldap_next_entry (ld, item), npth_protect ())
412 if (myopt->verbose > 1)
413 log_info (_("scanning result for attribute '%s'\n"),
414 want_attr? want_attr : "[all]");
417 { /* Write item marker. */
418 if (es_fwrite ("I\0\0\0\0", 5, 1, myopt->outstream) != 1)
420 log_error (_("error writing to stdout: %s\n"),
427 for (npth_unprotect (), attr = my_ldap_first_attribute (ld, item, &berctx),
430 npth_unprotect (), attr = my_ldap_next_attribute (ld, item, berctx),
433 struct berval **values;
436 if (myopt->verbose > 1)
437 log_info (_(" available attribute '%s'\n"), attr);
441 /* I case we want only one attribute we do a case
442 insensitive compare without the optional extension
443 (i.e. ";binary"). Case insensitive is not really correct
444 but the best we can do. */
450 cp1 = strchr (want_attr, ';');
453 cp2 = strchr (attr, ';');
456 cmpres = ascii_strcasecmp (want_attr, attr);
463 my_ldap_free_attr (attr);
464 continue; /* Not found: Try next attribute. */
469 values = my_ldap_get_values_len (ld, item, attr);
475 log_info (_("attribute '%s' not found\n"), attr);
476 my_ldap_free_attr (attr);
482 log_info (_("found attribute '%s'\n"), attr);
483 if (myopt->verbose > 1)
484 for (idx=0; values[idx]; idx++)
485 log_info (" length[%d]=%d\n",
486 idx, (int)values[0]->bv_len);
491 { /* Write attribute marker. */
492 unsigned char tmp[5];
493 size_t n = strlen (attr);
500 if (es_fwrite (tmp, 5, 1, myopt->outstream) != 1
501 || es_fwrite (attr, n, 1, myopt->outstream) != 1)
503 log_error (_("error writing to stdout: %s\n"),
505 ldap_value_free_len (values);
506 my_ldap_free_attr (attr);
507 ber_free (berctx, 0);
512 for (idx=0; values[idx]; idx++)
515 { /* Write value marker. */
516 unsigned char tmp[5];
517 size_t n = values[0]->bv_len;
525 if (es_fwrite (tmp, 5, 1, myopt->outstream) != 1)
527 log_error (_("error writing to stdout: %s\n"),
529 ldap_value_free_len (values);
530 my_ldap_free_attr (attr);
531 ber_free (berctx, 0);
536 if (es_fwrite (values[0]->bv_val, values[0]->bv_len,
537 1, myopt->outstream) != 1)
539 log_error (_("error writing to stdout: %s\n"),
541 ldap_value_free_len (values);
542 my_ldap_free_attr (attr);
543 ber_free (berctx, 0);
549 break; /* Print only the first value. */
551 ldap_value_free_len (values);
552 my_ldap_free_attr (attr);
553 if (want_attr || !myopt->multi)
554 break; /* We only want to return the first attribute. */
556 ber_free (berctx, 0);
559 if (myopt->verbose > 1 && any)
560 log_info ("result has been printed\n");
567 /* Helper for the URL based LDAP query. */
569 fetch_ldap (my_opt_t myopt, const char *url, const LDAPURLDesc *ludp)
574 char *host, *dn, *filter, *attrs[2], *attr;
578 host = myopt->host? myopt->host : ludp->lud_host;
579 port = myopt->port? myopt->port : ludp->lud_port;
580 dn = myopt->dn? myopt->dn : ludp->lud_dn;
581 filter = myopt->filter? myopt->filter : ludp->lud_filter;
582 attrs[0] = myopt->attr? myopt->attr : ludp->lud_attrs? ludp->lud_attrs[0]:NULL;
587 port = (ludp->lud_scheme && !strcmp (ludp->lud_scheme, "ldaps"))? 636:389;
591 log_info (_("processing url '%s'\n"), url);
593 log_info (_(" user '%s'\n"), myopt->user);
595 log_info (_(" pass '%s'\n"), *myopt->pass?"*****":"");
597 log_info (_(" host '%s'\n"), host);
598 log_info (_(" port %d\n"), port);
600 log_info (_(" DN '%s'\n"), dn);
602 log_info (_(" filter '%s'\n"), filter);
603 if (myopt->multi && !myopt->attr && ludp->lud_attrs)
606 for (i=0; ludp->lud_attrs[i]; i++)
607 log_info (_(" attr '%s'\n"), ludp->lud_attrs[i]);
610 log_info (_(" attr '%s'\n"), attr);
616 log_error (_("no host name in '%s'\n"), url);
619 if (!myopt->multi && !attr)
621 log_error (_("no attribute given for query '%s'\n"), url);
625 if (!myopt->multi && !myopt->attr
626 && ludp->lud_attrs && ludp->lud_attrs[0] && ludp->lud_attrs[1])
627 log_info (_("WARNING: using first attribute only\n"));
632 ld = my_ldap_init (host, port);
636 log_error (_("LDAP init to '%s:%d' failed: %s\n"),
637 host, port, strerror (errno));
641 /* Fixme: Can we use MYOPT->user or is it shared with other theeads?. */
642 ret = my_ldap_simple_bind_s (ld, myopt->user, myopt->pass);
645 if (ret == LDAP_PROTOCOL_ERROR)
647 /* Protocol error could mean that the server only supports v3. */
648 int version = LDAP_VERSION3;
650 log_info ("protocol error; retrying bind with v3 protocol\n");
652 ldap_set_option (ld, LDAP_OPT_PROTOCOL_VERSION, &version);
653 ret = my_ldap_simple_bind_s (ld, myopt->user, myopt->pass);
659 log_error (_("binding to '%s:%d' failed: %s\n"),
660 host, port, ldap_err2string (ret));
667 rc = my_ldap_search_st (ld, dn, ludp->lud_scope, filter,
668 myopt->multi && !myopt->attr && ludp->lud_attrs?
669 ludp->lud_attrs:attrs,
671 &myopt->timeout, &msg);
673 if (rc == LDAP_SIZELIMIT_EXCEEDED && myopt->multi)
675 if (es_fwrite ("E\0\0\0\x09truncated", 14, 1, myopt->outstream) != 1)
677 log_error (_("error writing to stdout: %s\n"), strerror (errno));
683 #ifdef HAVE_W32CE_SYSTEM
684 log_error ("searching '%s' failed: %d\n", url, rc);
686 log_error (_("searching '%s' failed: %s\n"),
687 url, ldap_err2string (rc));
689 if (rc != LDAP_NO_SUCH_OBJECT)
691 /* FIXME: Need deinit (ld)? */
692 /* Hmmm: Do we need to released MSG in case of an error? */
697 rc = print_ldap_entries (myopt, ld, msg, myopt->multi? NULL:attr);
707 /* Main processing. Take the URL and run the LDAP query. The result
708 is printed to stdout, errors are logged to the log stream. */
710 process_url (my_opt_t myopt, const char *url)
713 LDAPURLDesc *ludp = NULL;
716 if (!ldap_is_ldap_url (url))
718 log_error (_("'%s' is not an LDAP URL\n"), url);
722 if (ldap_url_parse (url, &ludp))
724 log_error (_("'%s' is an invalid LDAP URL\n"), url);
728 rc = fetch_ldap (myopt, url, ludp);
730 ldap_free_urldesc (ludp);