summaryrefslogtreecommitdiffstats
path: root/dirmngr/ldap-parse-uri.c
diff options
context:
space:
mode:
Diffstat (limited to 'dirmngr/ldap-parse-uri.c')
-rw-r--r--dirmngr/ldap-parse-uri.c262
1 files changed, 262 insertions, 0 deletions
diff --git a/dirmngr/ldap-parse-uri.c b/dirmngr/ldap-parse-uri.c
new file mode 100644
index 0000000..c36763e
--- /dev/null
+++ b/dirmngr/ldap-parse-uri.c
@@ -0,0 +1,262 @@
+/* ldap-parse-uri.c - Parse an LDAP URI.
+ * Copyright (C) 2015 g10 Code GmbH
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <gpg-error.h>
+
+#ifdef HAVE_W32_SYSTEM
+# include "ldap-url.h"
+#else
+# include <ldap.h>
+#endif
+
+#include "../common/util.h"
+#include "http.h"
+
+/* Returns 1 if the string is an LDAP URL (begins with ldap:, ldaps:
+ or ldapi:). */
+int
+ldap_uri_p (const char *url)
+{
+ char *colon = strchr (url, ':');
+
+ if (!colon)
+ return 0;
+ else
+ {
+ int offset = (uintptr_t) colon - (uintptr_t) url;
+
+ if ( (offset == 4 && !ascii_memcasecmp (url, "ldap", 4))
+ || (offset == 5 && (!ascii_memcasecmp (url, "ldaps", 5)
+ || !ascii_memcasecmp (url, "ldapi", 5))))
+ return 1;
+ return 0;
+ }
+}
+
+/* Parse a URI and put the result into *purip. On success the
+ caller must use http_release_parsed_uri() to releases the resources.
+
+ uri->path is the base DN (or NULL for the default).
+ uri->auth is the bindname (or NULL for none).
+ The uri->query variable "password" is the password.
+
+ Note: any specified scope, any attributes, any filter and any
+ unknown extensions are simply ignored. */
+gpg_error_t
+ldap_parse_uri (parsed_uri_t *purip, const char *uri)
+{
+ gpg_err_code_t err = 0;
+ parsed_uri_t puri = NULL;
+
+ int result;
+ LDAPURLDesc *lud = NULL;
+
+ char *scheme = NULL;
+ char *host = NULL;
+ char *dn = NULL;
+ char *bindname = NULL;
+ char *password = NULL;
+ char *gpg_ntds = NULL;
+
+ char **s;
+
+ char *buffer;
+ int len;
+
+ result = ldap_url_parse (uri, &lud);
+ if (result != 0)
+ {
+ log_error ("Unable to parse LDAP uri '%s'\n", uri);
+ err = GPG_ERR_GENERAL;
+ goto out;
+ }
+
+ scheme = lud->lud_scheme;
+ host = lud->lud_host;
+ dn = lud->lud_dn;
+
+ for (s = lud->lud_exts; s && *s; s ++)
+ {
+ if (strncmp (*s, "bindname=", 9) == 0)
+ {
+ if (bindname)
+ log_error ("bindname given multiple times in URL '%s', ignoring.\n",
+ uri);
+ else
+ bindname = *s + 9;
+ }
+ else if (strncmp (*s, "password=", 9) == 0)
+ {
+ if (password)
+ log_error ("password given multiple times in URL '%s', ignoring.\n",
+ uri);
+ else
+ password = *s + 9;
+ }
+ else if (!ascii_strncasecmp (*s, "gpgNtds=", 8)
+ || !strncmp (*s, "1.3.6.1.4.1.11591.2.5.1=", 24))
+ {
+ if (gpg_ntds)
+ log_error ("gpgNtds given multiple times in URL '%s', ignoring.\n",
+ uri);
+ else
+ gpg_ntds = *s + (**s == 'g'? 8 : 24);
+ }
+ else
+ log_error ("Unhandled extension (%s) in URL '%s', ignoring.",
+ *s, uri);
+ }
+
+ len = 0;
+
+#define add(s) do { if (s) len += strlen (s) + 1; } while (0)
+
+ add (scheme);
+ add (host);
+ add (dn);
+ add (bindname);
+ add (password);
+
+ puri = xtrycalloc (1, sizeof *puri + len);
+ if (! puri)
+ {
+ err = gpg_err_code_from_syserror ();
+ goto out;
+ }
+
+ buffer = puri->buffer;
+
+#define copy(to, s) \
+ do \
+ { \
+ if (s) \
+ { \
+ to = buffer; \
+ buffer = stpcpy (buffer, s) + 1; \
+ } \
+ } \
+ while (0)
+
+ copy (puri->scheme, scheme);
+ /* Make sure the scheme is lower case. */
+ ascii_strlwr (puri->scheme);
+
+ copy (puri->host, host);
+ copy (puri->path, dn);
+ copy (puri->auth, bindname);
+
+ if (password)
+ {
+ puri->query = calloc (sizeof (*puri->query), 1);
+ if (!puri->query)
+ {
+ err = gpg_err_code_from_syserror ();
+ goto out;
+ }
+ puri->query->name = "password";
+ copy (puri->query->value, password);
+ puri->query->valuelen = strlen (password) + 1;
+ }
+
+ puri->use_tls = !strcmp (puri->scheme, "ldaps");
+ puri->port = lud->lud_port;
+
+ /* On Windows detect whether this is ldap:// or ldaps:// to indicate
+ * that authentication via AD and the current user is requested.
+ * This is shortform of adding "gpgNtDs=1" as extension parameter to
+ * the URL. */
+ puri->ad_current = 0;
+ if (gpg_ntds && atoi (gpg_ntds) == 1)
+ puri->ad_current = 1;
+#ifdef HAVE_W32_SYSTEM
+ else if ((!puri->host || !*puri->host)
+ && (!puri->path || !*puri->path)
+ && (!puri->auth || !*puri->auth)
+ && !password
+ )
+ puri->ad_current = 1;
+#endif
+
+ out:
+ if (lud)
+ ldap_free_urldesc (lud);
+
+ if (err)
+ {
+ if (puri)
+ http_release_parsed_uri (puri);
+ }
+ else
+ *purip = puri;
+
+ return gpg_err_make (default_errsource, err);
+}
+
+/* The following characters need to be escaped to be part of an LDAP
+ filter: *, (, ), \, NUL and /. Note: we don't handle NUL, since a
+ NUL can't be part of a C string.
+
+ This function always allocates a new string on success. It is the
+ caller's responsibility to free it.
+*/
+char *
+ldap_escape_filter (const char *filter)
+{
+ int l = strcspn (filter, "*()\\/");
+ if (l == strlen (filter))
+ /* Nothing to escape. */
+ return xstrdup (filter);
+
+ {
+ /* In the worst case we need to escape every letter. */
+ char *escaped = xmalloc (1 + 3 * strlen (filter));
+
+ /* Indices into filter and escaped. */
+ int filter_i = 0;
+ int escaped_i = 0;
+
+ for (filter_i = 0; filter_i < strlen (filter); filter_i ++)
+ {
+ switch (filter[filter_i])
+ {
+ case '*':
+ case '(':
+ case ')':
+ case '\\':
+ case '/':
+ snprintf (&escaped[escaped_i], 4, "%%%02x",
+ ((const unsigned char *)filter)[filter_i]);
+ escaped_i += 3;
+ break;
+
+ default:
+ escaped[escaped_i ++] = filter[filter_i];
+ break;
+ }
+ }
+ /* NUL terminate it. */
+ escaped[escaped_i] = 0;
+
+ /* We could shrink escaped to be just escaped_i bytes, but the
+ result will probably be freed very quickly anyways. */
+ return escaped;
+ }
+}