Allow plaintext 'password' authentication when user has a SCRAM verifier.
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Fri, 17 Mar 2017 09:33:27 +0000 (11:33 +0200)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
Fri, 17 Mar 2017 09:33:27 +0000 (11:33 +0200)
Oversight in the main SCRAM patch.

src/backend/libpq/auth-scram.c
src/backend/libpq/crypt.c
src/include/libpq/scram.h

index 9f78e57aae5102c33c9d8637eb827b39881e9de8..db15a2fac6ca03cc65854adde9af2c8b4a9aee90 100644 (file)
@@ -364,6 +364,52 @@ scram_build_verifier(const char *username, const char *password,
    return psprintf("scram-sha-256:%s:%d:%s:%s", encoded_salt, iterations, storedkey_hex, serverkey_hex);
 }
 
+/*
+ * Verify a plaintext password against a SCRAM verifier.  This is used when
+ * performing plaintext password authentication for a user that has a SCRAM
+ * verifier stored in pg_authid.
+ */
+bool
+scram_verify_plain_password(const char *username, const char *password,
+                           const char *verifier)
+{
+   char       *encoded_salt;
+   char       *salt;
+   int         saltlen;
+   int         iterations;
+   uint8       stored_key[SCRAM_KEY_LEN];
+   uint8       server_key[SCRAM_KEY_LEN];
+   uint8       computed_key[SCRAM_KEY_LEN];
+
+   if (!parse_scram_verifier(verifier, &encoded_salt, &iterations,
+                             stored_key, server_key))
+   {
+       /*
+        * The password looked like a SCRAM verifier, but could not be
+        * parsed.
+        */
+       elog(LOG, "invalid SCRAM verifier for user \"%s\"", username);
+       return false;
+   }
+
+   salt = palloc(pg_b64_dec_len(strlen(encoded_salt)));
+   saltlen = pg_b64_decode(encoded_salt, strlen(encoded_salt), salt);
+   if (saltlen == -1)
+   {
+       elog(LOG, "invalid SCRAM verifier for user \"%s\"", username);
+       return false;
+   }
+
+   /* Compute Server key based on the user-supplied plaintext password */
+   scram_ClientOrServerKey(password, salt, saltlen, iterations,
+                           SCRAM_SERVER_KEY_NAME, computed_key);
+
+   /*
+    * Compare the verifier's Server Key with the one computed from the
+    * user-supplied password.
+    */
+   return memcmp(computed_key, server_key, SCRAM_KEY_LEN) == 0;
+}
 
 /*
  * Check if given verifier can be used for SCRAM authentication.
index 9f0ae15b00ea16d3bf70b61e6c38041476a9d746..ac10751ec2026f43487bedd856816c3e806dc556 100644 (file)
@@ -283,7 +283,6 @@ plain_crypt_verify(const char *role, const char *shadow_pass,
                   const char *client_pass,
                   char **logdetail)
 {
-   int         retval;
    char        crypt_client_pass[MD5_PASSWD_LEN + 1];
 
    /*
@@ -293,6 +292,21 @@ plain_crypt_verify(const char *role, const char *shadow_pass,
     */
    switch (get_password_type(shadow_pass))
    {
+       case PASSWORD_TYPE_SCRAM:
+           if (scram_verify_plain_password(role,
+                                           client_pass,
+                                           shadow_pass))
+           {
+               return STATUS_OK;
+           }
+           else
+           {
+               *logdetail = psprintf(_("Password does not match for user \"%s\"."),
+                                     role);
+               return STATUS_ERROR;
+           }
+           break;
+
        case PASSWORD_TYPE_MD5:
            if (!pg_md5_encrypt(client_pass,
                                role,
@@ -307,30 +321,33 @@ plain_crypt_verify(const char *role, const char *shadow_pass,
                 */
                return STATUS_ERROR;
            }
-           client_pass = crypt_client_pass;
+           if (strcmp(crypt_client_pass, shadow_pass) == 0)
+               return STATUS_OK;
+           else
+           {
+               *logdetail = psprintf(_("Password does not match for user \"%s\"."),
+                                     role);
+               return STATUS_ERROR;
+           }
            break;
+
        case PASSWORD_TYPE_PLAINTEXT:
+           if (strcmp(client_pass, shadow_pass) == 0)
+               return STATUS_OK;
+           else
+           {
+               *logdetail = psprintf(_("Password does not match for user \"%s\"."),
+                                     role);
+               return STATUS_ERROR;
+           }
            break;
-
-       default:
-
-           /*
-            * This shouldn't happen. Plain "password" authentication should
-            * be possible with any kind of stored password hash.
-            */
-           *logdetail = psprintf(_("Password of user \"%s\" is in unrecognized format."),
-                                 role);
-           return STATUS_ERROR;
    }
 
-   if (strcmp(client_pass, shadow_pass) == 0)
-       retval = STATUS_OK;
-   else
-   {
-       *logdetail = psprintf(_("Password does not match for user \"%s\"."),
-                             role);
-       retval = STATUS_ERROR;
-   }
-
-   return retval;
+   /*
+    * This shouldn't happen.  Plain "password" authentication is possible
+    * with any kind of stored password hash.
+    */
+   *logdetail = psprintf(_("Password of user \"%s\" is in unrecognized format."),
+                         role);
+   return STATUS_ERROR;
 }
index 78a52db6841ed1cf6becbd4bc6b0a26311f26921..fb21e056c81dde814cf9ba9c8f714d3703c11176 100644 (file)
@@ -31,5 +31,7 @@ extern char *scram_build_verifier(const char *username,
                     const char *password,
                     int iterations);
 extern bool is_scram_verifier(const char *verifier);
+extern bool scram_verify_plain_password(const char *username,
+                           const char *password, const char *verifier);
 
 #endif   /* PG_SCRAM_H */