Line data Source code
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 "util.h"
31 : #include "http.h"
32 :
33 : /* Returns 1 if the string is an LDAP URL (begins with ldap:, ldaps:
34 : or ldapi:). */
35 : int
36 20 : ldap_uri_p (const char *url)
37 : {
38 20 : char *colon = strchr (url, ':');
39 20 : if (! colon)
40 8 : return 0;
41 : else
42 : {
43 12 : int offset = (uintptr_t) colon - (uintptr_t) url;
44 :
45 12 : if (/* All lower case. */
46 6 : (offset == 4 && memcmp (url, "ldap", 4) == 0)
47 9 : || (offset == 5
48 6 : && (memcmp (url, "ldaps", 5) == 0
49 3 : && memcmp (url, "ldapi", 5) == 0))
50 : /* Mixed case. */
51 9 : || ((url[0] == 'l' || url[0] == 'L')
52 9 : && (url[1] == 'd' || url[1] == 'D')
53 9 : && (url[2] == 'a' || url[2] == 'A')
54 9 : && (url[3] == 'p' || url[3] == 'P')
55 9 : && (url[4] == ':'
56 6 : || ((url[4] == 's' || url[4] == 'S'
57 3 : || url[4] == 'i' || url[4] == 'i')
58 6 : && url[5] == ':'))))
59 12 : return 1;
60 0 : return 0;
61 : }
62 : }
63 :
64 : /* Parse a URI and put the result into *purip. On success the
65 : caller must use http_release_parsed_uri() to releases the resources.
66 :
67 : uri->path is the base DN (or NULL for the default).
68 : uri->auth is the bindname (or NULL for none).
69 : The uri->query variable "password" is the password.
70 :
71 : Note: any specified scope, any attributes, any filter and any
72 : unknown extensions are simply ignored. */
73 : gpg_error_t
74 6 : ldap_parse_uri (parsed_uri_t *purip, const char *uri)
75 : {
76 6 : gpg_err_code_t err = 0;
77 6 : parsed_uri_t puri = NULL;
78 :
79 : int result;
80 6 : LDAPURLDesc *lud = NULL;
81 :
82 6 : char *scheme = NULL;
83 6 : char *host = NULL;
84 6 : char *dn = NULL;
85 6 : char *bindname = NULL;
86 6 : char *password = NULL;
87 :
88 : char **s;
89 :
90 : char *buffer;
91 : int len;
92 :
93 6 : result = ldap_url_parse (uri, &lud);
94 6 : if (result != 0)
95 : {
96 0 : log_error ("Unable to parse LDAP uri '%s'\n", uri);
97 0 : err = GPG_ERR_GENERAL;
98 0 : goto out;
99 : }
100 :
101 6 : scheme = lud->lud_scheme;
102 6 : host = lud->lud_host;
103 6 : dn = lud->lud_dn;
104 :
105 8 : for (s = lud->lud_exts; s && *s; s ++)
106 : {
107 2 : if (strncmp (*s, "bindname=", 9) == 0)
108 : {
109 1 : if (bindname)
110 0 : log_error ("bindname given multiple times in URL '%s', ignoring.\n",
111 : uri);
112 : else
113 1 : bindname = *s + 9;
114 : }
115 1 : else if (strncmp (*s, "password=", 9) == 0)
116 : {
117 1 : if (password)
118 0 : log_error ("password given multiple times in URL '%s', ignoring.\n",
119 : uri);
120 : else
121 1 : password = *s + 9;
122 : }
123 : else
124 0 : log_error ("Unhandled extension (%s) in URL '%s', ignoring.",
125 : *s, uri);
126 : }
127 :
128 6 : len = 0;
129 :
130 : #define add(s) do { if (s) len += strlen (s) + 1; } while (0)
131 :
132 6 : add (scheme);
133 6 : add (host);
134 6 : add (dn);
135 6 : add (bindname);
136 6 : add (password);
137 :
138 6 : puri = xtrycalloc (1, sizeof *puri + len);
139 6 : if (! puri)
140 : {
141 0 : err = gpg_err_code_from_syserror ();
142 0 : goto out;
143 : }
144 :
145 6 : buffer = puri->buffer;
146 :
147 : #define copy(to, s) \
148 : do \
149 : { \
150 : if (s) \
151 : { \
152 : to = buffer; \
153 : buffer = stpcpy (buffer, s) + 1; \
154 : } \
155 : } \
156 : while (0)
157 :
158 6 : copy (puri->scheme, scheme);
159 : /* Make sure the scheme is lower case. */
160 6 : ascii_strlwr (puri->scheme);
161 :
162 6 : copy (puri->host, host);
163 6 : copy (puri->path, dn);
164 6 : copy (puri->auth, bindname);
165 :
166 6 : if (password)
167 : {
168 1 : puri->query = calloc (sizeof (*puri->query), 1);
169 1 : if (!puri->query)
170 : {
171 0 : err = gpg_err_code_from_syserror ();
172 0 : goto out;
173 : }
174 1 : puri->query->name = "password";
175 1 : copy (puri->query->value, password);
176 1 : puri->query->valuelen = strlen (password) + 1;
177 : }
178 :
179 6 : puri->use_tls = strcmp (puri->scheme, "ldaps") == 0;
180 6 : puri->port = lud->lud_port;
181 :
182 : out:
183 6 : if (lud)
184 6 : ldap_free_urldesc (lud);
185 :
186 6 : if (err)
187 : {
188 0 : if (puri)
189 0 : http_release_parsed_uri (puri);
190 : }
191 : else
192 6 : *purip = puri;
193 :
194 6 : return gpg_err_make (default_errsource, err);
195 : }
196 :
197 : /* The following characters need to be escaped to be part of an LDAP
198 : filter: *, (, ), \, NUL and /. Note: we don't handle NUL, since a
199 : NUL can't be part of a C string.
200 :
201 : This function always allocates a new string on success. It is the
202 : caller's responsibility to free it.
203 : */
204 : char *
205 4 : ldap_escape_filter (const char *filter)
206 : {
207 4 : int l = strcspn (filter, "*()\\/");
208 4 : if (l == strlen (filter))
209 : /* Nothing to escape. */
210 2 : return xstrdup (filter);
211 :
212 : {
213 : /* In the worst case we need to escape every letter. */
214 2 : char *escaped = xmalloc (1 + 3 * strlen (filter));
215 :
216 : /* Indices into filter and escaped. */
217 2 : int filter_i = 0;
218 2 : int escaped_i = 0;
219 :
220 16 : for (filter_i = 0; filter_i < strlen (filter); filter_i ++)
221 : {
222 14 : switch (filter[filter_i])
223 : {
224 : case '*':
225 : case '(':
226 : case ')':
227 : case '\\':
228 : case '/':
229 7 : snprintf (&escaped[escaped_i], 4, "%%%02x",
230 7 : ((const unsigned char *)filter)[filter_i]);
231 7 : escaped_i += 3;
232 7 : break;
233 :
234 : default:
235 7 : escaped[escaped_i ++] = filter[filter_i];
236 7 : break;
237 : }
238 : }
239 : /* NUL terminate it. */
240 2 : escaped[escaped_i] = 0;
241 :
242 : /* We could shrink escaped to be just escaped_i bytes, but the
243 : result will probably be freed very quickly anyways. */
244 2 : return escaped;
245 : }
246 : }
|