1 /* $OpenBSD: auth.c,v 1.15 2022/06/29 09:10:13 martijn Exp $ */
2
3 /*
4 * Copyright (c) 2009, 2010 Martin Hedenfalk <martin@bzero.se>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/types.h>
20 #include <sys/queue.h>
21 #include <netinet/in.h>
22
23 #include <errno.h>
24 #include <openssl/sha.h>
25 #include <pwd.h>
26 #include <resolv.h> /* for b64_pton */
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30
31 #include "ldapd.h"
32 #include "log.h"
33
34 static int
aci_matches(struct aci * aci,struct conn * conn,struct namespace * ns,char * dn,int rights,char * attr,enum scope scope)35 aci_matches(struct aci *aci, struct conn *conn, struct namespace *ns,
36 char *dn, int rights, char *attr, enum scope scope)
37 {
38 struct btval key;
39
40 if ((rights & aci->rights) != rights)
41 return 0;
42
43 if (dn == NULL)
44 return 0;
45
46 if (aci->target != NULL) {
47 key.size = strlen(dn);
48 key.data = dn;
49
50 if (scope == LDAP_SCOPE_BASE) {
51 switch (aci->scope) {
52 case LDAP_SCOPE_BASE:
53 if (strcmp(dn, aci->target) != 0)
54 return 0;
55 break;
56 case LDAP_SCOPE_ONELEVEL:
57 if (!is_child_of(&key, aci->target))
58 return 0;
59 break;
60 case LDAP_SCOPE_SUBTREE:
61 if (!has_suffix(&key, aci->target))
62 return 0;
63 break;
64 }
65 } else if (scope == LDAP_SCOPE_ONELEVEL) {
66 switch (aci->scope) {
67 case LDAP_SCOPE_BASE:
68 return 0;
69 case LDAP_SCOPE_ONELEVEL:
70 if (strcmp(dn, aci->target) != 0)
71 return 0;
72 break;
73 case LDAP_SCOPE_SUBTREE:
74 if (!has_suffix(&key, aci->target))
75 return 0;
76 break;
77 }
78 } else if (scope == LDAP_SCOPE_SUBTREE) {
79 switch (aci->scope) {
80 case LDAP_SCOPE_BASE:
81 case LDAP_SCOPE_ONELEVEL:
82 return 0;
83 case LDAP_SCOPE_SUBTREE:
84 if (!has_suffix(&key, aci->target))
85 return 0;
86 break;
87 }
88 }
89 }
90
91 if (aci->subject != NULL) {
92 if (conn->binddn == NULL)
93 return 0;
94 if (strcmp(aci->subject, "@") == 0) {
95 if (strcmp(dn, conn->binddn) != 0)
96 return 0;
97 } else if (strcmp(aci->subject, conn->binddn) != 0)
98 return 0;
99 }
100
101 if (aci->attribute != NULL) {
102 if (attr == NULL)
103 return 0;
104 if (strcasecmp(aci->attribute, attr) != 0)
105 return 0;
106 }
107
108 return 1;
109 }
110
111 /* Returns true (1) if conn is authorized for op on dn in namespace.
112 */
113 int
authorized(struct conn * conn,struct namespace * ns,int rights,char * dn,char * attr,int scope)114 authorized(struct conn *conn, struct namespace *ns, int rights, char *dn,
115 char *attr, int scope)
116 {
117 struct aci *aci;
118 int type = ACI_ALLOW;
119
120 /* Root DN is always allowed. */
121 if (conn->binddn != NULL) {
122 if (conf->rootdn != NULL &&
123 strcasecmp(conn->binddn, conf->rootdn) == 0)
124 return 1;
125 if (ns != NULL && ns->rootdn != NULL &&
126 strcasecmp(conn->binddn, ns->rootdn) == 0)
127 return 1;
128 }
129
130 /* Default to deny for write access. */
131 if ((rights & (ACI_WRITE | ACI_CREATE)) != 0)
132 type = ACI_DENY;
133
134 log_debug("requesting %02X access to %s%s%s by %s, in namespace %s",
135 rights,
136 dn ? dn : "any",
137 attr ? " attribute " : "",
138 attr ? attr : "",
139 conn->binddn ? conn->binddn : "any",
140 ns ? ns->suffix : "global");
141
142 SIMPLEQ_FOREACH(aci, &conf->acl, entry) {
143 if (aci_matches(aci, conn, ns, dn, rights,
144 attr, scope)) {
145 type = aci->type;
146 log_debug("%s by: %s %02X access to %s%s%s by %s",
147 type == ACI_ALLOW ? "allowed" : "denied",
148 aci->type == ACI_ALLOW ? "allow" : "deny",
149 aci->rights,
150 aci->target ? aci->target : "any",
151 aci->attribute ? " attribute " : "",
152 aci->attribute ? aci->attribute : "",
153 aci->subject ? aci->subject : "any");
154 }
155 }
156
157 if (ns != NULL) {
158 SIMPLEQ_FOREACH(aci, &ns->acl, entry) {
159 if (aci_matches(aci, conn, ns, dn, rights,
160 attr, scope)) {
161 type = aci->type;
162 log_debug("%s by: %s %02X access to %s%s%s by %s",
163 type == ACI_ALLOW ? "allowed" : "denied",
164 aci->type == ACI_ALLOW ? "allow" : "deny",
165 aci->rights,
166 aci->target ? aci->target : "any",
167 aci->attribute ? " attribute " : "",
168 aci->attribute ? aci->attribute : "",
169 aci->subject ? aci->subject : "any");
170 }
171 }
172 }
173
174 return type == ACI_ALLOW ? 1 : 0;
175 }
176
177 static int
send_auth_request(struct request * req,const char * username,const char * password)178 send_auth_request(struct request *req, const char *username,
179 const char *password)
180 {
181 struct auth_req auth_req;
182
183 memset(&auth_req, 0, sizeof(auth_req));
184 if (strlcpy(auth_req.name, username,
185 sizeof(auth_req.name)) >= sizeof(auth_req.name))
186 goto fail;
187 if (strlcpy(auth_req.password, password,
188 sizeof(auth_req.password)) >= sizeof(auth_req.password))
189 goto fail;
190 auth_req.fd = req->conn->fd;
191 auth_req.msgid = req->msgid;
192
193 if (imsgev_compose(iev_ldapd, IMSG_LDAPD_AUTH, 0, 0, -1, &auth_req,
194 sizeof(auth_req)) == -1)
195 goto fail;
196
197 req->conn->bind_req = req;
198 return 0;
199
200 fail:
201 memset(&auth_req, 0, sizeof(auth_req));
202 return -1;
203 }
204
205 /*
206 * Check password. Returns 1 if password matches, 2 if password matching
207 * is in progress, 0 on mismatch and -1 on error.
208 */
209 static int
check_password(struct request * req,const char * stored_passwd,const char * passwd)210 check_password(struct request *req, const char *stored_passwd,
211 const char *passwd)
212 {
213 unsigned char tmp[128];
214 unsigned char md[SHA_DIGEST_LENGTH];
215 char *encpw;
216 unsigned char *salt;
217 SHA_CTX ctx;
218 int sz;
219
220 if (stored_passwd == NULL)
221 return -1;
222
223 if (strncasecmp(stored_passwd, "{SHA}", 5) == 0) {
224 sz = b64_pton(stored_passwd + 5, tmp, sizeof(tmp));
225 if (sz != SHA_DIGEST_LENGTH)
226 return (-1);
227 SHA1_Init(&ctx);
228 SHA1_Update(&ctx, passwd, strlen(passwd));
229 SHA1_Final(md, &ctx);
230 return (bcmp(md, tmp, SHA_DIGEST_LENGTH) == 0 ? 1 : 0);
231 } else if (strncasecmp(stored_passwd, "{SSHA}", 6) == 0) {
232 sz = b64_pton(stored_passwd + 6, tmp, sizeof(tmp));
233 if (sz <= SHA_DIGEST_LENGTH)
234 return (-1);
235 salt = tmp + SHA_DIGEST_LENGTH;
236 SHA1_Init(&ctx);
237 SHA1_Update(&ctx, passwd, strlen(passwd));
238 SHA1_Update(&ctx, salt, sz - SHA_DIGEST_LENGTH);
239 SHA1_Final(md, &ctx);
240 return (bcmp(md, tmp, SHA_DIGEST_LENGTH) == 0 ? 1 : 0);
241 } else if (strncasecmp(stored_passwd, "{CRYPT}", 7) == 0) {
242 encpw = crypt(passwd, stored_passwd + 7);
243 if (encpw == NULL)
244 return (-1);
245 return (strcmp(encpw, stored_passwd + 7) == 0 ? 1 : 0);
246 } else if (strncasecmp(stored_passwd, "{BSDAUTH}", 9) == 0) {
247 if (send_auth_request(req, stored_passwd + 9, passwd) == -1)
248 return (-1);
249 return 2; /* Operation in progress. */
250 } else
251 return (strcmp(stored_passwd, passwd) == 0 ? 1 : 0);
252 }
253
254 static int
ldap_auth_sasl(struct request * req,char * binddn,struct ber_element * params)255 ldap_auth_sasl(struct request *req, char *binddn, struct ber_element *params)
256 {
257 char *method;
258 char *authzid, *authcid, *password;
259 char *creds;
260 size_t len;
261
262 if (ober_scanf_elements(params, "{sx", &method, &creds, &len) != 0)
263 return LDAP_PROTOCOL_ERROR;
264
265 if (strcmp(method, "PLAIN") != 0)
266 return LDAP_STRONG_AUTH_NOT_SUPPORTED;
267
268 if ((req->conn->s_flags & F_SECURE) == 0) {
269 log_info("refusing plain sasl bind on insecure connection");
270 return LDAP_CONFIDENTIALITY_REQUIRED;
271 }
272
273 authzid = creds;
274 authcid = memchr(creds, '\0', len);
275 if (authcid == NULL || authcid + 2 >= creds + len)
276 return LDAP_PROTOCOL_ERROR;
277 authcid++;
278 password = memchr(authcid, '\0', len - (authcid - creds));
279 if (password == NULL || password + 2 >= creds + len)
280 return LDAP_PROTOCOL_ERROR;
281 password++;
282
283 log_debug("sasl authorization id = [%s]", authzid);
284 log_debug("sasl authentication id = [%s]", authcid);
285
286 if (send_auth_request(req, authcid, password) != 0)
287 return LDAP_OPERATIONS_ERROR;
288
289 free(req->conn->binddn);
290 req->conn->binddn = NULL;
291 if ((req->conn->pending_binddn = strdup(authcid)) == NULL)
292 return LDAP_OTHER;
293
294 return LDAP_SUCCESS;
295 }
296
297 static int
ldap_auth_simple(struct request * req,char * binddn,struct ber_element * auth)298 ldap_auth_simple(struct request *req, char *binddn, struct ber_element *auth)
299 {
300 int pwret = 0;
301 char *password;
302 char *user_password;
303 struct namespace *ns;
304 struct ber_element *elm = NULL, *pw = NULL;
305
306 if (*binddn == '\0') {
307 free(req->conn->binddn); /* anonymous bind */
308 req->conn->binddn = NULL;
309 log_debug("anonymous bind");
310 return LDAP_SUCCESS;
311 }
312
313 if ((req->conn->s_flags & F_SECURE) == 0) {
314 log_info("refusing non-anonymous bind on insecure connection");
315 return LDAP_CONFIDENTIALITY_REQUIRED;
316 }
317
318 if (ober_scanf_elements(auth, "s", &password) != 0)
319 return LDAP_PROTOCOL_ERROR;
320
321 if (*password == '\0') {
322 /* Unauthenticated Authentication Mechanism of Simple Bind */
323 log_debug("refusing unauthenticated bind");
324 return LDAP_UNWILLING_TO_PERFORM;
325 }
326
327 if (conf->rootdn != NULL && strcmp(conf->rootdn, binddn) == 0) {
328 pwret = check_password(req, conf->rootpw, password);
329 } else if ((ns = namespace_lookup_base(binddn, 1)) == NULL) {
330 return LDAP_INVALID_CREDENTIALS;
331 } else if (ns->rootdn != NULL && strcmp(ns->rootdn, binddn) == 0) {
332 pwret = check_password(req, ns->rootpw, password);
333 } else if (namespace_has_referrals(ns)) {
334 return LDAP_INVALID_CREDENTIALS;
335 } else {
336 if (!authorized(req->conn, ns, ACI_BIND, binddn,
337 NULL, LDAP_SCOPE_BASE))
338 return LDAP_INSUFFICIENT_ACCESS;
339
340 elm = namespace_get(ns, binddn);
341 if (elm == NULL && errno == ESTALE) {
342 if (namespace_queue_request(ns, req) != 0)
343 return LDAP_BUSY;
344 return -1; /* Database is being reopened. */
345 }
346
347 if (elm != NULL)
348 pw = ldap_get_attribute(elm, "userPassword");
349 if (pw != NULL) {
350 for (elm = pw->be_next->be_sub; elm;
351 elm = elm->be_next) {
352 if (ober_get_string(elm, &user_password) != 0)
353 continue;
354 pwret = check_password(req, user_password, password);
355 if (pwret >= 1)
356 break;
357 }
358 }
359 }
360
361 free(req->conn->binddn);
362 req->conn->binddn = NULL;
363
364 if (pwret == 1) {
365 if ((req->conn->binddn = strdup(binddn)) == NULL)
366 return LDAP_OTHER;
367 log_debug("successfully authenticated as %s",
368 req->conn->binddn);
369 return LDAP_SUCCESS;
370 } else if (pwret == 2) {
371 if ((req->conn->pending_binddn = strdup(binddn)) == NULL)
372 return LDAP_OTHER;
373 return -LDAP_SASL_BIND_IN_PROGRESS;
374 } else if (pwret == 0)
375 return LDAP_INVALID_CREDENTIALS;
376 else
377 return LDAP_OPERATIONS_ERROR;
378 }
379
380 void
ldap_bind_continue(struct conn * conn,int ok)381 ldap_bind_continue(struct conn *conn, int ok)
382 {
383 int rc;
384
385 if (ok) {
386 rc = LDAP_SUCCESS;
387 conn->binddn = conn->pending_binddn;
388 log_debug("successfully authenticated as %s", conn->binddn);
389 } else {
390 rc = LDAP_INVALID_CREDENTIALS;
391 free(conn->pending_binddn);
392 }
393 conn->pending_binddn = NULL;
394
395 ldap_respond(conn->bind_req, rc);
396 conn->bind_req = NULL;
397 }
398
399 int
ldap_bind(struct request * req)400 ldap_bind(struct request *req)
401 {
402 int rc = LDAP_OTHER;
403 long long ver;
404 char *binddn;
405 struct ber_element *auth;
406
407 ++stats.req_bind;
408
409 if (ober_scanf_elements(req->op, "{ise", &ver, &binddn, &auth) != 0) {
410 rc = LDAP_PROTOCOL_ERROR;
411 goto done;
412 }
413
414 if (req->conn->bind_req) {
415 log_debug("aborting bind in progress with msgid %lld",
416 req->conn->bind_req->msgid);
417 request_free(req->conn->bind_req);
418 req->conn->bind_req = NULL;
419 }
420
421 normalize_dn(binddn);
422 log_debug("bind dn = %s", binddn);
423
424 switch (auth->be_type) {
425 case LDAP_AUTH_SIMPLE:
426 if ((rc = ldap_auth_simple(req, binddn, auth)) < 0)
427 return rc;
428 break;
429 case LDAP_AUTH_SASL:
430 if ((rc = ldap_auth_sasl(req, binddn, auth)) == LDAP_SUCCESS)
431 return LDAP_SUCCESS;
432 break;
433 default:
434 rc = LDAP_STRONG_AUTH_NOT_SUPPORTED;
435 break;
436 }
437
438 done:
439 return ldap_respond(req, rc);
440 }
441
442