1 /* ldap.c - LDAP access
2 * Copyright (C) 2002 Klarälvdalens Datakonsult AB
3 * Copyright (C) 2003, 2004, 2005, 2007, 2008, 2010, 2021 g10 Code GmbH
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 * 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, see <https://www.gnu.org/licenses/>.
19 * SPDX-License-Identifier: GPL-3.0-or-later
34 #include "../common/exechelp.h"
36 #include "ldapserver.h"
38 #include "ldap-wrapper.h"
40 #include "../common/host2net.h"
43 #define UNENCODED_URL_CHARS "abcdefghijklmnopqrstuvwxyz" \
44 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
47 #define USERCERTIFICATE "userCertificate"
48 #define CACERTIFICATE "caCertificate"
49 #define X509CACERT "x509caCert"
50 #define USERSMIMECERTIFICATE "userSMIMECertificate"
53 /* Definition for the context of the cert fetch functions. */
54 struct cert_fetch_context_s
56 ksba_reader_t reader; /* The reader used (shallow copy). */
57 unsigned char *tmpbuf; /* Helper buffer. */
58 size_t tmpbufsize; /* Allocated size of tmpbuf. */
59 int truncated; /* Flag to indicate a truncated output. */
65 /* Add HOST and PORT to our list of LDAP servers. Fixme: We should
66 better use an extra list of servers. */
68 add_server_to_servers (const char *host, int port)
71 ldap_server_t last = NULL;
77 for (server=opt.ldapservers; server; server = server->next)
79 if (!strcmp (server->host, host) && server->port == port)
80 return; /* already in list... */
84 /* We assume that the host names are all supplied by our
85 configuration files and thus are sane. To keep this assumption
86 we must reject all invalid host names. */
88 if (!strchr ("abcdefghijklmnopqrstuvwxyz"
89 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
92 log_error (_("invalid char 0x%02x in host name - not added\n"), *s);
96 log_info (_("adding '%s:%d' to the ldap server list\n"), host, port);
97 server = xtrycalloc (1, sizeof *s);
99 log_error (_("malloc failed: %s\n"), strerror (errno));
102 server->host = xstrdup (host);
107 opt.ldapservers = server;
114 /* Perform an LDAP query. Returns an gpg error code or 0 on success.
115 The function returns a new reader object at READER. */
117 run_ldap_wrapper (ctrl_t ctrl,
124 const char *host, int port,
125 const char *user, const char *pass,
126 const char *base, const char *filter, const char *attr,
127 ksba_reader_t *reader)
129 const char *argv[51];
131 char portbuf[30], timeoutbuf[30];
137 if (pass && *pass) /* Note, that the password must be the first item. */
139 argv[argc++] = "--pass";
144 argv[argc++] = "-vv";
145 else if (DBG_EXTPROG)
148 argv[argc++] = "--log-with-pid";
150 argv[argc++] = "--multi";
153 argv[argc++] = "--starttls";
155 argv[argc++] = "--ldaptls";
158 argv[argc++] = "--ntds";
161 argv[argc++] = "--areconly";
165 snprintf (timeoutbuf, sizeof timeoutbuf, "%u", opt.ldaptimeout);
166 argv[argc++] = "--timeout";
167 argv[argc++] = timeoutbuf;
169 argv[argc++] = "--only-search-timeout";
173 argv[argc++] = "--proxy";
174 argv[argc++] = proxy;
178 argv[argc++] = "--host";
183 sprintf (portbuf, "%d", port);
184 argv[argc++] = "--port";
185 argv[argc++] = portbuf;
189 argv[argc++] = "--user";
194 argv[argc++] = "--base";
199 argv[argc++] = "--attr";
204 argv[argc++] = filter;
207 return ldap_wrapper (ctrl, reader, argv);
213 /* Perform a LDAP query using a given URL. On success a new ksba
214 reader is returned. If HOST or PORT are not 0, they are used to
215 override the values from the URL. */
217 url_fetch_ldap (ctrl_t ctrl, const char *url, ksba_reader_t *reader)
220 LDAPURLDesc *ludp = NULL;
223 if (!ldap_is_ldap_url (url))
225 log_error (_("'%s' is not an LDAP URL\n"), url);
226 return gpg_error (GPG_ERR_INV_URI);
229 if (ldap_url_parse (url, &ludp))
231 log_error (_("'%s' is an invalid LDAP URL\n"), url);
232 return gpg_error (GPG_ERR_INV_URI);
235 if (ludp->lud_filter && ludp->lud_filter[0] != '(')
237 if (!strcmp (ludp->lud_filter, "objectClass=cRLDistributionPoint"))
239 /* Hack for broken DPs in DGN certs. */
240 log_info ("fixing broken LDAP URL\n");
241 free (ludp->lud_filter);
243 = strdup ("(objectClass=cRLDistributionPoint)");
244 if (!ludp->lud_filter)
246 err = gpg_error_from_syserror ();
252 log_error (_("'%s' is an invalid LDAP URL\n"), url);
253 err = gpg_error (GPG_ERR_BAD_URI);
258 if (ludp->lud_scheme && !strcmp (ludp->lud_scheme, "ldaps"))
259 tls_mode = 2; /* LDAP-over-TLS here becuase we get it from certs. */
263 err = run_ldap_wrapper (ctrl,
264 1, /* Ignore explicit timeout because CRLs
265 might be very large. */
266 0, /* No Multi-mode. */
268 0, /* No AD authentication. */
269 0, /* No areconly. */
271 ludp->lud_host, ludp->lud_port,
272 NULL, NULL, /* user, password */
273 ludp->lud_dn, /* Base DN */
275 ludp->lud_attrs? ludp->lud_attrs[0] : NULL,
278 /* FIXME: This option might be used for DoS attacks. Because it
279 will enlarge the list of servers to consult without a limit and
280 all LDAP queries w/o a host are will then try each host in
282 if (!err && opt.add_new_ldapservers && !opt.ldap_proxy)
285 add_server_to_servers (ludp->lud_host, ludp->lud_port);
288 /* If the lookup failed and we are not only using the proxy, we try
289 again using our default list of servers. */
290 if (err && !(opt.ldap_proxy && opt.only_ldap_proxy))
292 struct ldapserver_iter iter;
295 log_debug ("no hostname in URL or query failed; "
296 "trying all default hostnames\n");
298 for (ldapserver_iter_begin (&iter, ctrl);
299 err && ! ldapserver_iter_end_p (&iter);
300 ldapserver_iter_next (&iter))
302 ldap_server_t server = iter.server;
304 if (server->starttls)
306 else if (server->ldap_over_tls)
311 err = run_ldap_wrapper (ctrl,
313 0, /* No Multi-mode */
318 server->host, server->port,
319 server->user, server->pass,
322 ludp->lud_attrs? ludp->lud_attrs[0] : NULL,
330 ldap_free_urldesc (ludp);
336 /* Perform an LDAP query on all configured servers. On error the
337 error code of the last try is returned. */
339 attr_fetch_ldap (ctrl_t ctrl,
340 const char *dn, const char *attr, ksba_reader_t *reader)
342 gpg_error_t err = gpg_error (GPG_ERR_CONFIGURATION);
343 struct ldapserver_iter iter;
347 /* FIXME; we might want to look at the Base DN to try matching
349 for (ldapserver_iter_begin (&iter, ctrl); ! ldapserver_iter_end_p (&iter);
350 ldapserver_iter_next (&iter))
352 ldap_server_t server = iter.server;
355 if (server->starttls)
357 else if (server->ldap_over_tls)
362 err = run_ldap_wrapper (ctrl,
369 server->host, server->port,
370 server->user, server->pass,
376 break; /* Probably found a result. Ready. */
383 /* Return true if VALUE needs escaping. */
385 rfc2254_need_escape (const char *value)
387 /* NUL needs to be escaped as well but we can represent that in
388 * VALUE, so no need for it. */
389 return !!strpbrk (value, "*()\\");
392 /* Escape VALUE using RFC-2254 rules. Returns NULL on error. */
394 rfc2254_escape (const char *value)
400 for (s=value; *s; s++)
406 case '\\': length += 3; break;
407 default: length++; break;
410 buffer = xtrymalloc (length+1);
414 for (s=value; *s; s++)
417 case '*': p = stpcpy (p, "\\2a"); break;
418 case '(': p = stpcpy (p, "\\28"); break;
419 case ')': p = stpcpy (p, "\\29"); break;
420 case '\\': p = stpcpy (p, "\\5c"); break;
421 default: *p++ = *s; break;
428 /* Return true if VALUE needs escaping. */
430 extfilt_need_escape (const char *value)
432 /* NUL needs to be escaped as well but we can represent that in
433 * VALUE, so no need for it. */
434 return !!strchr (value, '&');
437 /* Escape VALUE using our extended filter rules from dirmngr_ldap.c.
438 * Returns NULL on error. */
440 extfilt_escape (const char *value)
446 for (s=value; *s; s++)
453 buffer = xtrymalloc (length+1);
457 for (s=value; *s; s++)
468 /* Parse PATTERN and return a new filter expression for an LDAP query.
469 * The extended filter syntax as known by dirmngr_ldap.c is used.
470 * Caller must release the returned value. R_RESULT is set to NULL on
473 * Supported patterns:
475 * | Ok | gpg style user id type |
476 * |-----+------------------------------------------------------|
478 * | no | Fingerprint |
479 * | no | OpenPGP userid |
480 * | yes | Email address Indicated by a left angle bracket. |
481 * | no | Exact word match in user id or subj. name |
482 * | yes | Subj. DN indicated by a leading slash |
484 * | no | Serial number + subj. DN |
485 * | yes | Substring match indicated by a leading '*; (default) |
488 make_one_filter (const char *pattern, char **r_result)
491 char *pattern_buffer = NULL;
499 case '<': /* Email. */
502 if (rfc2254_need_escape (pattern)
503 && !(pattern = pattern_buffer = rfc2254_escape (pattern)))
505 err = gpg_error_from_syserror ();
508 result = strconcat ("(mail=", pattern, ")", NULL);
511 err = gpg_error_from_syserror ();
515 if (result[n-2] == '>') /* Strip trailing '>' */
522 case '/': /* Subject DN. */
526 /* We need just the BaseDN. This assumes that the Subject
527 * is correcly stored in the DT. This is however not always
528 * the case and the actual DN is different from the
529 * subject. In this case we won't find anything. */
530 if (extfilt_need_escape (pattern)
531 && !(pattern = pattern_buffer = extfilt_escape (pattern)))
533 err = gpg_error_from_syserror ();
536 result = strconcat ("^", pattern, "&base&", NULL);
539 err = gpg_error_from_syserror ();
544 case '#': /* Issuer DN - Not yet working. */
546 if (*pattern == '/') /* Just issuer DN. */
549 if (extfilt_need_escape (pattern)
550 && !(pattern = pattern_buffer = extfilt_escape (pattern)))
552 err = gpg_error_from_syserror ();
555 result = strconcat ("^", pattern, "&base&", NULL);
558 err = gpg_error_from_syserror ();
562 else /* Serial number + issuer DN */
570 default: /* Take as substring match. */
573 if (rfc2254_need_escape (pattern)
574 && !(pattern = pattern_buffer = rfc2254_escape (pattern)))
576 err = gpg_error_from_syserror ();
579 result = strconcat ("(|(sn=*", pattern,
580 "*)(|(cn=*", pattern,
581 "*)(mail=*", pattern,
585 err = gpg_error_from_syserror ();
593 err = gpg_error (GPG_ERR_INV_USER_ID);
596 xfree (pattern_buffer);
606 /* Prepare an LDAP query to return the cACertificate attribute for DN.
607 * All configured default servers are queried until one responds.
608 * This function returns an error code or 0 and stored a newly
609 * allocated contect object at CONTEXT on success. */
611 start_cacert_fetch_ldap (ctrl_t ctrl, cert_fetch_context_t *r_context,
615 struct ldapserver_iter iter;
617 *r_context = xtrycalloc (1, sizeof **r_context);
619 return gpg_error_from_errno (errno);
621 /* FIXME; we might want to look at the Base DN to try matching
623 err = gpg_error (GPG_ERR_CONFIGURATION);
625 for (ldapserver_iter_begin (&iter, ctrl); ! ldapserver_iter_end_p (&iter);
626 ldapserver_iter_next (&iter))
628 ldap_server_t server = iter.server;
630 err = run_ldap_wrapper (ctrl,
632 1, /* --multi (record format) */
634 0, /* No AD authentication. */
637 server->host, server->port,
638 server->user, server->pass,
639 dn, "objectClass=*", "cACertificate",
640 &(*r_context)->reader);
642 break; /* Probably found a result. */
654 /* Prepare an LDAP query to return certificates matching PATTERNS
655 * using the SERVER. This function returns an error code or 0 and
656 * stores a newly allocated object at R_CONTEXT on success. */
658 start_cert_fetch_ldap (ctrl_t ctrl, cert_fetch_context_t *r_context,
659 strlist_t patterns, const ldap_server_t server)
670 int argc_malloced = 0;
671 char portbuf[30], timeoutbuf[30];
672 int starttls, ldaptls, ntds;
676 if (opt.ldap_proxy && !(proxy = xtrystrdup (opt.ldap_proxy)))
678 err = gpg_error_from_syserror ();
684 if (server->host && !(host = xtrystrdup (server->host)))
686 err = gpg_error_from_syserror ();
690 if (server->user && !(user = xtrystrdup (server->user)))
692 err = gpg_error_from_syserror ();
695 if (server->pass && !(pass = xtrystrdup (server->pass)))
697 err = gpg_error_from_syserror ();
700 if (server->base && !(base = xtrystrdup (server->base)))
702 err = gpg_error_from_syserror ();
706 starttls = server->starttls;
707 ldaptls = server->ldap_over_tls;
710 else /* Use a default server. */
712 err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
717 if (pass && *pass) /* Note: Must be the first item. */
719 argv[argc++] = "--pass";
724 argv[argc++] = "-vv";
725 else if (DBG_EXTPROG)
728 argv[argc++] = "--log-with-pid";
729 argv[argc++] = "--multi";
732 argv[argc++] = "--starttls";
734 argv[argc++] = "--ldaptls";
737 argv[argc++] = "--ntds";
741 snprintf (timeoutbuf, sizeof timeoutbuf, "%u", opt.ldaptimeout);
742 argv[argc++] = "--timeout";
743 argv[argc++] = timeoutbuf;
747 argv[argc++] = "--proxy";
748 argv[argc++] = proxy;
752 argv[argc++] = "--host";
757 snprintf (portbuf, sizeof portbuf, "%d", port);
758 argv[argc++] = "--port";
759 argv[argc++] = portbuf;
763 argv[argc++] = "--user";
768 argv[argc++] = "--base";
773 /* All entries in argv from this index on are malloc'ed. */
774 argc_malloced = argc;
776 for (; patterns; patterns = patterns->next)
778 if (argc >= DIM (argv) - 1)
780 /* Too many patterns. It does not make sense to allow an
781 arbitrary number of patters because the length of the
782 command line is limited anyway. */
783 err = gpg_error (GPG_ERR_RESOURCE_LIMIT);
788 err = make_one_filter (patterns->d, &argv[argc]);
796 *r_context = xtrycalloc (1, sizeof **r_context);
799 err = gpg_error_from_syserror ();
803 err = ldap_wrapper (ctrl, &(*r_context)->reader, (const char**)argv);
811 for (; argc_malloced < argc; argc_malloced++)
812 xfree (argv[argc_malloced]);
822 /* Read a fixed amount of data from READER into BUFFER. */
824 read_buffer (ksba_reader_t reader, unsigned char *buffer, size_t count)
831 err = ksba_reader_read (reader, buffer, count, &nread);
841 /* Fetch the next certificate. Return 0 on success, GPG_ERR_EOF if no
842 (more) certificates are available or any other error
843 code. GPG_ERR_TRUNCATED may be returned to indicate that the result
844 has been truncated. */
846 fetch_next_cert_ldap (cert_fetch_context_t context,
847 unsigned char **value, size_t *valuelen)
850 unsigned char hdr[5];
854 /* int is_cms = 0; */
862 err = read_buffer (context->reader, hdr, 5);
865 n = buf32_to_ulong (hdr+1);
866 if (*hdr == 'V' && okay)
868 #if 0 /* That code to extra a cert from a CMS object is not yet ready. */
871 /* The certificate needs to be parsed from CMS data. */
873 ksba_stop_reason_t stopreason;
876 err = ksba_cms_new (&cms);
879 err = ksba_cms_set_reader_writer (cms, context->reader, NULL);
882 log_error ("ksba_cms_set_reader_writer failed: %s\n",
889 err = ksba_cms_parse (cms, &stopreason);
892 log_error ("ksba_cms_parse failed: %s\n",
897 if (stopreason == KSBA_SR_BEGIN_DATA)
898 log_error ("userSMIMECertificate is not "
899 "a certs-only message\n");
901 while (stopreason != KSBA_SR_READY);
903 for (i=0; (cert=ksba_cms_get_cert (cms, i)); i++)
905 check_and_store (ctrl, stats, cert, 0);
906 ksba_cert_release (cert);
910 log_error ("no certificate found\n");
915 #endif /* End unfinished code to extract from a CMS object. */
917 *value = xtrymalloc (n);
919 return gpg_error_from_errno (errno);
921 err = read_buffer (context->reader, *value, n);
922 break; /* Ready or error. */
925 else if (!n && *hdr == 'A')
929 if (n > context->tmpbufsize)
931 xfree (context->tmpbuf);
932 context->tmpbufsize = 0;
933 context->tmpbuf = xtrymalloc (n+1);
934 if (!context->tmpbuf)
935 return gpg_error_from_errno (errno);
936 context->tmpbufsize = n;
938 err = read_buffer (context->reader, context->tmpbuf, n);
944 p[n] = 0; /*(we allocated one extra byte for this.)*/
945 /* fixme: is_cms = 0; */
946 if ( (pend = strchr (p, ';')) )
947 *pend = 0; /* Strip off the extension. */
948 if (!ascii_strcasecmp (p, USERCERTIFICATE))
951 log_debug ("fetch_next_cert_ldap: got attribute '%s'\n",
955 else if (!ascii_strcasecmp (p, CACERTIFICATE))
958 log_debug ("fetch_next_cert_ldap: got attribute '%s'\n",
962 else if (!ascii_strcasecmp (p, X509CACERT))
965 log_debug ("fetch_next_cert_ldap: got attribute '%s'\n",
969 /* else if (!ascii_strcasecmp (p, USERSMIMECERTIFICATE)) */
971 /* if (DBG_LOOKUP) */
972 /* log_debug ("fetch_next_cert_ldap: got attribute '%s'\n", */
973 /* USERSMIMECERTIFICATE); */
980 log_debug ("fetch_next_cert_ldap: got attribute '%s'"
985 else if (*hdr == 'E')
988 p[n] = 0; /*(we allocated one extra byte for this.)*/
989 if (!strcmp (p, "truncated"))
991 context->truncated = 1;
992 log_info (_("ldap_search hit the size limit of"
1004 if (gpg_err_code (err) == GPG_ERR_EOF && context->truncated)
1006 context->truncated = 0; /* So that the next call would return EOF. */
1007 err = gpg_error (GPG_ERR_TRUNCATED);
1016 end_cert_fetch_ldap (cert_fetch_context_t context)
1020 ksba_reader_t reader = context->reader;
1022 xfree (context->tmpbuf);
1024 ldap_wrapper_release_context (reader);
1025 ksba_reader_release (reader);