diff options
author | Werner Koch <[email protected]> | 2010-06-09 16:53:51 +0000 |
---|---|---|
committer | Werner Koch <[email protected]> | 2010-06-09 16:53:51 +0000 |
commit | c3f08dcb7266efeac84f5f720ec0a353a45e950d (patch) | |
tree | 51501aa7d0e6dadb80576a1e982fdfde871bd2ad /dirmngr/dirmngr_ldap.c | |
parent | 2010-06-08 Marcus Brinkmann <[email protected]> (diff) | |
download | gnupg-c3f08dcb7266efeac84f5f720ec0a353a45e950d.tar.gz gnupg-c3f08dcb7266efeac84f5f720ec0a353a45e950d.zip |
Merged Dirmngr with GnuPG.
A few code changes to support dirmngr.
Diffstat (limited to '')
-rw-r--r-- | dirmngr/dirmngr_ldap.c | 646 |
1 files changed, 646 insertions, 0 deletions
diff --git a/dirmngr/dirmngr_ldap.c b/dirmngr/dirmngr_ldap.c new file mode 100644 index 000000000..b73cc7da6 --- /dev/null +++ b/dirmngr/dirmngr_ldap.c @@ -0,0 +1,646 @@ +/* dirmngr-ldap.c - The LDAP helper for dirmngr. + * Copyright (C) 2004 g10 Code GmbH + * + * This file is part of DirMngr. + * + * DirMngr is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * DirMngr is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <stdarg.h> +#include <string.h> +#include <signal.h> +#include <errno.h> +#include <assert.h> +#include <sys/time.h> +#include <unistd.h> + +#ifdef HAVE_W32_SYSTEM +#include <winsock2.h> +#include <winldap.h> +#include <fcntl.h> +#include "ldap-url.h" +#else +/* For OpenLDAP, to enable the API that we're using. */ +#define LDAP_DEPRECATED 1 +#include <ldap.h> +#endif + + +#define JNLIB_NEED_LOG_LOGV +#include "../common/logging.h" +#include "../common/argparse.h" +#include "../common/stringhelp.h" +#include "../common/mischelp.h" +#include "../common/strlist.h" + +#include "i18n.h" +#include "util.h" + +#define DEFAULT_LDAP_TIMEOUT 100 /* Arbitrary long timeout. */ + + +/* Constants for the options. */ +enum + { + oQuiet = 'q', + oVerbose = 'v', + + oTimeout = 500, + oMulti, + oProxy, + oHost, + oPort, + oUser, + oPass, + oEnvPass, + oDN, + oFilter, + oAttr, + + oOnlySearchTimeout, + oLogWithPID + }; + + +/* The list of options as used by the argparse.c code. */ +static ARGPARSE_OPTS opts[] = { + { oVerbose, "verbose", 0, N_("verbose") }, + { oQuiet, "quiet", 0, N_("be somewhat more quiet") }, + { oTimeout, "timeout", 1, N_("|N|set LDAP timeout to N seconds")}, + { oMulti, "multi", 0, N_("return all values in" + " a record oriented format")}, + { oProxy, "proxy", 2, + N_("|NAME|ignore host part and connect through NAME")}, + { oHost, "host", 2, N_("|NAME|connect to host NAME")}, + { oPort, "port", 1, N_("|N|connect to port N")}, + { oUser, "user", 2, N_("|NAME|use user NAME for authentication")}, + { oPass, "pass", 2, N_("|PASS|use password PASS" + " for authentication")}, + { oEnvPass, "env-pass", 0, N_("take password from $DIRMNGR_LDAP_PASS")}, + { oDN, "dn", 2, N_("|STRING|query DN STRING")}, + { oFilter, "filter", 2, N_("|STRING|use STRING as filter expression")}, + { oAttr, "attr", 2, N_("|STRING|return the attribute STRING")}, + { oOnlySearchTimeout, "only-search-timeout", 0, "@"}, + { oLogWithPID,"log-with-pid", 0, "@"}, + { 0, NULL, 0, NULL } +}; + + +/* The usual structure for the program flags. */ +static struct +{ + int quiet; + int verbose; + struct timeval timeout; /* Timeout for the LDAP search functions. */ + unsigned int alarm_timeout; /* And for the alarm based timeout. */ + int multi; + + /* Note that we can't use const for the strings because ldap_* are + not defined that way. */ + char *proxy; /* Host and Port override. */ + char *user; /* Authentication user. */ + char *pass; /* Authentication password. */ + char *host; /* Override host. */ + int port; /* Override port. */ + char *dn; /* Override DN. */ + char *filter;/* Override filter. */ + char *attr; /* Override attribute. */ +} opt; + + +/* Prototypes. */ +static void catch_alarm (int dummy); +static int process_url (const char *url); + + + +/* Function called by argparse.c to display information. */ +static const char * +my_strusage (int level) +{ + const char *p; + + switch(level) + { + case 11: p = "dirmngr_ldap (GnuPG)"; + break; + case 13: p = VERSION; break; + case 17: p = PRINTABLE_OS_NAME; break; + case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break; + case 49: p = PACKAGE_BUGREPORT; break; + case 1: + case 40: p = + _("Usage: dirmngr_ldap [options] [URL] (-h for help)\n"); + break; + case 41: p = + _("Syntax: dirmngr_ldap [options] [URL]\n" + "Internal LDAP helper for Dirmngr.\n" + "Interface and options may change without notice.\n"); + break; + + default: p = NULL; + } + return p; +} + + +static void +my_i18n_init (void) +{ +#warning Better use common init functions +#ifdef USE_SIMPLE_GETTEXT + set_gettext_file (PACKAGE); +#else +# ifdef ENABLE_NLS + setlocale (LC_ALL, "" ); + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); +# endif +#endif +} + + +int +main (int argc, char **argv ) +{ + ARGPARSE_ARGS pargs; + int any_err = 0; + char *p; + int only_search_timeout = 0; + +#ifdef HAVE_W32_SYSTEM + /* Yeah, right. Sigh. */ + _setmode (_fileno (stdout), _O_BINARY); +#endif + + set_strusage (my_strusage); + log_set_prefix ("dirmngr_ldap", JNLIB_LOG_WITH_PREFIX); + + /* Setup I18N. */ + my_i18n_init(); + + /* LDAP defaults */ + opt.timeout.tv_sec = DEFAULT_LDAP_TIMEOUT; + opt.timeout.tv_usec = 0; + opt.alarm_timeout = 0; + + /* Parse the command line. */ + pargs.argc = &argc; + pargs.argv = &argv; + pargs.flags= 1; /* Do not remove the args. */ + while (arg_parse (&pargs, opts) ) + { + switch (pargs.r_opt) + { + case oVerbose: opt.verbose++; break; + case oQuiet: opt.quiet++; break; + case oTimeout: + opt.timeout.tv_sec = pargs.r.ret_int; + opt.timeout.tv_usec = 0; + opt.alarm_timeout = pargs.r.ret_int; + break; + case oOnlySearchTimeout: only_search_timeout = 1; break; + case oMulti: opt.multi = 1; break; + case oUser: opt.user = pargs.r.ret_str; break; + case oPass: opt.pass = pargs.r.ret_str; break; + case oEnvPass: + opt.pass = getenv ("DIRMNGR_LDAP_PASS"); + break; + case oProxy: opt.proxy = pargs.r.ret_str; break; + case oHost: opt.host = pargs.r.ret_str; break; + case oPort: opt.port = pargs.r.ret_int; break; + case oDN: opt.dn = pargs.r.ret_str; break; + case oFilter: opt.filter = pargs.r.ret_str; break; + case oAttr: opt.attr = pargs.r.ret_str; break; + case oLogWithPID: + { + unsigned int oldflags; + log_get_prefix (&oldflags); + log_set_prefix (NULL, oldflags | JNLIB_LOG_WITH_PID); + } + break; + + default : pargs.err = 2; break; + } + } + + if (only_search_timeout) + opt.alarm_timeout = 0; + + if (opt.proxy) + { + opt.host = xstrdup (opt.proxy); + p = strchr (opt.host, ':'); + if (p) + { + *p++ = 0; + opt.port = atoi (p); + } + if (!opt.port) + opt.port = 389; /* make sure ports gets overridden. */ + } + + if (opt.port < 0 || opt.port > 65535) + log_error (_("invalid port number %d\n"), opt.port); + + if (log_get_errorcount (0)) + exit (2); + + if (argc < 1) + usage (1); + + if (opt.alarm_timeout) + { +#ifndef HAVE_W32_SYSTEM +# if defined(HAVE_SIGACTION) && defined(HAVE_STRUCT_SIGACTION) + struct sigaction act; + + act.sa_handler = catch_alarm; + sigemptyset (&act.sa_mask); + act.sa_flags = 0; + if (sigaction (SIGALRM,&act,NULL)) +# else + if (signal (SIGALRM, catch_alarm) == SIG_ERR) +# endif + log_fatal ("unable to register timeout handler\n"); +#endif + } + + for (; argc; argc--, argv++) + if (process_url (*argv)) + any_err = 1; + + return any_err; +} + + +static void +catch_alarm (int dummy) +{ + (void)dummy; + _exit (10); +} + + +static void +set_timeout (void) +{ +#ifndef HAVE_W32_SYSTEM + /* FIXME for W32. */ + if (opt.alarm_timeout) + alarm (opt.alarm_timeout); +#endif +} + + +/* Helper for fetch_ldap(). */ +static int +print_ldap_entries (LDAP *ld, LDAPMessage *msg, char *want_attr) +{ + LDAPMessage *item; + int any = 0; + + for (item = ldap_first_entry (ld, msg); item; + item = ldap_next_entry (ld, item)) + { + BerElement *berctx; + char *attr; + + if (opt.verbose > 1) + log_info (_("scanning result for attribute `%s'\n"), + want_attr? want_attr : "[all]"); + + if (opt.multi) + { /* Write item marker. */ + if (fwrite ("I\0\0\0\0", 5, 1, stdout) != 1) + { + log_error (_("error writing to stdout: %s\n"), + strerror (errno)); + return -1; + } + } + + + for (attr = ldap_first_attribute (ld, item, &berctx); attr; + attr = ldap_next_attribute (ld, item, berctx)) + { + struct berval **values; + int idx; + + if (opt.verbose > 1) + log_info (_(" available attribute `%s'\n"), attr); + + set_timeout (); + + /* I case we want only one attribute we do a case + insensitive compare without the optional extension + (i.e. ";binary"). Case insensitive is not really correct + but the best we can do. */ + if (want_attr) + { + char *cp1, *cp2; + int cmpres; + + cp1 = strchr (want_attr, ';'); + if (cp1) + *cp1 = 0; + cp2 = strchr (attr, ';'); + if (cp2) + *cp2 = 0; + cmpres = ascii_strcasecmp (want_attr, attr); + if (cp1) + *cp1 = ';'; + if (cp2) + *cp2 = ';'; + if (cmpres) + { + ldap_memfree (attr); + continue; /* Not found: Try next attribute. */ + } + } + + values = ldap_get_values_len (ld, item, attr); + + if (!values) + { + if (opt.verbose) + log_info (_("attribute `%s' not found\n"), attr); + ldap_memfree (attr); + continue; + } + + if (opt.verbose) + { + log_info (_("found attribute `%s'\n"), attr); + if (opt.verbose > 1) + for (idx=0; values[idx]; idx++) + log_info (" length[%d]=%d\n", + idx, (int)values[0]->bv_len); + + } + + if (opt.multi) + { /* Write attribute marker. */ + unsigned char tmp[5]; + size_t n = strlen (attr); + + tmp[0] = 'A'; + tmp[1] = (n >> 24); + tmp[2] = (n >> 16); + tmp[3] = (n >> 8); + tmp[4] = (n); + if (fwrite (tmp, 5, 1, stdout) != 1 + || fwrite (attr, n, 1, stdout) != 1) + { + log_error (_("error writing to stdout: %s\n"), + strerror (errno)); + ldap_value_free_len (values); + ldap_memfree (attr); + ber_free (berctx, 0); + return -1; + } + } + + for (idx=0; values[idx]; idx++) + { + if (opt.multi) + { /* Write value marker. */ + unsigned char tmp[5]; + size_t n = values[0]->bv_len; + + tmp[0] = 'V'; + tmp[1] = (n >> 24); + tmp[2] = (n >> 16); + tmp[3] = (n >> 8); + tmp[4] = (n); + + if (fwrite (tmp, 5, 1, stdout) != 1) + { + log_error (_("error writing to stdout: %s\n"), + strerror (errno)); + ldap_value_free_len (values); + ldap_memfree (attr); + ber_free (berctx, 0); + return -1; + } + } +#if 1 + /* Note: this does not work for STDOUT on a Windows + console, where it fails with "Not enough space" for + CRLs which are 52 KB or larger. */ + if (fwrite (values[0]->bv_val, values[0]->bv_len, + 1, stdout) != 1) + { + log_error (_("error writing to stdout: %s\n"), + strerror (errno)); + ldap_value_free_len (values); + ldap_memfree (attr); + ber_free (berctx, 0); + return -1; + } +#else + /* On Windows console STDOUT, we have to break up the + writes into small parts. */ + { + int n = 0; + while (n < values[0]->bv_len) + { + int cnt = values[0]->bv_len - n; + /* The actual limit is (52 * 1024 - 1) on Windows XP SP2. */ +#define MAX_CNT (32*1024) + if (cnt > MAX_CNT) + cnt = MAX_CNT; + + if (fwrite (((char *) values[0]->bv_val) + n, cnt, 1, + stdout) != 1) + { + log_error (_("error writing to stdout: %s\n"), + strerror (errno)); + ldap_value_free_len (values); + ldap_memfree (attr); + ber_free (berctx, 0); + return -1; + } + n += cnt; + } + } +#endif + any = 1; + if (!opt.multi) + break; /* Print only the first value. */ + } + ldap_value_free_len (values); + ldap_memfree (attr); + if (want_attr || !opt.multi) + break; /* We only want to return the first attribute. */ + } + ber_free (berctx, 0); + } + + if (opt.verbose > 1 && any) + log_info ("result has been printed\n"); + + return any?0:-1; +} + + + +/* Helper for the URL based LDAP query. */ +static int +fetch_ldap (const char *url, const LDAPURLDesc *ludp) +{ + LDAP *ld; + LDAPMessage *msg; + int rc = 0; + char *host, *dn, *filter, *attrs[2], *attr; + int port; + + host = opt.host? opt.host : ludp->lud_host; + port = opt.port? opt.port : ludp->lud_port; + dn = opt.dn? opt.dn : ludp->lud_dn; + filter = opt.filter? opt.filter : ludp->lud_filter; + attrs[0] = opt.attr? opt.attr : ludp->lud_attrs? ludp->lud_attrs[0]:NULL; + attrs[1] = NULL; + attr = attrs[0]; + + if (!port) + port = (ludp->lud_scheme && !strcmp (ludp->lud_scheme, "ldaps"))? 636:389; + + if (opt.verbose) + { + log_info (_("processing url `%s'\n"), url); + if (opt.user) + log_info (_(" user `%s'\n"), opt.user); + if (opt.pass) + log_info (_(" pass `%s'\n"), *opt.pass?"*****":""); + if (host) + log_info (_(" host `%s'\n"), host); + log_info (_(" port %d\n"), port); + if (dn) + log_info (_(" DN `%s'\n"), dn); + if (filter) + log_info (_(" filter `%s'\n"), filter); + if (opt.multi && !opt.attr && ludp->lud_attrs) + { + int i; + for (i=0; ludp->lud_attrs[i]; i++) + log_info (_(" attr `%s'\n"), ludp->lud_attrs[i]); + } + else if (attr) + log_info (_(" attr `%s'\n"), attr); + } + + + if (!host || !*host) + { + log_error (_("no host name in `%s'\n"), url); + return -1; + } + if (!opt.multi && !attr) + { + log_error (_("no attribute given for query `%s'\n"), url); + return -1; + } + + if (!opt.multi && !opt.attr + && ludp->lud_attrs && ludp->lud_attrs[0] && ludp->lud_attrs[1]) + log_info (_("WARNING: using first attribute only\n")); + + + set_timeout (); + ld = ldap_init (host, port); + if (!ld) + { + log_error (_("LDAP init to `%s:%d' failed: %s\n"), + host, port, strerror (errno)); + return -1; + } + if (ldap_simple_bind_s (ld, opt.user, opt.pass)) + { + log_error (_("binding to `%s:%d' failed: %s\n"), + host, port, strerror (errno)); + /* FIXME: Need deinit (ld)? */ + return -1; + } + + set_timeout (); + rc = ldap_search_st (ld, dn, ludp->lud_scope, filter, + opt.multi && !opt.attr && ludp->lud_attrs? + ludp->lud_attrs:attrs, + 0, + &opt.timeout, &msg); + if (rc == LDAP_SIZELIMIT_EXCEEDED && opt.multi) + { + if (fwrite ("E\0\0\0\x09truncated", 14, 1, stdout) != 1) + { + log_error (_("error writing to stdout: %s\n"), + strerror (errno)); + return -1; + } + } + else if (rc) + { + log_error (_("searching `%s' failed: %s\n"), + url, ldap_err2string (rc)); + if (rc != LDAP_NO_SUCH_OBJECT) + { + /* FIXME: Need deinit (ld)? */ + /* Hmmm: Do we need to released MSG in case of an error? */ + return -1; + } + } + + rc = print_ldap_entries (ld, msg, opt.multi? NULL:attr); + + ldap_msgfree (msg); + /* FIXME: Need deinit (ld)? */ + return rc; +} + + + + +/* Main processing. Take the URL and run the LDAP query. The result + is printed to stdout, errors are logged to the log stream. */ +static int +process_url (const char *url) +{ + int rc; + LDAPURLDesc *ludp = NULL; + + + if (!ldap_is_ldap_url (url)) + { + log_error (_("`%s' is not an LDAP URL\n"), url); + return -1; + } + + if (ldap_url_parse (url, &ludp)) + { + log_error (_("`%s' is an invalid LDAP URL\n"), url); + return -1; + } + + rc = fetch_ldap (url, ludp); + + ldap_free_urldesc (ludp); + return rc; +} + |