0003-Add-support-for-multiple-verifiers.patch
application/x-patch
Filename: 0003-Add-support-for-multiple-verifiers.patch
Type: application/x-patch
Part: 2
Message:
WIP: SCRAM authentication
Patch
Format: format-patch
Series: patch 0003
Subject: Add support for multiple verifiers.
| File | + | − |
|---|---|---|
| src/backend/catalog/system_views.sql | 1 | 1 |
| src/backend/commands/user.c | 142 | 53 |
| src/backend/libpq/crypt.c | 102 | 15 |
| src/backend/parser/gram.y | 7 | 1 |
| src/bin/pg_dump/pg_dumpall.c | 25 | 3 |
| src/include/catalog/pg_authid.h | 2 | 2 |
| src/include/parser/kwlist.h | 1 | 0 |
| src/test/regress/expected/rules.out | 1 | 1 |
From 488b1d547cbf132bd3980971e91bbf8b8c6e442c Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date: Tue, 3 Mar 2015 20:10:10 +0200
Subject: [PATCH 3/4] Add support for multiple verifiers.
pg_authid.rolpassword field has been replaced with an array.
Add CREATE/ALTER ROLE support for password verifiers.
---
src/backend/catalog/system_views.sql | 2 +-
src/backend/commands/user.c | 195 +++++++++++++++++++++++++----------
src/backend/libpq/crypt.c | 117 ++++++++++++++++++---
src/backend/parser/gram.y | 8 +-
src/bin/pg_dump/pg_dumpall.c | 28 ++++-
src/include/catalog/pg_authid.h | 4 +-
src/include/parser/kwlist.h | 1 +
src/test/regress/expected/rules.out | 2 +-
8 files changed, 281 insertions(+), 76 deletions(-)
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 2800f73..cd78061 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -32,7 +32,7 @@ CREATE VIEW pg_shadow AS
rolsuper AS usesuper,
rolreplication AS userepl,
rolbypassrls AS usebypassrls,
- rolpassword AS passwd,
+ rolverifiers AS verifiers,
rolvaliduntil::abstime AS valuntil,
setconfig AS useconfig
FROM pg_authid LEFT JOIN pg_db_role_setting s
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 75f1b3c..42e473d 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -24,6 +24,7 @@
#include "catalog/pg_authid.h"
#include "catalog/pg_database.h"
#include "catalog/pg_db_role_setting.h"
+#include "catalog/pg_type.h"
#include "commands/comment.h"
#include "commands/dbcommands.h"
#include "commands/seclabel.h"
@@ -55,6 +56,9 @@ static void DelRoleMems(const char *rolename, Oid roleid,
List *memberSpecs, List *memberIds,
bool admin_opt);
+static Datum buildPasswordVerifiers(char *rolname, char *password,
+ bool store_encrypted,
+ char *verifiers, bool *isnull);
/* Check if current user has createrole privileges */
static bool
@@ -80,7 +84,7 @@ CreateRole(CreateRoleStmt *stmt)
ListCell *option;
char *password = NULL; /* user password */
bool encrypt_password = Password_encryption; /* encrypt password? */
- char encrypted_password[MD5_PASSWD_LEN + 1];
+ char *passwordVerifiers = NULL;
bool issuper = false; /* Make the user a superuser? */
bool inherit = true; /* Auto inherit privileges? */
bool createrole = false; /* Can this user create roles? */
@@ -96,6 +100,7 @@ CreateRole(CreateRoleStmt *stmt)
Datum validUntil_datum; /* same, as timestamptz Datum */
bool validUntil_null;
DefElem *dpassword = NULL;
+ DefElem *dpasswordverifiers = NULL;
DefElem *dissuper = NULL;
DefElem *dinherit = NULL;
DefElem *dcreaterole = NULL;
@@ -141,6 +146,14 @@ CreateRole(CreateRoleStmt *stmt)
else if (strcmp(defel->defname, "unencryptedPassword") == 0)
encrypt_password = false;
}
+ else if (strcmp(defel->defname, "passwordVerifiers") == 0)
+ {
+ if (dpassword || dpasswordverifiers)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ dpasswordverifiers = defel;
+ }
else if (strcmp(defel->defname, "sysid") == 0)
{
ereport(NOTICE,
@@ -249,6 +262,8 @@ CreateRole(CreateRoleStmt *stmt)
if (dpassword && dpassword->arg)
password = strVal(dpassword->arg);
+ if (dpasswordverifiers && dpasswordverifiers->arg)
+ passwordVerifiers = strVal(dpasswordverifiers->arg);
if (dissuper)
issuper = intVal(dissuper->arg) != 0;
if (dinherit)
@@ -346,7 +361,10 @@ CreateRole(CreateRoleStmt *stmt)
}
/*
- * Call the password checking hook if there is one defined
+ * Call the password checking hook if there is one defined. Pass the
+ * plaintext password to it if we have it, otherwise just the MD5 hash.
+ *
+ * XXX: what to do with other verifiers? Need to change the hook API...
*/
if (check_password_hook && password)
(*check_password_hook) (stmt->role,
@@ -371,24 +389,10 @@ CreateRole(CreateRoleStmt *stmt)
new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin);
new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(isreplication);
new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit);
-
- if (password)
- {
- if (!encrypt_password || isMD5(password))
- new_record[Anum_pg_authid_rolpassword - 1] =
- CStringGetTextDatum(password);
- else
- {
- if (!pg_md5_encrypt(password, stmt->role, strlen(stmt->role),
- encrypted_password))
- elog(ERROR, "password encryption failed");
- new_record[Anum_pg_authid_rolpassword - 1] =
- CStringGetTextDatum(encrypted_password);
- }
- }
- else
- new_record_nulls[Anum_pg_authid_rolpassword - 1] = true;
-
+ new_record[Anum_pg_authid_rolverifiers - 1] =
+ buildPasswordVerifiers(stmt->role, password, encrypt_password,
+ passwordVerifiers,
+ &new_record_nulls[Anum_pg_authid_rolverifiers - 1]);
new_record[Anum_pg_authid_rolvaliduntil - 1] = validUntil_datum;
new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = validUntil_null;
@@ -487,7 +491,7 @@ AlterRole(AlterRoleStmt *stmt)
char *rolename = NULL;
char *password = NULL; /* user password */
bool encrypt_password = Password_encryption; /* encrypt password? */
- char encrypted_password[MD5_PASSWD_LEN + 1];
+ char *passwordVerifiers = NULL;
int issuper = -1; /* Make the user a superuser? */
int inherit = -1; /* Auto inherit privileges? */
int createrole = -1; /* Can this user create roles? */
@@ -501,6 +505,7 @@ AlterRole(AlterRoleStmt *stmt)
bool validUntil_null;
bool bypassrls = -1;
DefElem *dpassword = NULL;
+ DefElem *dpasswordverifiers = NULL;
DefElem *dissuper = NULL;
DefElem *dinherit = NULL;
DefElem *dcreaterole = NULL;
@@ -519,10 +524,10 @@ AlterRole(AlterRoleStmt *stmt)
DefElem *defel = (DefElem *) lfirst(option);
if (strcmp(defel->defname, "password") == 0 ||
- strcmp(defel->defname, "encryptedPassword") == 0 ||
- strcmp(defel->defname, "unencryptedPassword") == 0)
+ strcmp(defel->defname, "encryptedPassword") == 0 ||
+ strcmp(defel->defname, "unencryptedPassword") == 0)
{
- if (dpassword)
+ if (dpassword || dpasswordverifiers)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("conflicting or redundant options")));
@@ -532,6 +537,14 @@ AlterRole(AlterRoleStmt *stmt)
else if (strcmp(defel->defname, "unencryptedPassword") == 0)
encrypt_password = false;
}
+ else if (strcmp(defel->defname, "passwordVerifiers") == 0)
+ {
+ if (dpassword || dpasswordverifiers)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ dpasswordverifiers = defel;
+ }
else if (strcmp(defel->defname, "superuser") == 0)
{
if (dissuper)
@@ -620,6 +633,8 @@ AlterRole(AlterRoleStmt *stmt)
if (dpassword && dpassword->arg)
password = strVal(dpassword->arg);
+ if (dpasswordverifiers && dpasswordverifiers->arg)
+ passwordVerifiers = strVal(dpasswordverifiers->arg);
if (dissuper)
issuper = intVal(dissuper->arg);
if (dinherit)
@@ -718,7 +733,10 @@ AlterRole(AlterRoleStmt *stmt)
}
/*
- * Call the password checking hook if there is one defined
+ * Call the password checking hook if there is one defined. Pass the
+ * plaintext password to it if we have it, otherwise just the MD5 hash.
+ *
+ * XXX: what to do with other verifiers? Need to change the hook API...
*/
if (check_password_hook && password)
(*check_password_hook)(rolename ,
@@ -779,28 +797,14 @@ AlterRole(AlterRoleStmt *stmt)
new_record_repl[Anum_pg_authid_rolconnlimit - 1] = true;
}
- /* password */
- if (password)
+ /* set or unset password */
+ if (dpassword)
{
- if (!encrypt_password || isMD5(password))
- new_record[Anum_pg_authid_rolpassword - 1] =
- CStringGetTextDatum(password);
- else
- {
- if (!pg_md5_encrypt(password, rolename, strlen(rolename),
- encrypted_password))
- elog(ERROR, "password encryption failed");
- new_record[Anum_pg_authid_rolpassword - 1] =
- CStringGetTextDatum(encrypted_password);
- }
- new_record_repl[Anum_pg_authid_rolpassword - 1] = true;
- }
-
- /* unset password */
- if (dpassword && dpassword->arg == NULL)
- {
- new_record_repl[Anum_pg_authid_rolpassword - 1] = true;
- new_record_nulls[Anum_pg_authid_rolpassword - 1] = true;
+ new_record_repl[Anum_pg_authid_rolverifiers - 1] = true;
+ new_record[Anum_pg_authid_rolverifiers - 1] =
+ buildPasswordVerifiers(rolename, password, encrypt_password,
+ passwordVerifiers,
+ &new_record_nulls[Anum_pg_authid_rolverifiers - 1]);
}
/* valid until */
@@ -1192,16 +1196,43 @@ RenameRole(const char *oldname, const char *newname)
CStringGetDatum(newname));
repl_null[Anum_pg_authid_rolname - 1] = false;
- datum = heap_getattr(oldtuple, Anum_pg_authid_rolpassword, dsc, &isnull);
+ datum = heap_getattr(oldtuple, Anum_pg_authid_rolverifiers, dsc, &isnull);
- if (!isnull && isMD5(TextDatumGetCString(datum)))
+ if (!isnull)
{
- /* MD5 uses the username as salt, so just clear it on a rename */
- repl_repl[Anum_pg_authid_rolpassword - 1] = true;
- repl_null[Anum_pg_authid_rolpassword - 1] = true;
+ Datum *verifiers;
+ int nverifiers;
+ bool modified = false;
- ereport(NOTICE,
- (errmsg("MD5 password cleared because of role rename")));
+ deconstruct_array(DatumGetArrayTypeP(datum), TEXTOID, -1, false, 'i',
+ &verifiers, NULL, &nverifiers);
+ for (i = 0; i < nverifiers;)
+ {
+ char *verifier = TextDatumGetCString(verifiers[i]);
+ if (strncmp(verifier, "md5:", 4) == 0)
+ {
+ /* MD5 uses the username as salt, so clear it on a rename */
+ nverifiers--;
+ memmove(&verifiers[i], &verifiers[i + 1], nverifiers - i);
+ modified = true;
+ ereport(NOTICE,
+ (errmsg("MD5 password cleared because of role rename")));
+ }
+ else
+ i++;
+ }
+ if (modified)
+ {
+ repl_repl[Anum_pg_authid_rolverifiers - 1] = true;
+ if (nverifiers == 0)
+ repl_null[Anum_pg_authid_rolverifiers - 1] = true;
+ else
+ {
+ repl_val[Anum_pg_authid_rolverifiers - 1] =
+ PointerGetDatum(construct_array(verifiers, nverifiers, TEXTOID, -1, false, 'i'));
+ repl_null[Anum_pg_authid_rolverifiers - 1] = false;
+ }
+ }
}
newtuple = heap_modify_tuple(oldtuple, dsc, repl_val, repl_null, repl_repl);
@@ -1624,3 +1655,61 @@ DelRoleMems(const char *rolename, Oid roleid,
*/
heap_close(pg_authmem_rel, NoLock);
}
+
+static Datum
+buildPasswordVerifiers(char *rolname, char *password, bool store_encrypted,
+ char *verifiers, bool *isnull)
+{
+ Datum datums[2];
+ int nverifiers = 0;
+ Datum result;
+ char *verifier = NULL;
+
+ /* If a verifier is given, use it as is */
+ if (verifiers)
+ {
+ result = OidInputFunctionCall(F_ARRAY_IN, verifiers, TEXTOID, -1);
+ *isnull = false;
+ return result;
+ }
+
+ /* If ENCRYPTED or UNENCRYPTED PASSWORD is given, store it */
+ if (password)
+ {
+ /*
+ * Check if the password is actually already an MD5 hash.
+ */
+ if (isMD5(password))
+ {
+ verifier = psprintf("md5:%s", password);
+ datums[nverifiers++] = CStringGetTextDatum(verifier);
+ }
+ else if (!store_encrypted)
+ {
+ verifier = psprintf("password:%s", password);
+ datums[nverifiers++] = CStringGetTextDatum(verifier);
+ }
+ else
+ {
+ char encrypted_password[MD5_PASSWD_LEN + 1];
+
+ if (!pg_md5_encrypt(password, rolname, strlen(rolname),
+ encrypted_password))
+ elog(ERROR, "password encryption failed");
+ verifier = psprintf("md5:%s", encrypted_password);
+ datums[nverifiers++] = CStringGetTextDatum(verifier);
+ }
+ }
+
+ if (nverifiers == 0)
+ {
+ result = (Datum) 0;
+ *isnull = true;
+ }
+ else
+ {
+ result = PointerGetDatum(construct_array(datums, nverifiers, TEXTOID, -1, false, 'i'));
+ *isnull = false;
+ }
+ return result;
+}
diff --git a/src/backend/libpq/crypt.c b/src/backend/libpq/crypt.c
index 97be944..4c94d63 100644
--- a/src/backend/libpq/crypt.c
+++ b/src/backend/libpq/crypt.c
@@ -21,13 +21,93 @@
#endif
#include "catalog/pg_authid.h"
+#include "catalog/pg_type.h"
#include "libpq/crypt.h"
#include "libpq/md5.h"
#include "miscadmin.h"
+#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/syscache.h"
#include "utils/timestamp.h"
+/*
+ * Get verifier stored in pg_roleid tuple, for given authentication method.
+ */
+static char *
+get_role_verifier_from_tup(HeapTuple roleTup, const char *method)
+{
+ Datum datum;
+ bool isnull;
+ Datum *elems;
+ int nelems;
+ char *verifier = NULL;
+ int i;
+
+ datum = SysCacheGetAttr(AUTHNAME, roleTup,
+ Anum_pg_authid_rolverifiers, &isnull);
+ if (isnull)
+ return NULL;
+
+ /* Parse the stored value */
+ deconstruct_array(DatumGetArrayTypeP(datum), TEXTOID, -1, false, 'i',
+ &elems, NULL, &nelems);
+ for (i = 0; i < nelems; i ++)
+ {
+ char *s = TextDatumGetCString(elems[i]);
+ char *colon;
+
+ colon = strchr(s, ':');
+ if (colon == NULL)
+ continue; /* shouldn't happen, invalid verifier */
+
+ *colon = '\0';
+ verifier = colon + 1;
+
+ if (strcmp(s, method) == 0)
+ {
+ verifier = pstrdup(colon + 1);
+ pfree(s);
+ break;
+ }
+ pfree(s);
+ }
+
+ return verifier;
+}
+
+char *
+get_role_verifier(const char *role, const char *method, char **logdetail)
+{
+ char *verifier;
+ HeapTuple roleTup;
+ Datum datum;
+ bool isnull;
+
+ /* Get role info from pg_authid */
+ roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(role));
+ if (!HeapTupleIsValid(roleTup))
+ return NULL; /* no such user */
+
+ datum = SysCacheGetAttr(AUTHNAME, roleTup,
+ Anum_pg_authid_rolvaliduntil, &isnull);
+ if (!isnull)
+ {
+ TimestampTz vuntil = DatumGetTimestampTz(datum);
+ if (vuntil < GetCurrentTimestamp())
+ {
+ *logdetail = psprintf(_("User \"%s\" has an expired password."),
+ role);
+ ReleaseSysCache(roleTup);
+ return NULL;
+ }
+ }
+
+ verifier = get_role_verifier_from_tup(roleTup, method);
+
+ ReleaseSysCache(roleTup);
+
+ return verifier;
+}
/*
* Check given password for given user, and return STATUS_OK or STATUS_ERROR.
@@ -39,9 +119,10 @@ md5_crypt_verify(const Port *port, const char *role, char *client_pass,
char **logdetail)
{
int retval = STATUS_ERROR;
- char *shadow_pass,
+ char *verifier,
*crypt_pwd;
TimestampTz vuntil = 0;
+ bool verifier_is_md5;
char *crypt_client_pass = client_pass;
HeapTuple roleTup;
Datum datum;
@@ -52,16 +133,22 @@ md5_crypt_verify(const Port *port, const char *role, char *client_pass,
if (!HeapTupleIsValid(roleTup))
return STATUS_ERROR; /* no such user */
- datum = SysCacheGetAttr(AUTHNAME, roleTup,
- Anum_pg_authid_rolpassword, &isnull);
- if (isnull)
+ verifier_is_md5 = true;
+ verifier = get_role_verifier_from_tup(roleTup, "md5");
+ if (verifier == NULL)
{
- ReleaseSysCache(roleTup);
- *logdetail = psprintf(_("User \"%s\" has no password assigned."),
- role);
- return STATUS_ERROR; /* user has no password */
+ /* we can also use a plaintext password, by creating the hash from it */
+ verifier_is_md5 = false;
+ verifier = get_role_verifier_from_tup(roleTup, "password");
+
+ if (verifier == NULL)
+ {
+ *logdetail = psprintf(_("User \"%s\" has no password assigned for authentication method \"%s\"."),
+ role, "md5");
+ ReleaseSysCache(roleTup);
+ return STATUS_ERROR;
+ }
}
- shadow_pass = TextDatumGetCString(datum);
datum = SysCacheGetAttr(AUTHNAME, roleTup,
Anum_pg_authid_rolvaliduntil, &isnull);
@@ -70,7 +157,7 @@ md5_crypt_verify(const Port *port, const char *role, char *client_pass,
ReleaseSysCache(roleTup);
- if (*shadow_pass == '\0')
+ if (*verifier == '\0')
return STATUS_ERROR; /* empty password */
CHECK_FOR_INTERRUPTS();
@@ -83,10 +170,10 @@ md5_crypt_verify(const Port *port, const char *role, char *client_pass,
{
case uaMD5:
crypt_pwd = palloc(MD5_PASSWD_LEN + 1);
- if (isMD5(shadow_pass))
+ if (verifier_is_md5)
{
/* stored password already encrypted, only do salt */
- if (!pg_md5_encrypt(shadow_pass + strlen("md5"),
+ if (!pg_md5_encrypt(verifier + strlen("md5"),
port->md5Salt,
sizeof(port->md5Salt), crypt_pwd))
{
@@ -99,7 +186,7 @@ md5_crypt_verify(const Port *port, const char *role, char *client_pass,
/* stored password is plain, double-encrypt */
char *crypt_pwd2 = palloc(MD5_PASSWD_LEN + 1);
- if (!pg_md5_encrypt(shadow_pass,
+ if (!pg_md5_encrypt(verifier,
port->user_name,
strlen(port->user_name),
crypt_pwd2))
@@ -121,7 +208,7 @@ md5_crypt_verify(const Port *port, const char *role, char *client_pass,
}
break;
default:
- if (isMD5(shadow_pass))
+ if (verifier_is_md5)
{
/* Encrypt user-supplied password to match stored MD5 */
crypt_client_pass = palloc(MD5_PASSWD_LEN + 1);
@@ -134,7 +221,7 @@ md5_crypt_verify(const Port *port, const char *role, char *client_pass,
return STATUS_ERROR;
}
}
- crypt_pwd = shadow_pass;
+ crypt_pwd = verifier;
break;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 3aa9e42..d68e61d 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -623,7 +623,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
UNTIL UPDATE USER USING
VACUUM VALID VALIDATE VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING
- VERBOSE VERSION_P VIEW VIEWS VOLATILE
+ VERBOSE VERIFIERS VERSION_P VIEW VIEWS VOLATILE
WHEN WHERE WHITESPACE_P WINDOW WITH WITHIN WITHOUT WORK WRAPPER WRITE
@@ -916,6 +916,11 @@ AlterOptRoleElem:
$$ = makeDefElem("unencryptedPassword",
(Node *)makeString($3));
}
+ | PASSWORD VERIFIERS Sconst
+ {
+ $$ = makeDefElem("passwordVerifiers",
+ (Node *)makeString($3));
+ }
| INHERIT
{
$$ = makeDefElem("inherit", (Node *)makeInteger(TRUE));
@@ -13504,6 +13509,7 @@ unreserved_keyword:
| VALIDATOR
| VALUE_P
| VARYING
+ | VERIFIERS
| VERSION_P
| VIEW
| VIEWS
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 6a7a641..32d710c 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -660,7 +660,8 @@ dumpRoles(PGconn *conn)
i_rolcreatedb,
i_rolcanlogin,
i_rolconnlimit,
- i_rolpassword,
+ i_rolverifiers = -1,
+ i_rolpassword = -1,
i_rolvaliduntil,
i_rolreplication,
i_rolbypassrls,
@@ -669,10 +670,21 @@ dumpRoles(PGconn *conn)
int i;
/* note: rolconfig is dumped later */
+ /* FIXME: bump this when we branch off */
if (server_version >= 90500)
printfPQExpBuffer(buf,
"SELECT oid, rolname, rolsuper, rolinherit, "
"rolcreaterole, rolcreatedb, "
+ "rolcanlogin, rolconnlimit, rolverifiers, "
+ "rolvaliduntil, rolreplication, rolbypassrls, "
+ "pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, "
+ "rolname = current_user AS is_current_user "
+ "FROM pg_authid "
+ "ORDER BY 2");
+ else if (server_version >= 90500)
+ printfPQExpBuffer(buf,
+ "SELECT oid, rolname, rolsuper, rolinherit, "
+ "rolcreaterole, rolcreatedb, "
"rolcanlogin, rolconnlimit, rolpassword, "
"rolvaliduntil, rolreplication, rolbypassrls, "
"pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, "
@@ -757,7 +769,11 @@ dumpRoles(PGconn *conn)
i_rolcreatedb = PQfnumber(res, "rolcreatedb");
i_rolcanlogin = PQfnumber(res, "rolcanlogin");
i_rolconnlimit = PQfnumber(res, "rolconnlimit");
- i_rolpassword = PQfnumber(res, "rolpassword");
+ /* FIXME: version again */
+ if (server_version >= 90500)
+ i_rolverifiers = PQfnumber(res, "rolverifiers");
+ else
+ i_rolpassword = PQfnumber(res, "rolpassword");
i_rolvaliduntil = PQfnumber(res, "rolvaliduntil");
i_rolreplication = PQfnumber(res, "rolreplication");
i_rolbypassrls = PQfnumber(res, "rolbypassrls");
@@ -837,12 +853,18 @@ dumpRoles(PGconn *conn)
appendPQExpBuffer(buf, " CONNECTION LIMIT %s",
PQgetvalue(res, i, i_rolconnlimit));
- if (!PQgetisnull(res, i, i_rolpassword))
+ if (i_rolpassword != -1 && !PQgetisnull(res, i, i_rolpassword))
{
appendPQExpBufferStr(buf, " PASSWORD ");
appendStringLiteralConn(buf, PQgetvalue(res, i, i_rolpassword), conn);
}
+ if (i_rolverifiers != -1 && !PQgetisnull(res, i, i_rolverifiers))
+ {
+ appendPQExpBufferStr(buf, " PASSWORD VERIFIERS ");
+ appendStringLiteralConn(buf, PQgetvalue(res, i, i_rolverifiers), conn);
+ }
+
if (!PQgetisnull(res, i, i_rolvaliduntil))
appendPQExpBuffer(buf, " VALID UNTIL '%s'",
PQgetvalue(res, i, i_rolvaliduntil));
diff --git a/src/include/catalog/pg_authid.h b/src/include/catalog/pg_authid.h
index d5f19d6..498c18b 100644
--- a/src/include/catalog/pg_authid.h
+++ b/src/include/catalog/pg_authid.h
@@ -56,7 +56,7 @@ CATALOG(pg_authid,1260) BKI_SHARED_RELATION BKI_ROWTYPE_OID(2842) BKI_SCHEMA_MAC
/* remaining fields may be null; use heap_getattr to read them! */
#ifdef CATALOG_VARLEN /* variable-length fields start here */
- text rolpassword; /* password, if any */
+ text rolverifiers[]; /* password hashes, if any */
timestamptz rolvaliduntil; /* password expiration time, if any */
#endif
} FormData_pg_authid;
@@ -85,7 +85,7 @@ typedef FormData_pg_authid *Form_pg_authid;
#define Anum_pg_authid_rolreplication 7
#define Anum_pg_authid_rolbypassrls 8
#define Anum_pg_authid_rolconnlimit 9
-#define Anum_pg_authid_rolpassword 10
+#define Anum_pg_authid_rolverifiers 10
#define Anum_pg_authid_rolvaliduntil 11
/* ----------------
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 7c243ec..067112f 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -407,6 +407,7 @@ PG_KEYWORD("varchar", VARCHAR, COL_NAME_KEYWORD)
PG_KEYWORD("variadic", VARIADIC, RESERVED_KEYWORD)
PG_KEYWORD("varying", VARYING, UNRESERVED_KEYWORD)
PG_KEYWORD("verbose", VERBOSE, TYPE_FUNC_NAME_KEYWORD)
+PG_KEYWORD("verifiers", VERIFIERS, UNRESERVED_KEYWORD)
PG_KEYWORD("version", VERSION_P, UNRESERVED_KEYWORD)
PG_KEYWORD("view", VIEW, UNRESERVED_KEYWORD)
PG_KEYWORD("views", VIEWS, UNRESERVED_KEYWORD)
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 1788270..052c4b7 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1608,7 +1608,7 @@ pg_shadow| SELECT pg_authid.rolname AS usename,
pg_authid.rolsuper AS usesuper,
pg_authid.rolreplication AS userepl,
pg_authid.rolbypassrls AS usebypassrls,
- pg_authid.rolpassword AS passwd,
+ pg_authid.rolverifiers AS verifiers,
(pg_authid.rolvaliduntil)::abstime AS valuntil,
s.setconfig AS useconfig
FROM (pg_authid
--
2.1.4