1 /* $OpenBSD: auth.c,v 1.11 2015/12/24 17:47:57 mmcc 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 memset(&auth_req, 0, 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 memset(&auth_req, 0, 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