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

Reply via email to