Blame SOURCES/0088-Ticket-bz1525628-1.3.6-backport-invalid-password-mig.patch

6f51e1
From 4219c54d9706f5597e8186d4f983b30587c2762e Mon Sep 17 00:00:00 2001
6f51e1
From: Mark Reynolds <mreynolds@redhat.com>
6f51e1
Date: Tue, 13 Feb 2018 10:32:06 -0500
6f51e1
Subject: [PATCH] Ticket bz1525628 1.3.6 backport - invalid password migration 
6f51e1
 causes unauth bind
6f51e1
6f51e1
Bug Description:  Slapi_ct_memcmp expects both inputs to be
6f51e1
at LEAST size n. If they are not, we only compared UP to n.
6f51e1
6f51e1
Invalid migrations of passwords (IE {CRYPT}XX) would create
6f51e1
a pw which is just salt and no hash. ct_memcmp would then
6f51e1
only verify the salt bits and would allow the authentication.
6f51e1
6f51e1
This relies on an administrative mistake both of allowing
6f51e1
password migration (nsslapd-allow-hashed-passwords) and then
6f51e1
subsequently migrating an INVALID password to the server.
6f51e1
6f51e1
Fix Description:  slapi_ct_memcmp now access n1, n2 size
6f51e1
and will FAIL if they are not the same, but will still compare
6f51e1
n bytes, where n is the "longest" memory, to the first byte
6f51e1
of the other to prevent length disclosure of the shorter
6f51e1
value (generally the mis-migrated password)
6f51e1
6f51e1
https://bugzilla.redhat.com/show_bug.cgi?id=1525628
6f51e1
6f51e1
Author: wibrown
6f51e1
---
6f51e1
 ldap/servers/plugins/pwdstorage/clear_pwd.c |  4 +-
6f51e1
 ldap/servers/plugins/pwdstorage/crypt_pwd.c |  4 +-
6f51e1
 ldap/servers/plugins/pwdstorage/md5_pwd.c   | 14 +++----
6f51e1
 ldap/servers/plugins/pwdstorage/sha_pwd.c   | 18 +++++++--
6f51e1
 ldap/servers/plugins/pwdstorage/smd5_pwd.c  | 60 +++++++++++++++--------------
6f51e1
 ldap/servers/slapd/ch_malloc.c              | 37 +++++++++++++++---
6f51e1
 ldap/servers/slapd/slapi-plugin.h           |  2 +-
6f51e1
 7 files changed, 88 insertions(+), 51 deletions(-)
6f51e1
6f51e1
diff --git a/ldap/servers/plugins/pwdstorage/clear_pwd.c b/ldap/servers/plugins/pwdstorage/clear_pwd.c
6f51e1
index b9b362d34..050e60dd7 100644
6f51e1
--- a/ldap/servers/plugins/pwdstorage/clear_pwd.c
6f51e1
+++ b/ldap/servers/plugins/pwdstorage/clear_pwd.c
6f51e1
@@ -39,7 +39,7 @@ clear_pw_cmp( const char *userpwd, const char *dbpwd )
6f51e1
          * However, even if the first part of userpw matches dbpwd, but len !=, we
6f51e1
          * have already failed anyawy. This prevents substring matching.
6f51e1
          */
