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 */