xref: /openbsd/usr.sbin/ldapd/auth.c (revision 8529ddd3)
1 /*	$OpenBSD: auth.c,v 1.10 2014/09/21 05:33:49 daniel 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 
33 static int
34 aci_matches(struct aci *aci, struct conn *conn, struct namespace *ns,
35     char *dn, int rights, enum scope scope)
36 {
37 	struct btval	 key;
38 
39 	if ((rights & aci->rights) != rights)
40 		return 0;
41 
42 	if (dn == NULL)
43 		return 0;
44 
45 	if (aci->target != NULL) {
46 		key.size = strlen(dn);
47 		key.data = dn;
48 
49 		if (scope == LDAP_SCOPE_BASE) {
50 			switch (aci->scope) {
51 			case LDAP_SCOPE_BASE:
52 				if (strcmp(dn, aci->target) != 0)
53 					return 0;
54 				break;
55 			case LDAP_SCOPE_ONELEVEL:
56 				if (!is_child_of(&key, aci->target))
57 					return 0;
58 				break;
59 			case LDAP_SCOPE_SUBTREE:
60 				if (!has_suffix(&key, aci->target))
61 					return 0;
62 				break;
63 			}
64 		} else if (scope == LDAP_SCOPE_ONELEVEL) {
65 			switch (aci->scope) {
66 			case LDAP_SCOPE_BASE:
67 				return 0;
68 			case LDAP_SCOPE_ONELEVEL:
69 				if (strcmp(dn, aci->target) != 0)
70 					return 0;
71 				break;
72 			case LDAP_SCOPE_SUBTREE:
73 				if (!has_suffix(&key, aci->target))
74 					return 0;
75 				break;
76 			}
77 		} else if (scope == LDAP_SCOPE_SUBTREE) {
78 			switch (aci->scope) {
79 			case LDAP_SCOPE_BASE:
80 			case LDAP_SCOPE_ONELEVEL:
81 				return 0;
82 			case LDAP_SCOPE_SUBTREE:
83 				if (!has_suffix(&key, aci->target))
84 					return 0;
85 				break;
86 			}
87 		}
88 	}
89 
90 	if (aci->subject != NULL) {
91 		if (conn->binddn == NULL)
92 			return 0;
93 		if (strcmp(aci->subject, "@") == 0) {
94 			if (strcmp(dn, conn->binddn) != 0)
95 				return 0;
96 		} else if (strcmp(aci->subject, conn->binddn) != 0)
97 			return 0;
98 	}
99 
100 	return 1;
101 }
102 
103 /* Returns true (1) if conn is authorized for op on dn in namespace.
104  */
105 int
106 authorized(struct conn *conn, struct namespace *ns, int rights, char *dn,
107     int scope)
108 {
109 	struct aci	*aci;
110 	int		 type = ACI_ALLOW;
111 
112 	/* Root DN is always allowed. */
113 	if (conn->binddn != NULL) {
114 		if (conf->rootdn != NULL &&
115 		    strcasecmp(conn->binddn, conf->rootdn) == 0)
116 			return 1;
117 		if (ns != NULL && ns->rootdn != NULL &&
118 		    strcasecmp(conn->binddn, ns->rootdn) == 0)
119 			return 1;
120 	}
121 
122 	/* Default to deny for write access. */
123 	if ((rights & (ACI_WRITE | ACI_CREATE)) != 0)
124 		type = ACI_DENY;
125 
126 	log_debug("requesting %02X access to %s by %s, in namespace %s",
127 	    rights,
128 	    dn ? dn : "any",
129 	    conn->binddn ? conn->binddn : "any",
130 	    ns ? ns->suffix : "global");
131 
132 	SIMPLEQ_FOREACH(aci, &conf->acl, entry) {
133 		if (aci_matches(aci, conn, ns, dn, rights, scope)) {
134 			type = aci->type;
135 			log_debug("%s by: %s %02X access to %s by %s",
136 			    type == ACI_ALLOW ? "allowed" : "denied",
137 			    aci->type == ACI_ALLOW ? "allow" : "deny",
138 			    aci->rights,
139 			    aci->target ? aci->target : "any",
140 			    aci->subject ? aci->subject : "any");
141 		}
142 	}
143 
144 	if (ns != NULL) {
145 		SIMPLEQ_FOREACH(aci, &ns->acl, entry) {
146 			if (aci_matches(aci, conn, ns, dn, rights, scope)) {
147 				type = aci->type;
148 				log_debug("%s by: %s %02X access to %s by %s",
149 				    type == ACI_ALLOW ? "allowed" : "denied",
150 				    aci->type == ACI_ALLOW ? "allow" : "deny",
151 				    aci->rights,
152 				    aci->target ? aci->target : "any",
153 				    aci->subject ? aci->subject : "any");
154 			}
155 		}
156 	}
157 
158 	return type == ACI_ALLOW ? 1 : 0;
159 }
160 
161 static int
162 send_auth_request(struct request *req, const char *username,
163     const char *password)
164 {
165 	struct auth_req	 auth_req;
166 
167 	bzero(&auth_req, sizeof(auth_req));
168 	if (strlcpy(auth_req.name, username,
169 	    sizeof(auth_req.name)) >= sizeof(auth_req.name))
170 		goto fail;
171 	if (strlcpy(auth_req.password, password,
172 	    sizeof(auth_req.password)) >= sizeof(auth_req.password))
173 		goto fail;
174 	auth_req.fd = req->conn->fd;
175 	auth_req.msgid = req->msgid;
176 
177 	if (imsgev_compose(iev_ldapd, IMSG_LDAPD_AUTH, 0, 0, -1, &auth_req,
178 	    sizeof(auth_req)) == -1)
179 		goto fail;
180 
181 	req->conn->bind_req = req;
182 	return 0;
183 
184 fail:
185 	bzero(&auth_req, sizeof(auth_req));
186 	return -1;
187 }
188 
189 /*
190  * Check password. Returns 1 if password matches, 2 if password matching
191  * is in progress, 0 on mismatch and -1 on error.
192  */
193 static int
194 check_password(struct request *req, const char *stored_passwd,
195     const char *passwd)
196 {
197 	unsigned char	 tmp[128];
198 	unsigned char	 md[SHA_DIGEST_LENGTH];
199 	char		*encpw;
200 	unsigned char	*salt;
201 	SHA_CTX		 ctx;
202 	int		 sz;
203 
204 	if (stored_passwd == NULL)
205 		return -1;
206 
207 	if (strncmp(stored_passwd, "{SHA}", 5) == 0) {
208 		sz = b64_pton(stored_passwd + 5, tmp, sizeof(tmp));
209 		if (sz != SHA_DIGEST_LENGTH)
210 			return (-1);
211 		SHA1_Init(&ctx);
212 		SHA1_Update(&ctx, passwd, strlen(passwd));
213 		SHA1_Final(md, &ctx);
214 		return (bcmp(md, tmp, SHA_DIGEST_LENGTH) == 0 ? 1 : 0);
215 	} else if (strncmp(stored_passwd, "{SSHA}", 6) == 0) {
216 		sz = b64_pton(stored_passwd + 6, tmp, sizeof(tmp));
217 		if (sz <= SHA_DIGEST_LENGTH)
218 			return (-1);
219 		salt = tmp + SHA_DIGEST_LENGTH;
220 		SHA1_Init(&ctx);
221 		SHA1_Update(&ctx, passwd, strlen(passwd));
222 		SHA1_Update(&ctx, salt, sz - SHA_DIGEST_LENGTH);
223 		SHA1_Final(md, &ctx);
224 		return (bcmp(md, tmp, SHA_DIGEST_LENGTH) == 0 ? 1 : 0);
225 	} else if (strncmp(stored_passwd, "{CRYPT}", 7) == 0) {
226 		encpw = crypt(passwd, stored_passwd + 7);
227 		if (encpw == NULL)
228 			return (-1);
229 		return (strcmp(encpw, stored_passwd + 7) == 0 ? 1 : 0);
230 	} else if (strncmp(stored_passwd, "{BSDAUTH}", 9) == 0) {
231 		if (send_auth_request(req, stored_passwd + 9, passwd) == -1)
232 			return (-1);
233 		return 2;	/* Operation in progress. */
234 	} else
235 		return (strcmp(stored_passwd, passwd) == 0 ? 1 : 0);
236 }
237 
238 static int
239 ldap_auth_sasl(struct request *req, char *binddn, struct ber_element *params)
240 {
241 	char			*method;
242 	char			*authzid, *authcid, *password;
243 	char			*creds;
244 	size_t			 len;
245 
246 	if (ber_scanf_elements(params, "{sx", &method, &creds, &len) != 0)
247 		return LDAP_PROTOCOL_ERROR;
248 
249 	if (strcmp(method, "PLAIN") != 0)
250 		return LDAP_STRONG_AUTH_NOT_SUPPORTED;
251 
252 	if ((req->conn->s_flags & F_SECURE) == 0) {
253 		log_info("refusing plain sasl bind on insecure connection");
254 		return LDAP_CONFIDENTIALITY_REQUIRED;
255 	}
256 
257 	authzid = creds;
258 	authcid = memchr(creds, '\0', len);
259 	if (authcid == NULL || authcid + 2 >= creds + len)
260 		return LDAP_PROTOCOL_ERROR;
261 	authcid++;
262 	password = memchr(authcid, '\0', len - (authcid - creds));
263 	if (password == NULL || password + 2 >= creds + len)
264 		return LDAP_PROTOCOL_ERROR;
265 	password++;
266 
267 	log_debug("sasl authorization id = [%s]", authzid);
268 	log_debug("sasl authentication id = [%s]", authcid);
269 
270 	if (send_auth_request(req, authcid, password) != 0)
271 		return LDAP_OPERATIONS_ERROR;
272 
273 	free(req->conn->binddn);
274 	req->conn->binddn = NULL;
275 	if ((req->conn->pending_binddn = strdup(authcid)) == NULL)
276 		return LDAP_OTHER;
277 
278 	return LDAP_SUCCESS;
279 }
280 
281 static int
282 ldap_auth_simple(struct request *req, char *binddn, struct ber_element *auth)
283 {
284 	int			 pwret = 0;
285 	char			*password;
286 	char			*user_password;
287 	struct namespace	*ns;
288 	struct ber_element	*elm = NULL, *pw = NULL;
289 
290 	if (*binddn == '\0') {
291 		free(req->conn->binddn);		/* anonymous bind */
292 		req->conn->binddn = NULL;
293 		log_debug("anonymous bind");
294 		return LDAP_SUCCESS;
295 	}
296 
297 	if ((req->conn->s_flags & F_SECURE) == 0) {
298 		log_info("refusing non-anonymous bind on insecure connection");
299 		return LDAP_CONFIDENTIALITY_REQUIRED;
300 	}
301 
302 	if (ber_scanf_elements(auth, "s", &password) != 0)
303 		return LDAP_PROTOCOL_ERROR;
304 
305 	if (*password == '\0') {
306 		/* Unauthenticated Authentication Mechanism of Simple Bind */
307 		log_debug("refusing unauthenticated bind");
308 		return LDAP_UNWILLING_TO_PERFORM;
309 	}
310 
311 	if (conf->rootdn != NULL && strcmp(conf->rootdn, binddn) == 0) {
312 		pwret = check_password(req, conf->rootpw, password);
313 	} else if ((ns = namespace_lookup_base(binddn, 1)) == NULL) {
314 		return LDAP_INVALID_CREDENTIALS;
315 	} else if (ns->rootdn != NULL && strcmp(ns->rootdn, binddn) == 0) {
316 		pwret = check_password(req, ns->rootpw, password);
317 	} else if (namespace_has_referrals(ns)) {
318 		return LDAP_INVALID_CREDENTIALS;
319 	} else {
320 		if (!authorized(req->conn, ns, ACI_BIND, binddn,
321 		    LDAP_SCOPE_BASE))
322 			return LDAP_INSUFFICIENT_ACCESS;
323 
324 		elm = namespace_get(ns, binddn);
325 		if (elm == NULL && errno == ESTALE) {
326 			if (namespace_queue_request(ns, req) != 0)
327 				return LDAP_BUSY;
328 			return -1;	/* Database is being reopened. */
329 		}
330 
331 		if (elm != NULL)
332 			pw = ldap_get_attribute(elm, "userPassword");
333 		if (pw != NULL) {
334 			for (elm = pw->be_next->be_sub; elm;
335 			    elm = elm->be_next) {
336 				if (ber_get_string(elm, &user_password) != 0)
337 					continue;
338 				pwret = check_password(req, user_password, password);
339 				if (pwret >= 1)
340 					break;
341 			}
342 		}
343 	}
344 
345 	free(req->conn->binddn);
346 	req->conn->binddn = NULL;
347 
348 	if (pwret == 1) {
349 		if ((req->conn->binddn = strdup(binddn)) == NULL)
350 			return LDAP_OTHER;
351 		log_debug("successfully authenticated as %s",
352 		    req->conn->binddn);
353 		return LDAP_SUCCESS;
354 	} else if (pwret == 2) {
355 		if ((req->conn->pending_binddn = strdup(binddn)) == NULL)
356 			return LDAP_OTHER;
357 		return -LDAP_SASL_BIND_IN_PROGRESS;
358 	} else if (pwret == 0)
359 		return LDAP_INVALID_CREDENTIALS;
360 	else
361 		return LDAP_OPERATIONS_ERROR;
362 }
363 
364 void
365 ldap_bind_continue(struct conn *conn, int ok)
366 {
367 	int			 rc;
368 
369 	if (ok) {
370 		rc = LDAP_SUCCESS;
371 		conn->binddn = conn->pending_binddn;
372 		log_debug("successfully authenticated as %s", conn->binddn);
373 	} else {
374 		rc = LDAP_INVALID_CREDENTIALS;
375 		free(conn->pending_binddn);
376 	}
377 	conn->pending_binddn = NULL;
378 
379 	ldap_respond(conn->bind_req, rc);
380 	conn->bind_req = NULL;
381 }
382 
383 int
384 ldap_bind(struct request *req)
385 {
386 	int			 rc = LDAP_OTHER;
387 	long long		 ver;
388 	char			*binddn;
389 	struct ber_element	*auth;
390 
391 	++stats.req_bind;
392 
393 	if (ber_scanf_elements(req->op, "{ise", &ver, &binddn, &auth) != 0) {
394 		rc = LDAP_PROTOCOL_ERROR;
395 		goto done;
396 	}
397 
398 	if (req->conn->bind_req) {
399 		log_debug("aborting bind in progress with msgid %lld",
400 		    req->conn->bind_req->msgid);
401 		request_free(req->conn->bind_req);
402 		req->conn->bind_req = NULL;
403 	}
404 
405 	normalize_dn(binddn);
406 	log_debug("bind dn = %s", binddn);
407 
408 	switch (auth->be_type) {
409 	case LDAP_AUTH_SIMPLE:
410 		if ((rc = ldap_auth_simple(req, binddn, auth)) < 0)
411 			return rc;
412 		break;
413 	case LDAP_AUTH_SASL:
414 		if ((rc = ldap_auth_sasl(req, binddn, auth)) == LDAP_SUCCESS)
415 			return LDAP_SUCCESS;
416 		break;
417 	default:
418 		rc = LDAP_STRONG_AUTH_NOT_SUPPORTED;
419 		break;
420 	}
421 
422 done:
423 	return ldap_respond(req, rc);
424 }
425 
426