6f51e1
-        if (slapi_ct_memcmp(userpwd, dbpwd, len_dbp) != 0) {
6f51e1
+        if (slapi_ct_memcmp(userpwd, dbpwd, len_user, len_dbp) != 0) {
6f51e1
             result = 1;
6f51e1
         }
6f51e1
     } else {
6f51e1
@@ -51,7 +51,7 @@ clear_pw_cmp( const char *userpwd, const char *dbpwd )
6f51e1
          * dbpwd to itself. We have already got result == 1 if we are here, so we are
6f51e1
          * just trying to take up time!
6f51e1
          */
6f51e1
-        if (slapi_ct_memcmp(dbpwd, dbpwd, len_dbp)) {
6f51e1
+        if (slapi_ct_memcmp(dbpwd, dbpwd, len_dbp, len_dbp)) {
6f51e1
             /* Do nothing, we have the if to fix a coverity check. */
6f51e1
         }
6f51e1
     }
6f51e1
diff --git a/ldap/servers/plugins/pwdstorage/crypt_pwd.c b/ldap/servers/plugins/pwdstorage/crypt_pwd.c
6f51e1
index dfd5af94b..ff8eabb07 100644
6f51e1
--- a/ldap/servers/plugins/pwdstorage/crypt_pwd.c
6f51e1
+++ b/ldap/servers/plugins/pwdstorage/crypt_pwd.c
6f51e1
@@ -56,13 +56,13 @@ crypt_close(Slapi_PBlock *pb __attribute__((unused)))
6f51e1
 int
6f51e1
 crypt_pw_cmp( const char *userpwd, const char *dbpwd )
6f51e1
 {
6f51e1
-    int rc;
6f51e1
+    int32_t rc;
6f51e1
     char *cp;
6f51e1
     PR_Lock(cryptlock);
6f51e1
     /* we use salt (first 2 chars) of encoded password in call to crypt() */
6f51e1
     cp = crypt( userpwd, dbpwd );
6f51e1
     if (cp) {
6f51e1
-       rc= slapi_ct_memcmp( dbpwd, cp, strlen(dbpwd));
6f51e1
+       rc= slapi_ct_memcmp(dbpwd, cp, strlen(dbpwd), strlen(cp));
6f51e1
     } else {
6f51e1
        rc = -1;
6f51e1
     }
6f51e1
diff --git a/ldap/servers/plugins/pwdstorage/md5_pwd.c b/ldap/servers/plugins/pwdstorage/md5_pwd.c
6f51e1
index b27994667..88c11688b 100644
6f51e1
--- a/ldap/servers/plugins/pwdstorage/md5_pwd.c
6f51e1
+++ b/ldap/servers/plugins/pwdstorage/md5_pwd.c
6f51e1
@@ -30,12 +30,12 @@
6f51e1
 int
6f51e1
 md5_pw_cmp( const char *userpwd, const char *dbpwd )
6f51e1
 {
6f51e1
-   int rc=-1;
6f51e1
-   char * bver;
6f51e1
-   PK11Context *ctx=NULL;
6f51e1
+   int32_t rc=-1;
6f51e1
+   char *bver;
6f51e1
+   PK11Context *ctx = NULL;
6f51e1
    unsigned int outLen;
6f51e1
    unsigned char hash_out[MD5_HASH_LEN];
6f51e1
-   unsigned char b2a_out[MD5_HASH_LEN*2]; /* conservative */
6f51e1
+   unsigned char b2a_out[MD5_HASH_LEN * 2]; /* conservative */
6f51e1
    SECItem binary_item;
6f51e1
 
6f51e1
    ctx = PK11_CreateDigestContext(SEC_OID_MD5);
6f51e1
@@ -57,10 +57,10 @@ md5_pw_cmp( const char *userpwd, const char *dbpwd )
6f51e1
    bver = NSSBase64_EncodeItem(NULL, (char *)b2a_out, sizeof b2a_out, &binary_item);
6f51e1
    /* bver points to b2a_out upon success */
6f51e1
    if (bver) {
6f51e1
-	   rc = slapi_ct_memcmp(bver,dbpwd, strlen(dbpwd));
6f51e1
+       rc = slapi_ct_memcmp(bver,dbpwd, strlen(dbpwd), strlen(bver));
6f51e1
    } else {
6f51e1
-	   slapi_log_err(SLAPI_LOG_PLUGIN, MD5_SUBSYSTEM_NAME,
6f51e1
-					   "Could not base64 encode hashed value for password compare");
6f51e1
+       slapi_log_err(SLAPI_LOG_PLUGIN, MD5_SUBSYSTEM_NAME,
6f51e1
+                     "Could not base64 encode hashed value for password compare");
6f51e1
    }
6f51e1
 loser:
6f51e1
    return rc;
6f51e1
diff --git a/ldap/servers/plugins/pwdstorage/sha_pwd.c b/ldap/servers/plugins/pwdstorage/sha_pwd.c
6f51e1
index 5f41c5b93..c9db8964a 100644
6f51e1
--- a/ldap/servers/plugins/pwdstorage/sha_pwd.c
6f51e1
+++ b/ldap/servers/plugins/pwdstorage/sha_pwd.c
6f51e1
@@ -49,7 +49,7 @@ sha_pw_cmp (const char *userpwd, const char *dbpwd, unsigned int shaLen )
6f51e1
     char userhash[MAX_SHA_HASH_SIZE];
6f51e1
     char quick_dbhash[MAX_SHA_HASH_SIZE + SHA_SALT_LENGTH + 3];
6f51e1
     char *dbhash = quick_dbhash;
6f51e1
-    struct berval salt;
6f51e1
+    struct berval salt = {0};
6f51e1
     PRUint32 hash_len;
6f51e1
     unsigned int secOID;
6f51e1
     char *schemeName;
6f51e1
@@ -120,10 +120,20 @@ sha_pw_cmp (const char *userpwd, const char *dbpwd, unsigned int shaLen )
6f51e1
     }
6f51e1
 
6f51e1
     /* the proof is in the comparison... */
6f51e1
-    if ( hash_len >= shaLen ) {
6f51e1
-        result = slapi_ct_memcmp( userhash, dbhash, shaLen );
6f51e1
+    if (hash_len >= shaLen) {
6f51e1
+        /*
6f51e1
+         * This say "if the hash has a salt IE >, OR if they are equal, check the hash component ONLY.
6f51e1
+         * This is why we repeat shaLen twice, even though it seems odd. If you have a dbhast of ssha
6f51e1
+         * it's len is 28, and the userpw is 20, but 0 - 20 is the sha, and 21-28 is the salt, which
6f51e1
+         * has already been processed into userhash.
6f51e1
+         * The case where dbpwd is truncated is handled above in "invalid base64" arm.
6f51e1
+         */
6f51e1
+        result = slapi_ct_memcmp(userhash, dbhash, shaLen, shaLen);
6f51e1
     } else {
6f51e1
-        result = slapi_ct_memcmp( userhash, dbhash + OLD_SALT_LENGTH, hash_len - OLD_SALT_LENGTH );
6f51e1
+        /* This case is for if the salt is at the START, which only applies to DS40B1 case.
6f51e1
+         * May never be a valid check...
6f51e1
+         */
6f51e1
+        result = slapi_ct_memcmp(userhash, dbhash + OLD_SALT_LENGTH, shaLen, hash_len - OLD_SALT_LENGTH);
6f51e1
     }
6f51e1
 
6f51e1
 loser:
6f51e1
diff --git a/ldap/servers/plugins/pwdstorage/smd5_pwd.c b/ldap/servers/plugins/pwdstorage/smd5_pwd.c
6f51e1
index 2e9d195ea..f6b4bb4a0 100644
6f51e1
--- a/ldap/servers/plugins/pwdstorage/smd5_pwd.c
6f51e1
+++ b/ldap/servers/plugins/pwdstorage/smd5_pwd.c
6f51e1
@@ -52,35 +52,37 @@ smd5_pw_cmp( const char *userpwd, const char *dbpwd )
6f51e1
    /*
6f51e1
     * Decode hash stored in database.
6f51e1
     */
6f51e1
-   hash_len = pwdstorage_base64_decode_len(dbpwd, 0);
6f51e1
-   if ( hash_len >= sizeof(quick_dbhash) ) { /* get more space: */
6f51e1
-      dbhash = (char*) slapi_ch_calloc( hash_len + 1, sizeof(char) );
6f51e1
-      if ( dbhash == NULL ) goto loser;
6f51e1
-   } else {
6f51e1
-      memset( quick_dbhash, 0, sizeof(quick_dbhash) );
6f51e1
-   }
6f51e1
-
6f51e1
-   hashresult = PL_Base64Decode( dbpwd, 0, dbhash );
6f51e1
-   if (NULL == hashresult) {
6f51e1
-      slapi_log_err(SLAPI_LOG_PLUGIN, SALTED_MD5_SUBSYSTEM_NAME,
6f51e1
-            "smd5_pw_cmp: userPassword \"%s\" is the wrong length "
6f51e1
-            "or is not properly encoded BASE64\n", dbpwd );
6f51e1
-      goto loser;
6f51e1
-   }
6f51e1
-
6f51e1
-   salt.bv_val = (void*)(dbhash + MD5_LENGTH); /* salt starts after hash value */
6f51e1
-   salt.bv_len = hash_len - MD5_LENGTH; /* remaining bytes must be salt */
6f51e1
-
6f51e1
-   /* create the hash */
6f51e1
-   memset( userhash, 0, sizeof(userhash) );
6f51e1
-   PK11_DigestBegin(ctx);
6f51e1
-   PK11_DigestOp(ctx, (const unsigned char *)userpwd, strlen(userpwd));
6f51e1
-   PK11_DigestOp(ctx, (unsigned char*)(salt.bv_val), salt.bv_len);
6f51e1
-   PK11_DigestFinal(ctx, userhash, &outLen, sizeof userhash);
6f51e1
-   PK11_DestroyContext(ctx, 1);
6f51e1
-
6f51e1
-   /* Compare everything up to the salt. */
6f51e1
-   rc = slapi_ct_memcmp( userhash, dbhash, MD5_LENGTH );
6f51e1
+    hash_len = pwdstorage_base64_decode_len(dbpwd, 0);
6f51e1
+    if (hash_len >= sizeof(quick_dbhash)) { /* get more space: */
6f51e1
+        dbhash = (char *)slapi_ch_calloc(hash_len + 1, sizeof(char));
6f51e1
+        if (dbhash == NULL)
6f51e1
+            goto loser;
6f51e1
+    } else {
6f51e1
+        memset(quick_dbhash, 0, sizeof(quick_dbhash));
6f51e1
+    }
6f51e1
+
6f51e1
+    hashresult = PL_Base64Decode(dbpwd, 0, dbhash);
6f51e1
+    if (NULL == hashresult) {
6f51e1
+        slapi_log_err(SLAPI_LOG_PLUGIN, SALTED_MD5_SUBSYSTEM_NAME,
6f51e1
+                      "smd5_pw_cmp: userPassword \"%s\" is the wrong length "
6f51e1
+                      "or is not properly encoded BASE64\n",
6f51e1
+                      dbpwd);
6f51e1
+        goto loser;
6f51e1
+    }
6f51e1
+
6f51e1
+    salt.bv_val = (void *)(dbhash + MD5_LENGTH); /* salt starts after hash value */
6f51e1
+    salt.bv_len = hash_len - MD5_LENGTH;         /* remaining bytes must be salt */
6f51e1
+
6f51e1
+    /* create the hash */
6f51e1
+    memset(userhash, 0, sizeof(userhash));
6f51e1
+    PK11_DigestBegin(ctx);
6f51e1
+    PK11_DigestOp(ctx, (const unsigned char *)userpwd, strlen(userpwd));
6f51e1
+    PK11_DigestOp(ctx, (unsigned char *)(salt.bv_val), salt.bv_len);
6f51e1
+    PK11_DigestFinal(ctx, userhash, &outLen, sizeof userhash);
6f51e1
+    PK11_DestroyContext(ctx, 1);
6f51e1
+
6f51e1
+    /* Compare everything up to the salt. */
6f51e1
+    rc = slapi_ct_memcmp(userhash, dbhash, MD5_LENGTH, MD5_LENGTH);
6f51e1
 
6f51e1
 loser:
6f51e1
    if ( dbhash && dbhash != quick_dbhash ) slapi_ch_free_string( (char **)&dbhash );
6f51e1
diff --git a/ldap/servers/slapd/ch_malloc.c b/ldap/servers/slapd/ch_malloc.c
6f51e1
index 52ccb64e8..da0b5f6d8 100644
6f51e1
--- a/ldap/servers/slapd/ch_malloc.c
6f51e1
+++ b/ldap/servers/slapd/ch_malloc.c
6f51e1
@@ -343,8 +343,8 @@ slapi_ch_smprintf(const char *fmt, ...)
6f51e1
 
6f51e1
 /* Constant time memcmp. Does not shortcircuit on failure! */
6f51e1
 /* This relies on p1 and p2 both being size at least n! */
6f51e1
-int
6f51e1
-slapi_ct_memcmp( const void *p1, const void *p2, size_t n)
6f51e1
+int32_t
6f51e1
+slapi_ct_memcmp( const void *p1, const void *p2, size_t n1, size_t n2)
6f51e1
 {
6f51e1
     int result = 0;
6f51e1
     const unsigned char *_p1 = (const unsigned char *)p1;
6f51e1
@@ -353,10 +353,35 @@ slapi_ct_memcmp( const void *p1, const void *p2, size_t n)
6f51e1
     if (_p1 == NULL || _p2 == NULL) {
6f51e1
         return 2;
6f51e1
     }
6f51e1
-
6f51e1
-    for (size_t i = 0; i < n; i++) {
6f51e1
-        if (_p1[i] ^ _p2[i]) {
6f51e1
-            result = 1;
6f51e1
+    if (n1 == n2) {
6f51e1
+        for (size_t i = 0; i < n1; i++) {
6f51e1
+            if (_p1[i] ^ _p2[i]) {
6f51e1
+                result = 1;
6f51e1
+            }
6f51e1
+        }
6f51e1
+    } else {
6f51e1
+        const unsigned char *_pa;
6f51e1
+        const unsigned char *_pb;
6f51e1
+        size_t nl;
6f51e1
+        if (n2 > n1) {
6f51e1
+            _pa = _p2;
6f51e1
+            _pb = _p2;
6f51e1
+            nl = n2;
6f51e1
+        } else {
6f51e1
+            _pa = _p1;
6f51e1
+            _pb = _p1;
6f51e1
+            nl = n1;
6f51e1
+        }
6f51e1
+        /* We already fail as n1 != n2 */
6f51e1
+        result = 3;
6f51e1
+        for (size_t i = 0; i < nl; i++) {
6f51e1
+            if (_pa[i] ^ _pb[i]) {
6f51e1
+                /*
6f51e1
+                 * If we don't mutate result here, dead code elimination
6f51e1
+                 * we remove for loop.
6f51e1
+                 */
6f51e1
+                result = 4;
6f51e1
+            }
6f51e1
         }
6f51e1
     }
6f51e1
     return result;
6f51e1
diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h
6f51e1
index 16aa1b711..8555be939 100644
6f51e1
--- a/ldap/servers/slapd/slapi-plugin.h
6f51e1
+++ b/ldap/servers/slapd/slapi-plugin.h
6f51e1
@@ -5856,7 +5856,7 @@ char * slapi_ch_smprintf(const char *fmt, ...)
6f51e1
  * \param n length in bytes of the content of p1 AND p2.
6f51e1
  * \return 0 on match. 1 on non-match. 2 on presence of NULL pointer in p1 or p2.
6f51e1
  */
6f51e1
-int slapi_ct_memcmp( const void *p1, const void *p2, size_t n);
6f51e1
+int32_t slapi_ct_memcmp( const void *p1, const void *p2, size_t n1, size_t n2);
6f51e1
 
6f51e1
 /*
6f51e1
  * syntax plugin routines
6f51e1
-- 
6f51e1
2.13.6
6f51e1