1 /* $OpenBSD: auth.c,v 1.14 2019/10/24 12:39:26 tb 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 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 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 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 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 (strncmp(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 (strncmp(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 (strncmp(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 (strncmp(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 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 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 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 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