From 818028fcd648abe87a1c5d5dc582c34a907044b4 Mon Sep 17 00:00:00 2001 From: Michael Paquier Date: Tue, 23 Feb 2016 15:32:50 +0900 Subject: [PATCH 2/9] Introduce password_protocols This new superuser GUC parameters specifies a list of supported password protocols in Postgres backend. This is useful for system maintainers to prevent the creation of password using a protocol thought as unsafe in certain deployments. The current default is 'plain,md5', authorizing the creation of both plain passwords and MD5-encrypted passwords in the system. --- doc/src/sgml/config.sgml | 27 ++++++++++++++++ src/backend/commands/user.c | 46 +++++++++++++++++++++++++++ src/backend/utils/misc/guc.c | 29 +++++++++++++---- src/backend/utils/misc/postgresql.conf.sample | 2 ++ src/include/commands/user.h | 3 +- src/test/regress/expected/password.out | 28 ++++++++++++++++ src/test/regress/sql/password.sql | 20 ++++++++++++ 7 files changed, 147 insertions(+), 8 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index 226d51d..e62c729 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -1190,6 +1190,33 @@ include_dir 'conf.d' + + password_protocols (string) + + password_protocols configuration parameter + + + + + Specifies a comma-separated list of supported password formats by + the server. Supported formats are currently plain and + md5. + + + + When a password is specified in or + , this parameter determines if the + password specified is authorized to be stored or not, returning + an error message to caller if it is not. + + + + The default is plain,md5, meaning that MD5-encrypted + passwords and plain passwords are both accepted. + + + + krb_server_keyfile (string) diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c index 83fc210..2b3a33c 100644 --- a/src/backend/commands/user.c +++ b/src/backend/commands/user.c @@ -1597,6 +1597,8 @@ static void FlattenPasswordIdentifiers(List *verifiers, char *rolname) { ListCell *l; + char *rawstring; + List *elemlist; foreach(l, verifiers) { @@ -1627,6 +1629,50 @@ FlattenPasswordIdentifiers(List *verifiers, char *rolname) isMD5(spec->value)) spec->veriftype = AUTH_VERIFIER_MD5; } + + /* + * Now that the list of verifiers is built and consistent with the input + * values, check that the list of verifiers specified is actually + * supported by server or not. + */ + rawstring = pstrdup(password_protocols); + + if (!SplitIdentifierString(rawstring, ',', &elemlist)) + Assert(false); /* should not happen */ + + /* + * This is O(N ^ 2), but the small number of elements in the list of + * protocols supported is not worth complicating this code. + */ + foreach(l, verifiers) + { + AuthVerifierSpec *spec = (AuthVerifierSpec *) lfirst(l); + ListCell *l2; + bool found_match = false; + + foreach(l2, elemlist) + { + char *meth_name = (char *) lfirst(l2); + + if ((strcmp(meth_name, "md5") == 0 && + spec->veriftype == AUTH_VERIFIER_MD5) || + (strcmp(meth_name, "plain") == 0 && + spec->veriftype == AUTH_VERIFIER_PLAIN)) + { + found_match = true; + break; + } + } + + if (!found_match) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("specified password protocol not allowed"), + errdetail("List of authorized protocols is specified by password_protocols."))); + } + + pfree(rawstring); + list_free(elemlist); } /* diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index ca962bf..e5610aa 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -179,8 +179,8 @@ static void assign_pgstat_temp_directory(const char *newval, void *extra); static bool check_application_name(char **newval, void **extra, GucSource source); static void assign_application_name(const char *newval, void *extra); static bool check_cluster_name(char **newval, void **extra, GucSource source); -static bool check_password_encryption(char **newval, void **extra, - GucSource source); +static bool check_password_methods(char **newval, void **extra, + GucSource source); static const char *show_unix_socket_permissions(void); static const char *show_log_file_mode(void); @@ -436,6 +436,7 @@ int temp_file_limit = -1; int num_temp_buffers = 1024; char *Password_encryption; +char *password_protocols; char *cluster_name = ""; char *ConfigFileName; @@ -3328,7 +3329,21 @@ static struct config_string ConfigureNamesString[] = }, &Password_encryption, "md5", - check_password_encryption, NULL, NULL + check_password_methods, NULL, NULL + }, + + { + {"password_protocols", PGC_SUSET, CONN_AUTH_SECURITY, + gettext_noop("List of password protocols supported."), + gettext_noop("The list of password protocols specified by this " + "parameter determines what are the authorized methods " + "on the server when running CREATE USER or ALTER " + "USER."), + GUC_LIST_INPUT + }, + &password_protocols, + "plain,md5", + check_password_methods, NULL, NULL }, { @@ -10171,7 +10186,7 @@ check_cluster_name(char **newval, void **extra, GucSource source) } static bool -check_password_encryption(char **newval, void **extra, GucSource source) +check_password_methods(char **newval, void **extra, GucSource source) { char *rawstring = pstrdup(*newval); /* get copy of list string */ List *elemlist; @@ -10189,10 +10204,10 @@ check_password_encryption(char **newval, void **extra, GucSource source) /* Check that only supported formats are listed */ foreach(l, elemlist) { - char *encryption_name = (char *) lfirst(l); + char *method_name = (char *) lfirst(l); - if (strcmp(encryption_name, "md5") != 0 && - strcmp(encryption_name, "plain") != 0) + if (strcmp(method_name, "md5") != 0 && + strcmp(method_name, "plain") != 0) { pfree(rawstring); list_free(elemlist); diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index d6da960..065b4ab 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -88,6 +88,8 @@ #ssl_ca_file = '' # (change requires restart) #ssl_crl_file = '' # (change requires restart) #password_encryption = 'md5' +#password_protocols = 'plain,md5' # comma-separated list of supported + # password protocols. #db_user_namespace = off #row_security = on diff --git a/src/include/commands/user.h b/src/include/commands/user.h index 636e8ac..7a73bc5 100644 --- a/src/include/commands/user.h +++ b/src/include/commands/user.h @@ -14,8 +14,9 @@ #include "catalog/objectaddress.h" #include "nodes/parsenodes.h" -/* GUC parameter */ +/* GUC parameters */ extern char *Password_encryption; +extern char *password_protocols; typedef void (*check_password_hook_type) (const char *username, List *passwordVerifiers, diff --git a/src/test/regress/expected/password.out b/src/test/regress/expected/password.out index 73ca2e5..f5fdc3f 100644 --- a/src/test/regress/expected/password.out +++ b/src/test/regress/expected/password.out @@ -9,6 +9,14 @@ ERROR: invalid value for parameter "password_encryption": "true" SET password_encryption = 'md5'; -- ok SET password_encryption = 'plain'; -- ok SET password_encryption = 'md5,plain'; -- ok +-- Tests for GUC password_protocols +SET password_protocols = 'novalue'; -- error +ERROR: invalid value for parameter "password_protocols": "novalue" +SET password_protocols = true; -- error +ERROR: invalid value for parameter "password_protocols": "true" +SET password_protocols = 'md5'; -- ok +SET password_protocols = 'plain'; -- ok +SET password_protocols = 'md5,plain'; -- ok -- consistency of password entries SET password_encryption = 'plain'; CREATE ROLE role_passwd1 PASSWORD 'role_pwd1'; @@ -89,6 +97,26 @@ SELECT a.rolname, v.verimet, substr(v.verival, 1, 3) role_passwd4 | m | md5 (4 rows) +-- entries for password_protocols +SET password_protocols = 'md5,plain'; +ALTER ROLE role_passwd5 PASSWORD VERIFIERS (md5 = 'foo', plain = 'foo'); -- ok +SET password_protocols = 'md5'; +ALTER ROLE role_passwd5 PASSWORD VERIFIERS (plain = 'foo'); -- error +ERROR: specified password protocol not allowed +DETAIL: List of authorized protocols is specified by password_protocols. +ALTER ROLE role_passwd5 PASSWORD VERIFIERS (md5 = 'foo'); -- ok +SET password_protocols = 'plain'; +ALTER ROLE role_passwd5 PASSWORD VERIFIERS (plain = 'foo'); -- ok +ALTER ROLE role_passwd5 PASSWORD VERIFIERS (md5 = 'foo'); -- error +ERROR: specified password protocol not allowed +DETAIL: List of authorized protocols is specified by password_protocols. +SET password_protocols = ''; +ALTER ROLE role_passwd5 PASSWORD VERIFIERS (plain = 'foo'); -- error +ERROR: specified password protocol not allowed +DETAIL: List of authorized protocols is specified by password_protocols. +ALTER ROLE role_passwd5 PASSWORD VERIFIERS (md5 = 'foo'); -- error +ERROR: specified password protocol not allowed +DETAIL: List of authorized protocols is specified by password_protocols. DROP ROLE role_passwd1; DROP ROLE role_passwd2; DROP ROLE role_passwd3; diff --git a/src/test/regress/sql/password.sql b/src/test/regress/sql/password.sql index 0376e1b..5cba2d8 100644 --- a/src/test/regress/sql/password.sql +++ b/src/test/regress/sql/password.sql @@ -9,6 +9,13 @@ SET password_encryption = 'md5'; -- ok SET password_encryption = 'plain'; -- ok SET password_encryption = 'md5,plain'; -- ok +-- Tests for GUC password_protocols +SET password_protocols = 'novalue'; -- error +SET password_protocols = true; -- error +SET password_protocols = 'md5'; -- ok +SET password_protocols = 'plain'; -- ok +SET password_protocols = 'md5,plain'; -- ok + -- consistency of password entries SET password_encryption = 'plain'; CREATE ROLE role_passwd1 PASSWORD 'role_pwd1'; @@ -59,6 +66,19 @@ SELECT a.rolname, v.verimet, substr(v.verival, 1, 3) WHERE a.rolname LIKE 'role_passwd%' ORDER BY a.rolname, v.verimet; +-- entries for password_protocols +SET password_protocols = 'md5,plain'; +ALTER ROLE role_passwd5 PASSWORD VERIFIERS (md5 = 'foo', plain = 'foo'); -- ok +SET password_protocols = 'md5'; +ALTER ROLE role_passwd5 PASSWORD VERIFIERS (plain = 'foo'); -- error +ALTER ROLE role_passwd5 PASSWORD VERIFIERS (md5 = 'foo'); -- ok +SET password_protocols = 'plain'; +ALTER ROLE role_passwd5 PASSWORD VERIFIERS (plain = 'foo'); -- ok +ALTER ROLE role_passwd5 PASSWORD VERIFIERS (md5 = 'foo'); -- error +SET password_protocols = ''; +ALTER ROLE role_passwd5 PASSWORD VERIFIERS (plain = 'foo'); -- error +ALTER ROLE role_passwd5 PASSWORD VERIFIERS (md5 = 'foo'); -- error + DROP ROLE role_passwd1; DROP ROLE role_passwd2; DROP ROLE role_passwd3; -- 2.7.1