I agree that returning Operations Error is the wrong return value. I don't agree that we should *always* return invalidCredentials, however, acting like the other LDAP servers on an invalid entry seems reasonable to me. One option I do see is if we can't create an imsg to the parent process handling a BSDAUTH request, this clearly is an internal error.
Also, you missed the case for ldap_auth_sasl. Diff below should change this, but it's compile tested only. martijn@ On 3/1/20 5:24 PM, Robert Klein wrote: > Hi, > > When trying to bind to ldapd using a dn which has an invalid > userPassword entry, e.eg. a “defective” SHA valus like “{SHA}alpha”, > ldapd currently returns 1 (Operations Error). > > The patch below changes this to 49 (Invalid Credentials). > > There are basically two reasons for this: > > 1. The userPassword is a multi-valued attribute, i.e. there can be more > than one in an ldap entry. Now when you have a valid and a defective > entry and the binding user supplies a password which does not match > the valid entry you get different results whether the defective entry > comes last (return value 1) or not (return value 49). When the > supplied password matches the valid entry the bind succeeds > independent of order. > > A change to return value 49 gets consistent results. > > For a single userPassword entry 49 also is not wrong, as the supplied > password still doesn't match the entry (even if it is defective). > > 2. RFC 4511 defines the result code “Operations Error” as follows: > > “operationsError (1) > > Indicates that the operation is not properly sequenced with > relation to other operations (of same or different type). > > For example, this code is returned if the client attempts to > StartTLS [RFC4346] while there are other uncompleted > operations or if a TLS layer was already installed.” > > A defective password entry in no way is an operations error of the > ldapd server. Also I don't think it is the server's job to take care > of the correctness of password entries. There is no provision in the > LDAP RFCs to stop one from entering an incorrect entry. I checked > this with other directory servers: 389, apache ds, and openldap. All > three accept incorrect entries and all three return 49 (Invalid > Credentials) on bind attempts. > > Best regards > Robert > > Index: auth.c =================================================================== RCS file: /cvs/src/usr.sbin/ldapd/auth.c,v retrieving revision 1.14 diff -u -p -r1.14 auth.c --- auth.c 24 Oct 2019 12:39:26 -0000 1.14 +++ auth.c 3 Mar 2020 19:31:41 -0000 @@ -179,33 +179,34 @@ send_auth_request(struct request *req, c const char *password) { struct auth_req auth_req; + int ret = LDAP_SASL_BIND_IN_PROGRESS; memset(&auth_req, 0, sizeof(auth_req)); if (strlcpy(auth_req.name, username, - sizeof(auth_req.name)) >= sizeof(auth_req.name)) + sizeof(auth_req.name)) >= sizeof(auth_req.name)) { + ret = LDAP_INVALID_CREDENTIALS; goto fail; + } if (strlcpy(auth_req.password, password, - sizeof(auth_req.password)) >= sizeof(auth_req.password)) + sizeof(auth_req.password)) >= sizeof(auth_req.password)) { + ret = LDAP_INVALID_CREDENTIALS; goto fail; + } auth_req.fd = req->conn->fd; auth_req.msgid = req->msgid; if (imsgev_compose(iev_ldapd, IMSG_LDAPD_AUTH, 0, 0, -1, &auth_req, - sizeof(auth_req)) == -1) + sizeof(auth_req)) == -1) { + ret = LDAP_OTHER; goto fail; + } req->conn->bind_req = req; - return 0; - fail: memset(&auth_req, 0, sizeof(auth_req)); - return -1; + return ret; } -/* - * Check password. Returns 1 if password matches, 2 if password matching - * is in progress, 0 on mismatch and -1 on error. - */ static int check_password(struct request *req, const char *stored_passwd, const char *passwd) @@ -218,37 +219,39 @@ check_password(struct request *req, cons int sz; if (stored_passwd == NULL) - return -1; + return LDAP_INVALID_CREDENTIALS; if (strncmp(stored_passwd, "{SHA}", 5) == 0) { sz = b64_pton(stored_passwd + 5, tmp, sizeof(tmp)); if (sz != SHA_DIGEST_LENGTH) - return (-1); + return (LDAP_INVALID_CREDENTIALS); SHA1_Init(&ctx); SHA1_Update(&ctx, passwd, strlen(passwd)); SHA1_Final(md, &ctx); - return (bcmp(md, tmp, SHA_DIGEST_LENGTH) == 0 ? 1 : 0); + return (bcmp(md, tmp, SHA_DIGEST_LENGTH) == 0 ? + LDAP_SUCCESS : LDAP_INVALID_CREDENTIALS); } else if (strncmp(stored_passwd, "{SSHA}", 6) == 0) { sz = b64_pton(stored_passwd + 6, tmp, sizeof(tmp)); if (sz <= SHA_DIGEST_LENGTH) - return (-1); + return (LDAP_INVALID_CREDENTIALS); salt = tmp + SHA_DIGEST_LENGTH; SHA1_Init(&ctx); SHA1_Update(&ctx, passwd, strlen(passwd)); SHA1_Update(&ctx, salt, sz - SHA_DIGEST_LENGTH); SHA1_Final(md, &ctx); - return (bcmp(md, tmp, SHA_DIGEST_LENGTH) == 0 ? 1 : 0); + return (bcmp(md, tmp, SHA_DIGEST_LENGTH) == 0 ? + LDAP_SUCCESS : LDAP_INVALID_CREDENTIALS); } else if (strncmp(stored_passwd, "{CRYPT}", 7) == 0) { encpw = crypt(passwd, stored_passwd + 7); if (encpw == NULL) - return (-1); - return (strcmp(encpw, stored_passwd + 7) == 0 ? 1 : 0); + return (LDAP_INVALID_CREDENTIALS); + return (strcmp(encpw, stored_passwd + 7) == 0 ? + LDAP_SUCCESS : LDAP_INVALID_CREDENTIALS); } else if (strncmp(stored_passwd, "{BSDAUTH}", 9) == 0) { - if (send_auth_request(req, stored_passwd + 9, passwd) == -1) - return (-1); - return 2; /* Operation in progress. */ + return send_auth_request(req, stored_passwd + 9, passwd); } else - return (strcmp(stored_passwd, passwd) == 0 ? 1 : 0); + return (strcmp(stored_passwd, passwd) == 0 ? + LDAP_SUCCESS : LDAP_INVALID_CREDENTIALS); } static int @@ -258,6 +261,7 @@ ldap_auth_sasl(struct request *req, char char *authzid, *authcid, *password; char *creds; size_t len; + int ret; if (ober_scanf_elements(params, "{sx", &method, &creds, &len) != 0) return LDAP_PROTOCOL_ERROR; @@ -283,8 +287,8 @@ ldap_auth_sasl(struct request *req, char log_debug("sasl authorization id = [%s]", authzid); log_debug("sasl authentication id = [%s]", authcid); - if (send_auth_request(req, authcid, password) != 0) - return LDAP_OPERATIONS_ERROR; + if ((ret = send_auth_request(req, authcid, password)) != LDAP_SUCCESS) + return ret; free(req->conn->binddn); req->conn->binddn = NULL; @@ -352,7 +356,8 @@ ldap_auth_simple(struct request *req, ch if (ober_get_string(elm, &user_password) != 0) continue; pwret = check_password(req, user_password, password); - if (pwret >= 1) + if (pwret == LDAP_SUCCESS || + pwret == LDAP_SASL_BIND_IN_PROGRESS) break; } } @@ -361,20 +366,18 @@ ldap_auth_simple(struct request *req, ch free(req->conn->binddn); req->conn->binddn = NULL; - if (pwret == 1) { + if (pwret == LDAP_SUCCESS) { if ((req->conn->binddn = strdup(binddn)) == NULL) return LDAP_OTHER; log_debug("successfully authenticated as %s", req->conn->binddn); return LDAP_SUCCESS; - } else if (pwret == 2) { + } else if (pwret == LDAP_SASL_BIND_IN_PROGRESS) { if ((req->conn->pending_binddn = strdup(binddn)) == NULL) return LDAP_OTHER; return -LDAP_SASL_BIND_IN_PROGRESS; - } else if (pwret == 0) - return LDAP_INVALID_CREDENTIALS; - else - return LDAP_OPERATIONS_ERROR; + } else + return pwret; } void