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