1 /* ldap.c - LDAP access
2 * Copyright (C) 2002 Klarälvdalens Datakonsult AB
3 * Copyright (C) 2003, 2004, 2005, 2007, 2008, 2010 g10 Code GmbH
5 * This file is part of DirMngr.
7 * DirMngr 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 2 of the License, or
10 * (at your option) any later version.
12 * DirMngr 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, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
34 #include "../common/exechelp.h"
36 #include "ldapserver.h"
38 #include "ldap-wrapper.h"
39 #include "../common/host2net.h"
42 #define UNENCODED_URL_CHARS "abcdefghijklmnopqrstuvwxyz" \
43 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
46 #define USERCERTIFICATE "userCertificate"
47 #define CACERTIFICATE "caCertificate"
48 #define X509CACERT "x509caCert"
49 #define USERSMIMECERTIFICATE "userSMIMECertificate"
52 /* Definition for the context of the cert fetch functions. */
53 struct cert_fetch_context_s
55 ksba_reader_t reader; /* The reader used (shallow copy). */
56 unsigned char *tmpbuf; /* Helper buffer. */
57 size_t tmpbufsize; /* Allocated size of tmpbuf. */
58 int truncated; /* Flag to indicate a truncated output. */
64 /* Add HOST and PORT to our list of LDAP servers. Fixme: We should
65 better use an extra list of servers. */
67 add_server_to_servers (const char *host, int port)
70 ldap_server_t last = NULL;
76 for (server=opt.ldapservers; server; server = server->next)
78 if (!strcmp (server->host, host) && server->port == port)
79 return; /* already in list... */
83 /* We assume that the host names are all supplied by our
84 configuration files and thus are sane. To keep this assumption
85 we must reject all invalid host names. */
87 if (!strchr ("abcdefghijklmnopqrstuvwxyz"
88 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
91 log_error (_("invalid char 0x%02x in host name - not added\n"), *s);
95 log_info (_("adding '%s:%d' to the ldap server list\n"), host, port);
96 server = xtrycalloc (1, sizeof *s);
98 log_error (_("malloc failed: %s\n"), strerror (errno));
101 server->host = xstrdup (host);
106 opt.ldapservers = server;
113 /* Perform an LDAP query. Returns an gpg error code or 0 on success.
114 The function returns a new reader object at READER. */
116 run_ldap_wrapper (ctrl_t ctrl,
120 const char *host, int port,
121 const char *user, const char *pass,
122 const char *dn, const char *filter, const char *attr,
124 ksba_reader_t *reader)
126 const char *argv[40];
128 char portbuf[30], timeoutbuf[30];
134 if (pass) /* Note, that the password must be the first item. */
136 argv[argc++] = "--pass";
141 argv[argc++] = "-vv";
142 else if (DBG_EXTPROG)
145 argv[argc++] = "--log-with-pid";
147 argv[argc++] = "--multi";
150 sprintf (timeoutbuf, "%u", opt.ldaptimeout);
151 argv[argc++] = "--timeout";
152 argv[argc++] = timeoutbuf;
154 argv[argc++] = "--only-search-timeout";
158 argv[argc++] = "--proxy";
159 argv[argc++] = proxy;
163 argv[argc++] = "--host";
168 sprintf (portbuf, "%d", port);
169 argv[argc++] = "--port";
170 argv[argc++] = portbuf;
174 argv[argc++] = "--user";
179 argv[argc++] = "--dn";
184 argv[argc++] = "--filter";
185 argv[argc++] = filter;
189 argv[argc++] = "--attr";
192 argv[argc++] = url? url : "ldap://";
195 return ldap_wrapper (ctrl, reader, argv);
201 /* Perform a LDAP query using a given URL. On success a new ksba
202 reader is returned. If HOST or PORT are not 0, they are used to
203 override the values from the URL. */
205 url_fetch_ldap (ctrl_t ctrl, const char *url, const char *host, int port,
206 ksba_reader_t *reader)
210 err = run_ldap_wrapper (ctrl,
211 1, /* Ignore explicit timeout because CRLs
212 might be very large. */
217 NULL, NULL, NULL, url,
220 /* FIXME: This option might be used for DoS attacks. Because it
221 will enlarge the list of servers to consult without a limit and
222 all LDAP queries w/o a host are will then try each host in
224 if (!err && opt.add_new_ldapservers && !opt.ldap_proxy)
227 add_server_to_servers (host, port);
230 char *tmp = host_and_port_from_url (url, &port);
233 add_server_to_servers (tmp, port);
239 /* If the lookup failed and we are not only using the proxy, we try
240 again using our default list of servers. */
241 if (err && !(opt.ldap_proxy && opt.only_ldap_proxy))
243 struct ldapserver_iter iter;
246 log_debug ("no hostname in URL or query failed; "
247 "trying all default hostnames\n");
249 for (ldapserver_iter_begin (&iter, ctrl);
250 err && ! ldapserver_iter_end_p (&iter);
251 ldapserver_iter_next (&iter))
253 ldap_server_t server = iter.server;
255 err = run_ldap_wrapper (ctrl,
259 server->host, server->port,
261 NULL, NULL, NULL, url,
273 /* Perform an LDAP query on all configured servers. On error the
274 error code of the last try is returned. */
276 attr_fetch_ldap (ctrl_t ctrl,
277 const char *dn, const char *attr, ksba_reader_t *reader)
279 gpg_error_t err = gpg_error (GPG_ERR_CONFIGURATION);
280 struct ldapserver_iter iter;
284 /* FIXME; we might want to look at the Base SN to try matching
286 for (ldapserver_iter_begin (&iter, ctrl); ! ldapserver_iter_end_p (&iter);
287 ldapserver_iter_next (&iter))
289 ldap_server_t server = iter.server;
291 err = run_ldap_wrapper (ctrl,
295 server->host, server->port,
296 server->user, server->pass,
297 dn, "objectClass=*", attr, NULL,
300 break; /* Probably found a result. Ready. */
306 /* Parse PATTERN and return a new strlist to be used for the actual
307 LDAP query. Bit 0 of the flags field is set if that pattern is
308 actually a base specification. Caller must release the returned
309 strlist. NULL is returned on error.
316 * x Email address Indicated by a left angle bracket.
317 * Exact word match in user id or subj. name
318 * x Subj. DN indicated bu a leading slash
320 * Serial number + subj. DN
321 * x Substring match indicated by a leading '*; is also the default.
325 parse_one_pattern (const char *pattern)
327 strlist_t result = NULL;
332 case '<': /* Email. */
335 result = xmalloc (sizeof *result + 5 + strlen (pattern));
338 p = stpcpy (stpcpy (result->d, "mail="), pattern);
341 if (!*result->d) /* Error. */
348 case '/': /* Subject DN. */
352 result = xmalloc (sizeof *result + strlen (pattern));
354 result->flags = 1; /* Base spec. */
355 strcpy (result->d, pattern);
358 case '#': /* Issuer DN. */
360 if (*pattern == '/') /* Just issuer DN. */
364 else /* Serial number + issuer DN */
371 default: /* Take as substring match. */
373 const char format[] = "(|(sn=*%s*)(|(cn=*%s*)(mail=*%s*)))";
377 result = xmalloc (sizeof *result
378 + strlen (format) + 3 * strlen (pattern));
381 sprintf (result->d, format, pattern, pattern, pattern);
390 /* Take the string STRING and escape it according to the URL rules.
391 Retun a newly allocated string. */
393 escape4url (const char *string)
402 for (s=string,n=0; *s; s++)
403 if (strchr (UNENCODED_URL_CHARS, *s))
412 for (s=string,p=buf; *s; s++)
413 if (strchr (UNENCODED_URL_CHARS, *s))
417 sprintf (p, "%%%02X", *(const unsigned char *)s);
427 /* Create a LDAP URL from DN and FILTER and return it in URL. We don't
428 need the host and port because this will be specified using the
431 make_url (char **url, const char *dn, const char *filter)
434 char *u_dn, *u_filter;
435 char const attrs[] = (USERCERTIFICATE ","
436 /* USERSMIMECERTIFICATE "," */
442 u_dn = escape4url (dn);
444 return gpg_error_from_errno (errno);
446 u_filter = escape4url (filter);
449 err = gpg_error_from_errno (errno);
454 *url = strconcat ("ldap:///", u_dn, "?", attrs, "?sub?", u_filter, NULL);
456 err = gpg_error_from_syserror ();
466 /* Prepare an LDAP query to return the attribute ATTR for the DN. All
467 configured default servers are queried until one responds. This
468 function returns an error code or 0 and a CONTEXT on success. */
470 start_default_fetch_ldap (ctrl_t ctrl, cert_fetch_context_t *context,
471 const char *dn, const char *attr)
474 struct ldapserver_iter iter;
476 *context = xtrycalloc (1, sizeof **context);
478 return gpg_error_from_errno (errno);
480 /* FIXME; we might want to look at the Base SN to try matching
482 err = gpg_error (GPG_ERR_CONFIGURATION);
484 for (ldapserver_iter_begin (&iter, ctrl); ! ldapserver_iter_end_p (&iter);
485 ldapserver_iter_next (&iter))
487 ldap_server_t server = iter.server;
489 err = run_ldap_wrapper (ctrl,
493 server->host, server->port,
494 server->user, server->pass,
495 dn, "objectClass=*", attr, NULL,
496 &(*context)->reader);
498 break; /* Probably found a result. */
510 /* Prepare an LDAP query to return certificates matching PATTERNS using
511 the SERVER. This function returns an error code or 0 and a CONTEXT
514 start_cert_fetch_ldap (ctrl_t ctrl, cert_fetch_context_t *context,
515 strlist_t patterns, const ldap_server_t server)
526 int argc_malloced = 0;
527 char portbuf[30], timeoutbuf[30];
532 if (opt.ldap_proxy && !(proxy = xtrystrdup (opt.ldap_proxy)))
534 err = gpg_error_from_syserror ();
540 if (server->host && !(host = xtrystrdup (server->host)))
542 err = gpg_error_from_syserror ();
546 if (server->user && !(user = xtrystrdup (server->user)))
548 err = gpg_error_from_syserror ();
551 if (server->pass && !(pass = xtrystrdup (server->pass)))
553 err = gpg_error_from_syserror ();
559 else /* Use a default server. */
560 return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
566 if (pass) /* Note: Must be the first item. */
568 argv[argc++] = "--pass";
573 argv[argc++] = "-vv";
574 else if (DBG_EXTPROG)
577 argv[argc++] = "--log-with-pid";
578 argv[argc++] = "--multi";
581 snprintf (timeoutbuf, sizeof timeoutbuf, "%u", opt.ldaptimeout);
582 argv[argc++] = "--timeout";
583 argv[argc++] = timeoutbuf;
587 argv[argc++] = "--proxy";
588 argv[argc++] = proxy;
592 argv[argc++] = "--host";
597 snprintf (portbuf, sizeof portbuf, "%d", port);
598 argv[argc++] = "--port";
599 argv[argc++] = portbuf;
603 argv[argc++] = "--user";
607 /* All entries in argv from this index on are malloc'ed. */
608 argc_malloced = argc;
610 for (; patterns; patterns = patterns->next)
615 if (argc >= DIM (argv) - 1)
617 /* Too many patterns. It does not make sense to allow an
618 arbitrary number of patters because the length of the
619 command line is limited anyway. */
620 /* fixme: cleanup. */
621 return gpg_error (GPG_ERR_RESOURCE_LIMIT);
623 sl = parse_one_pattern (patterns->d);
626 log_error (_("start_cert_fetch: invalid pattern '%s'\n"),
628 err = gpg_error (GPG_ERR_INV_USER_ID);
632 err = make_url (&url, sl->d, "objectClass=*");
634 err = make_url (&url, base, sl->d);
642 *context = xtrycalloc (1, sizeof **context);
645 err = gpg_error_from_errno (errno);
649 err = ldap_wrapper (ctrl, &(*context)->reader, (const char**)argv);
658 for (; argc_malloced < argc; argc_malloced++)
659 xfree (argv[argc_malloced]);
668 /* Read a fixed amount of data from READER into BUFFER. */
670 read_buffer (ksba_reader_t reader, unsigned char *buffer, size_t count)
677 err = ksba_reader_read (reader, buffer, count, &nread);
687 /* Fetch the next certificate. Return 0 on success, GPG_ERR_EOF if no
688 (more) certificates are available or any other error
689 code. GPG_ERR_TRUNCATED may be returned to indicate that the result
690 has been truncated. */
692 fetch_next_cert_ldap (cert_fetch_context_t context,
693 unsigned char **value, size_t *valuelen)
696 unsigned char hdr[5];
700 /* int is_cms = 0; */
708 err = read_buffer (context->reader, hdr, 5);
711 n = buf32_to_ulong (hdr+1);
712 if (*hdr == 'V' && okay)
714 #if 0 /* That code is not yet ready. */
718 /* The certificate needs to be parsed from CMS data. */
720 ksba_stop_reason_t stopreason;
723 err = ksba_cms_new (&cms);
726 err = ksba_cms_set_reader_writer (cms, context->reader, NULL);
729 log_error ("ksba_cms_set_reader_writer failed: %s\n",
736 err = ksba_cms_parse (cms, &stopreason);
739 log_error ("ksba_cms_parse failed: %s\n",
744 if (stopreason == KSBA_SR_BEGIN_DATA)
745 log_error ("userSMIMECertificate is not "
746 "a certs-only message\n");
748 while (stopreason != KSBA_SR_READY);
750 for (i=0; (cert=ksba_cms_get_cert (cms, i)); i++)
752 check_and_store (ctrl, stats, cert, 0);
753 ksba_cert_release (cert);
757 log_error ("no certificate found\n");
764 *value = xtrymalloc (n);
766 return gpg_error_from_errno (errno);
768 err = read_buffer (context->reader, *value, n);
769 break; /* Ready or error. */
772 else if (!n && *hdr == 'A')
776 if (n > context->tmpbufsize)
778 xfree (context->tmpbuf);
779 context->tmpbufsize = 0;
780 context->tmpbuf = xtrymalloc (n+1);
781 if (!context->tmpbuf)
782 return gpg_error_from_errno (errno);
783 context->tmpbufsize = n;
785 err = read_buffer (context->reader, context->tmpbuf, n);
791 p[n] = 0; /*(we allocated one extra byte for this.)*/
792 /* fixme: is_cms = 0; */
793 if ( (pend = strchr (p, ';')) )
794 *pend = 0; /* Strip off the extension. */
795 if (!ascii_strcasecmp (p, USERCERTIFICATE))
798 log_debug ("fetch_next_cert_ldap: got attribute '%s'\n",
802 else if (!ascii_strcasecmp (p, CACERTIFICATE))
805 log_debug ("fetch_next_cert_ldap: got attribute '%s'\n",
809 else if (!ascii_strcasecmp (p, X509CACERT))
812 log_debug ("fetch_next_cert_ldap: got attribute '%s'\n",
816 /* else if (!ascii_strcasecmp (p, USERSMIMECERTIFICATE)) */
818 /* if (DBG_LOOKUP) */
819 /* log_debug ("fetch_next_cert_ldap: got attribute '%s'\n", */
820 /* USERSMIMECERTIFICATE); */
827 log_debug ("fetch_next_cert_ldap: got attribute '%s'"
832 else if (*hdr == 'E')
835 p[n] = 0; /*(we allocated one extra byte for this.)*/
836 if (!strcmp (p, "truncated"))
838 context->truncated = 1;
839 log_info (_("ldap_search hit the size limit of"
851 if (gpg_err_code (err) == GPG_ERR_EOF && context->truncated)
853 context->truncated = 0; /* So that the next call would return EOF. */
854 err = gpg_error (GPG_ERR_TRUNCATED);
863 end_cert_fetch_ldap (cert_fetch_context_t context)
867 ksba_reader_t reader = context->reader;
869 xfree (context->tmpbuf);
871 ldap_wrapper_release_context (reader);
872 ksba_reader_release (reader);