1 /* ldap-parse-uri.c - Parse an LDAP URI.
2  * Copyright (C) 2015  g10 Code GmbH
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG 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 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG 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.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <https://www.gnu.org/licenses/>.
18  */
19 
20 #include <config.h>
21 
22 #include <gpg-error.h>
23 
24 #ifdef HAVE_W32_SYSTEM
25 # include "ldap-url.h"
26 #else
27 # include <ldap.h>
28 #endif
29 
30 #include "../common/util.h"
31 #include "http.h"
32 
33 /* Returns 1 if the string is an LDAP URL.  */
34 int
ldap_uri_p(const char * url)35 ldap_uri_p (const char *url)
36 {
37   parsed_uri_t puri;
38   int result;
39 
40   if (http_parse_uri (&puri, url, 1))
41     result = 0;
42   else
43     result = !!puri->is_ldap;
44 
45   http_release_parsed_uri (puri);
46 
47   return result;
48 }
49 
50 
51 /* Parse a URI and put the result into *purip.  On success the
52    caller must use http_release_parsed_uri() to releases the resources.
53 
54    uri->path is the base DN (or NULL for the default).
55    uri->auth is the bindname (or NULL for none).
56    The uri->query variable "password" is the password.
57 
58    Note: any specified scope, any attributes, any filter and any
59    unknown extensions are simply ignored.  */
60 gpg_error_t
ldap_parse_uri(parsed_uri_t * purip,const char * uri)61 ldap_parse_uri (parsed_uri_t *purip, const char *uri)
62 {
63   gpg_err_code_t err = 0;
64   parsed_uri_t puri = NULL;
65 
66   int result;
67   LDAPURLDesc *lud = NULL;
68 
69   char *scheme = NULL;
70   char *host = NULL;
71   char *dn = NULL;
72   char *bindname = NULL;
73   char *password = NULL;
74   char *gpg_ntds = NULL;
75 
76   char **s;
77 
78   char *buffer;
79   int len;
80 
81   result = ldap_url_parse (uri, &lud);
82   if (result != 0)
83     {
84       log_error ("Unable to parse LDAP uri '%s'\n", uri);
85       err = GPG_ERR_GENERAL;
86       goto out;
87     }
88 
89   scheme = lud->lud_scheme;
90   host = lud->lud_host;
91   dn = lud->lud_dn;
92 
93   for (s = lud->lud_exts; s && *s; s ++)
94     {
95       if (strncmp (*s, "bindname=", 9) == 0)
96 	{
97 	  if (bindname)
98 	    log_error ("bindname given multiple times in URL '%s', ignoring.\n",
99 		       uri);
100 	  else
101 	    bindname = *s + 9;
102 	}
103       else if (strncmp (*s, "password=", 9) == 0)
104 	{
105 	  if (password)
106 	    log_error ("password given multiple times in URL '%s', ignoring.\n",
107 		       uri);
108 	  else
109 	    password = *s + 9;
110 	}
111       else if (!ascii_strncasecmp (*s, "gpgNtds=", 8)
112               || !strncmp (*s, "1.3.6.1.4.1.11591.2.5.1=", 24))
113 	{
114 	  if (gpg_ntds)
115 	    log_error ("gpgNtds given multiple times in URL '%s', ignoring.\n",
116 		       uri);
117 	  else
118 	    gpg_ntds = *s + (**s == 'g'? 8 : 24);
119 	}
120       else
121 	log_error ("Unhandled extension (%s) in URL '%s', ignoring.",
122 		   *s, uri);
123     }
124 
125   len = 0;
126 
127 #define add(s) do { if (s) len += strlen (s) + 1; } while (0)
128 
129   add (scheme);
130   add (host);
131   add (dn);
132   add (bindname);
133   add (password);
134 
135   puri = xtrycalloc (1, sizeof *puri + len);
136   if (! puri)
137     {
138       err = gpg_err_code_from_syserror ();
139       goto out;
140     }
141 
142   buffer = puri->buffer;
143 
144 #define copy(to, s)				\
145   do						\
146     {						\
147       if (s)					\
148 	{					\
149 	  to = buffer;				\
150 	  buffer = stpcpy (buffer, s) + 1;	\
151 	}					\
152     }						\
153   while (0)
154 
155   copy (puri->scheme, scheme);
156   /* Make sure the scheme is lower case.  */
157   ascii_strlwr (puri->scheme);
158 
159   copy (puri->host, host);
160   copy (puri->path, dn);
161   copy (puri->auth, bindname);
162 
163   if (password)
164     {
165       puri->query = calloc (sizeof (*puri->query), 1);
166       if (!puri->query)
167         {
168           err = gpg_err_code_from_syserror ();
169           goto out;
170         }
171       puri->query->name = "password";
172       copy (puri->query->value, password);
173       puri->query->valuelen = strlen (password) + 1;
174     }
175 
176   puri->use_tls = !strcmp (puri->scheme, "ldaps");
177   puri->port = lud->lud_port;
178 
179   /* On Windows detect whether this is ldap:// or ldaps:// to indicate
180    * that authentication via AD and the current user is requested.
181    * This is shortform of adding "gpgNtDs=1" as extension parameter to
182    * the URL.  */
183   puri->ad_current = 0;
184   if (gpg_ntds && atoi (gpg_ntds) == 1)
185     puri->ad_current = 1;
186 #ifdef HAVE_W32_SYSTEM
187   else if ((!puri->host || !*puri->host)
188       && (!puri->path || !*puri->path)
189       && (!puri->auth || !*puri->auth)
190       && !password
191       )
192     puri->ad_current = 1;
193 #endif
194 
195  out:
196   if (lud)
197     ldap_free_urldesc (lud);
198 
199   if (err)
200     {
201       if (puri)
202 	http_release_parsed_uri (puri);
203     }
204   else
205     *purip = puri;
206 
207   return gpg_err_make (default_errsource, err);
208 }
209 
210 /* The following characters need to be escaped to be part of an LDAP
211    filter: *, (, ), \, NUL and /.  Note: we don't handle NUL, since a
212    NUL can't be part of a C string.
213 
214    This function always allocates a new string on success.  It is the
215    caller's responsibility to free it.
216 */
217 char *
ldap_escape_filter(const char * filter)218 ldap_escape_filter (const char *filter)
219 {
220   int l = strcspn (filter, "*()\\/");
221   if (l == strlen (filter))
222     /* Nothing to escape.  */
223     return xstrdup (filter);
224 
225   {
226     /* In the worst case we need to escape every letter.  */
227     char *escaped = xmalloc (1 + 3 * strlen (filter));
228 
229     /* Indices into filter and escaped.  */
230     int filter_i = 0;
231     int escaped_i = 0;
232 
233     for (filter_i = 0; filter_i < strlen (filter); filter_i ++)
234       {
235 	switch (filter[filter_i])
236 	  {
237 	  case '*':
238 	  case '(':
239 	  case ')':
240 	  case '\\':
241 	  case '/':
242 	    snprintf (&escaped[escaped_i], 4, "%%%02x",
243                      ((const unsigned char *)filter)[filter_i]);
244 	    escaped_i += 3;
245 	    break;
246 
247 	  default:
248 	    escaped[escaped_i ++] = filter[filter_i];
249 	    break;
250 	  }
251       }
252     /* NUL terminate it.  */
253     escaped[escaped_i] = 0;
254 
255     /* We could shrink escaped to be just escaped_i bytes, but the
256        result will probably be freed very quickly anyways.  */
257     return escaped;
258   }
259 }
260