1 /* dirmngr-ldap.c - The LDAP helper for dirmngr.
2 * Copyright (C) 2004 g10 Code GmbH
4 * This file is part of DirMngr.
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.
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.
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
34 #ifdef HAVE_W32_SYSTEM
40 /* For OpenLDAP, to enable the API that we're using. */
41 #define LDAP_DEPRECATED 1
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"
56 #define DEFAULT_LDAP_TIMEOUT 100 /* Arbitrary long timeout. */
59 /* Constants for the options. */
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")},
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, "@"},
106 /* The usual structure for the program flags. */
111 struct timeval timeout; /* Timeout for the LDAP search functions. */
112 unsigned int alarm_timeout; /* And for the alarm based timeout. */
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. */
129 static void catch_alarm (int dummy);
130 static int process_url (const char *url);
134 /* Function called by argparse.c to display information. */
136 my_strusage (int level)
142 case 11: p = "dirmngr_ldap (GnuPG)";
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;
150 _("Usage: dirmngr_ldap [options] [URL] (-h for help)\n");
153 _("Syntax: dirmngr_ldap [options] [URL]\n"
154 "Internal LDAP helper for Dirmngr.\n"
155 "Interface and options may change without notice.\n");
165 main (int argc, char **argv )
170 int only_search_timeout = 0;
172 #ifdef HAVE_W32_SYSTEM
173 /* Yeah, right. Sigh. */
174 _setmode (_fileno (stdout), _O_BINARY);
177 set_strusage (my_strusage);
178 log_set_prefix ("dirmngr_ldap", JNLIB_LOG_WITH_PREFIX);
184 opt.timeout.tv_sec = DEFAULT_LDAP_TIMEOUT;
185 opt.timeout.tv_usec = 0;
186 opt.alarm_timeout = 0;
188 /* Parse the command line. */
191 pargs.flags= 1; /* Do not remove the args. */
192 while (arg_parse (&pargs, opts) )
196 case oVerbose: opt.verbose++; break;
197 case oQuiet: opt.quiet++; break;
199 opt.timeout.tv_sec = pargs.r.ret_int;
200 opt.timeout.tv_usec = 0;
201 opt.alarm_timeout = pargs.r.ret_int;
203 case oOnlySearchTimeout: only_search_timeout = 1; break;
204 case oMulti: opt.multi = 1; break;
205 case oUser: opt.user = pargs.r.ret_str; break;
206 case oPass: opt.pass = pargs.r.ret_str; break;
208 opt.pass = getenv ("DIRMNGR_LDAP_PASS");
210 case oProxy: opt.proxy = pargs.r.ret_str; break;
211 case oHost: opt.host = pargs.r.ret_str; break;
212 case oPort: opt.port = pargs.r.ret_int; break;
213 case oDN: opt.dn = pargs.r.ret_str; break;
214 case oFilter: opt.filter = pargs.r.ret_str; break;
215 case oAttr: opt.attr = pargs.r.ret_str; break;
218 unsigned int oldflags;
219 log_get_prefix (&oldflags);
220 log_set_prefix (NULL, oldflags | JNLIB_LOG_WITH_PID);
224 default : pargs.err = 2; break;
228 if (only_search_timeout)
229 opt.alarm_timeout = 0;
233 opt.host = xstrdup (opt.proxy);
234 p = strchr (opt.host, ':');
241 opt.port = 389; /* make sure ports gets overridden. */
244 if (opt.port < 0 || opt.port > 65535)
245 log_error (_("invalid port number %d\n"), opt.port);
247 if (log_get_errorcount (0))
253 if (opt.alarm_timeout)
255 #ifndef HAVE_W32_SYSTEM
256 # if defined(HAVE_SIGACTION) && defined(HAVE_STRUCT_SIGACTION)
257 struct sigaction act;
259 act.sa_handler = catch_alarm;
260 sigemptyset (&act.sa_mask);
262 if (sigaction (SIGALRM,&act,NULL))
264 if (signal (SIGALRM, catch_alarm) == SIG_ERR)
266 log_fatal ("unable to register timeout handler\n");
270 for (; argc; argc--, argv++)
271 if (process_url (*argv))
279 catch_alarm (int dummy)
289 #ifndef HAVE_W32_SYSTEM
291 if (opt.alarm_timeout)
292 alarm (opt.alarm_timeout);
297 /* Helper for fetch_ldap(). */
299 print_ldap_entries (LDAP *ld, LDAPMessage *msg, char *want_attr)
304 for (item = ldap_first_entry (ld, msg); item;
305 item = ldap_next_entry (ld, item))
311 log_info (_("scanning result for attribute `%s'\n"),
312 want_attr? want_attr : "[all]");
315 { /* Write item marker. */
316 if (fwrite ("I\0\0\0\0", 5, 1, stdout) != 1)
318 log_error (_("error writing to stdout: %s\n"),
325 for (attr = ldap_first_attribute (ld, item, &berctx); attr;
326 attr = ldap_next_attribute (ld, item, berctx))
328 struct berval **values;
332 log_info (_(" available attribute `%s'\n"), attr);
336 /* I case we want only one attribute we do a case
337 insensitive compare without the optional extension
338 (i.e. ";binary"). Case insensitive is not really correct
339 but the best we can do. */
345 cp1 = strchr (want_attr, ';');
348 cp2 = strchr (attr, ';');
351 cmpres = ascii_strcasecmp (want_attr, attr);
359 continue; /* Not found: Try next attribute. */
363 values = ldap_get_values_len (ld, item, attr);
368 log_info (_("attribute `%s' not found\n"), attr);
375 log_info (_("found attribute `%s'\n"), attr);
377 for (idx=0; values[idx]; idx++)
378 log_info (" length[%d]=%d\n",
379 idx, (int)values[0]->bv_len);
384 { /* Write attribute marker. */
385 unsigned char tmp[5];
386 size_t n = strlen (attr);
393 if (fwrite (tmp, 5, 1, stdout) != 1
394 || fwrite (attr, n, 1, stdout) != 1)
396 log_error (_("error writing to stdout: %s\n"),
398 ldap_value_free_len (values);
400 ber_free (berctx, 0);
405 for (idx=0; values[idx]; idx++)
408 { /* Write value marker. */
409 unsigned char tmp[5];
410 size_t n = values[0]->bv_len;
418 if (fwrite (tmp, 5, 1, stdout) != 1)
420 log_error (_("error writing to stdout: %s\n"),
422 ldap_value_free_len (values);
424 ber_free (berctx, 0);
429 /* Note: this does not work for STDOUT on a Windows
430 console, where it fails with "Not enough space" for
431 CRLs which are 52 KB or larger. */
432 if (fwrite (values[0]->bv_val, values[0]->bv_len,
435 log_error (_("error writing to stdout: %s\n"),
437 ldap_value_free_len (values);
439 ber_free (berctx, 0);
443 /* On Windows console STDOUT, we have to break up the
444 writes into small parts. */
447 while (n < values[0]->bv_len)
449 int cnt = values[0]->bv_len - n;
450 /* The actual limit is (52 * 1024 - 1) on Windows XP SP2. */
451 #define MAX_CNT (32*1024)
455 if (fwrite (((char *) values[0]->bv_val) + n, cnt, 1,
458 log_error (_("error writing to stdout: %s\n"),
460 ldap_value_free_len (values);
462 ber_free (berctx, 0);
471 break; /* Print only the first value. */
473 ldap_value_free_len (values);
475 if (want_attr || !opt.multi)
476 break; /* We only want to return the first attribute. */
478 ber_free (berctx, 0);
481 if (opt.verbose > 1 && any)
482 log_info ("result has been printed\n");
489 /* Helper for the URL based LDAP query. */
491 fetch_ldap (const char *url, const LDAPURLDesc *ludp)
496 char *host, *dn, *filter, *attrs[2], *attr;
499 host = opt.host? opt.host : ludp->lud_host;
500 port = opt.port? opt.port : ludp->lud_port;
501 dn = opt.dn? opt.dn : ludp->lud_dn;
502 filter = opt.filter? opt.filter : ludp->lud_filter;
503 attrs[0] = opt.attr? opt.attr : ludp->lud_attrs? ludp->lud_attrs[0]:NULL;
508 port = (ludp->lud_scheme && !strcmp (ludp->lud_scheme, "ldaps"))? 636:389;
512 log_info (_("processing url `%s'\n"), url);
514 log_info (_(" user `%s'\n"), opt.user);
516 log_info (_(" pass `%s'\n"), *opt.pass?"*****":"");
518 log_info (_(" host `%s'\n"), host);
519 log_info (_(" port %d\n"), port);
521 log_info (_(" DN `%s'\n"), dn);
523 log_info (_(" filter `%s'\n"), filter);
524 if (opt.multi && !opt.attr && ludp->lud_attrs)
527 for (i=0; ludp->lud_attrs[i]; i++)
528 log_info (_(" attr `%s'\n"), ludp->lud_attrs[i]);
531 log_info (_(" attr `%s'\n"), attr);
537 log_error (_("no host name in `%s'\n"), url);
540 if (!opt.multi && !attr)
542 log_error (_("no attribute given for query `%s'\n"), url);
546 if (!opt.multi && !opt.attr
547 && ludp->lud_attrs && ludp->lud_attrs[0] && ludp->lud_attrs[1])
548 log_info (_("WARNING: using first attribute only\n"));
552 ld = ldap_init (host, port);
555 log_error (_("LDAP init to `%s:%d' failed: %s\n"),
556 host, port, strerror (errno));
559 if (ldap_simple_bind_s (ld, opt.user, opt.pass))
561 log_error (_("binding to `%s:%d' failed: %s\n"),
562 host, port, strerror (errno));
563 /* FIXME: Need deinit (ld)? */
568 rc = ldap_search_st (ld, dn, ludp->lud_scope, filter,
569 opt.multi && !opt.attr && ludp->lud_attrs?
570 ludp->lud_attrs:attrs,
573 if (rc == LDAP_SIZELIMIT_EXCEEDED && opt.multi)
575 if (fwrite ("E\0\0\0\x09truncated", 14, 1, stdout) != 1)
577 log_error (_("error writing to stdout: %s\n"),
584 log_error (_("searching `%s' failed: %s\n"),
585 url, ldap_err2string (rc));
586 if (rc != LDAP_NO_SUCH_OBJECT)
588 /* FIXME: Need deinit (ld)? */
589 /* Hmmm: Do we need to released MSG in case of an error? */
594 rc = print_ldap_entries (ld, msg, opt.multi? NULL:attr);
597 /* FIXME: Need deinit (ld)? */
604 /* Main processing. Take the URL and run the LDAP query. The result
605 is printed to stdout, errors are logged to the log stream. */
607 process_url (const char *url)
610 LDAPURLDesc *ludp = NULL;
613 if (!ldap_is_ldap_url (url))
615 log_error (_("`%s' is not an LDAP URL\n"), url);
619 if (ldap_url_parse (url, &ludp))
621 log_error (_("`%s' is an invalid LDAP URL\n"), url);
625 rc = fetch_ldap (url, ludp);
627 ldap_free_urldesc (ludp);