From 6f00620dd2029b5f6777c1478edd31002834524b Mon Sep 17 00:00:00 2001 From: Michael Paquier Date: Mon, 20 Feb 2017 14:15:18 +0900 Subject: [PATCH 7/8] Make hba configuration for SASL more extensible The entries of pg_hba.conf are changed from "scram" to that: "sasl protocol=scram_sha_256". SASL supporting many families of authentication mechanisms, this likely makes the most sense. protocol is as well a mandatory parameter. In the future if other mechanisms are added this will prove to be very handy, by being able for example to have a list of mechanisms defined. Documentation and regression tests are updated according to that. --- doc/src/sgml/client-auth.sgml | 46 +++++++++++++++++++++++-------- src/backend/libpq/auth.c | 10 +++++-- src/backend/libpq/hba.c | 31 +++++++++++++++++++-- src/backend/libpq/pg_hba.conf.sample | 4 +-- src/bin/initdb/initdb.c | 14 +++++----- src/include/libpq/hba.h | 1 + src/test/recovery/t/010_authentication.pl | 14 +++++++--- 7 files changed, 91 insertions(+), 29 deletions(-) diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml index f27d417f65..125a07325a 100644 --- a/doc/src/sgml/client-auth.sgml +++ b/doc/src/sgml/client-auth.sgml @@ -423,11 +423,12 @@ hostnossl database user - scram + sasl Require the client to supply a password encrypted with - SCRAM-SHA-256 for authentication. + a protocol compatible with SASL exchange protocol, like + SCRAM-SHA-256, for authentication. See for details. @@ -684,19 +685,19 @@ host postgres all 192.168.93.0/24 ident # "postgres" if the user's password is correctly supplied. # # TYPE DATABASE USER ADDRESS METHOD -host postgres all 192.168.12.10/32 scram +host postgres all 192.168.12.10/32 sasl protocol=scram_sha_256 # Allow any user from hosts in the example.com domain to connect to # any database if the user's password is correctly supplied. # -# Most users use SCRAM authentication, but some users use older clients -# that don't support SCRAM authentication, and need to be able to log -# in using MD5 authentication. Such users are put in the @md5users -# group, everyone else must use SCRAM. +# Most users use SCRAM-SHA-256 authentication, but some users use older +# clients that don't support SCRAM-SHA-256 authentication, and need to be +# able to log in using MD5 authentication. Such users are put in the @md5users +# group, everyone else must use SCRAM-SHA-256. # # TYPE DATABASE USER ADDRESS METHOD host all @md5users .example.com md5 -host all all .example.com scram +host all all .example.com sasl protocol=scram_sha_256 # In the absence of preceding "host" lines, these two lines will # reject all connections from 192.168.54.1 (since that entry will be @@ -942,8 +943,8 @@ omicron bryanh guest1 - scram, as described in - RFC5802 is + sasl, as described in + RFC4422 is defined to be more robust more than md5 from a security point of view as it protects from cases where the hashed password is taken directly from pg_authid in which case @@ -951,8 +952,29 @@ omicron bryanh guest1 the password behind it. It protects as well from password interception and data sniffing where the password data could be directly obtained from the network as well as man-in-the-middle (MITM) attacks. So it - is strongly encouraged to use scram over md5 for - password-based deployments. + is strongly encouraged to use sasl over md5 for + password-based deployments. SASL supports many families + of authentication mechanisms, PostgreSQL supporting only + SCRAM-SHA-256 as described in + RFC5802. + + + + The following configuration options are supported for SASL: + + + protocol + + + Sets the exchange protocol supported. This parameter is mandatory + and only the authentication mechanism listed are supported for the + user attempting a connection. It can be to scram_sha_256 + to authorize users to connect using SCRAM-SHA-256 + as authentication mechanism for the SASL exchange protocol. + + + + diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c index e475366ef3..2adfb8c4d4 100644 --- a/src/backend/libpq/auth.c +++ b/src/backend/libpq/auth.c @@ -818,8 +818,14 @@ CheckSASLAuth(Port *port, char **logdetail) * guess that a server is expecting SASL or MD5 depending on the answer * given by the backend without the user providing a password first. */ - sendAuthRequest(port, AUTH_REQ_SASL, SCRAM_SHA256_NAME, - strlen(SCRAM_SHA256_NAME) + 1); + if (strcmp(port->hba->sasl_protocol, "scram_sha_256") == 0) + sendAuthRequest(port, AUTH_REQ_SASL, SCRAM_SHA256_NAME, + strlen(SCRAM_SHA256_NAME) + 1); + else + ereport(FATAL, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("SASL protocol %s is not supported", + port->hba->sasl_protocol))); /* * If the user doesn't exist, or doesn't have a valid password, or diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c index 3817d249c4..b060109f93 100644 --- a/src/backend/libpq/hba.c +++ b/src/backend/libpq/hba.c @@ -125,7 +125,7 @@ static const char *const UserAuthName[] = "ident", "password", "md5", - "scram", + "sasl", "gss", "sspi", "pam", @@ -1324,7 +1324,7 @@ parse_hba_line(TokenizedLine *tok_line, int elevel) } parsedline->auth_method = uaMD5; } - else if (strcmp(token->string, "scram") == 0) + else if (strcmp(token->string, "sasl") == 0) parsedline->auth_method = uaSASL; else if (strcmp(token->string, "pam") == 0) #ifdef USE_PAM @@ -1536,6 +1536,11 @@ parse_hba_line(TokenizedLine *tok_line, int elevel) MANDATORY_AUTH_ARG(parsedline->radiussecret, "radiussecret", "radius"); } + if (parsedline->auth_method == uaSASL) + { + MANDATORY_AUTH_ARG(parsedline->sasl_protocol, "protocol", "sasl"); + } + /* * Enforce any parameters implied by other settings. */ @@ -1821,6 +1826,21 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline, REQUIRE_AUTH_OPTION(uaRADIUS, "radiusidentifier", "radius"); hbaline->radiusidentifier = pstrdup(val); } + else if (strcmp(name, "protocol") == 0) + { + if (hbaline->auth_method != uaSASL) + INVALID_AUTH_OPTION("protocol", gettext_noop("sasl")); + if (strcmp(val, "scram_sha_256") != 0) + { + ereport(elevel, + (errcode(ERRCODE_CONFIG_FILE_ERROR), + errmsg("invalid protocol name for SASL: \"%s\"", val), + errcontext("line %d of configuration file \"%s\"", + line_num, HbaFileName))); + *err_msg = psprintf("invalid protocl name for SASL: \"%s\"", val); + } + hbaline->sasl_protocol = pstrdup(val); + } else { ereport(elevel, @@ -2077,6 +2097,13 @@ gethba_options(HbaLine *hba) options[noptions++] = CStringGetTextDatum(psprintf("pamservice=%s", hba->pamservice)); + if (hba->auth_method == uaSASL) + { + if (hba->sasl_protocol) + options[noptions++] = + CStringGetTextDatum(psprintf("protocol=%s", hba->sasl_protocol)); + } + if (hba->auth_method == uaLDAP) { if (hba->ldapserver) diff --git a/src/backend/libpq/pg_hba.conf.sample b/src/backend/libpq/pg_hba.conf.sample index 73f7973ea2..3b04d2367f 100644 --- a/src/backend/libpq/pg_hba.conf.sample +++ b/src/backend/libpq/pg_hba.conf.sample @@ -42,9 +42,9 @@ # or "samenet" to match any address in any subnet that the server is # directly connected to. # -# METHOD can be "trust", "reject", "md5", "password", "scram", "gss", +# METHOD can be "trust", "reject", "md5", "password", "sasl", "gss", # "sspi", "ident", "peer", "pam", "ldap", "radius" or "cert". Note that -# "password" sends passwords in clear text; "md5" or "scram" are preferred +# "password" sends passwords in clear text; "md5" or "sasl" are preferred # since they send encrypted passwords. # # OPTIONS are a set of options for the authentication in the format diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index 4968fc783e..52fb1372ca 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -75,7 +75,7 @@ extern const char *select_default_timezone(const char *share_path); static const char *const auth_methods_host[] = { - "trust", "reject", "md5", "password", "scram", "ident", "radius", + "trust", "reject", "md5", "password", "sasl", "ident", "radius", #ifdef ENABLE_GSS "gss", #endif @@ -97,7 +97,7 @@ static const char *const auth_methods_host[] = { NULL }; static const char *const auth_methods_local[] = { - "trust", "reject", "md5", "scram", "password", "peer", "radius", + "trust", "reject", "md5", "sasl", "password", "peer", "radius", #ifdef USE_PAM "pam", "pam ", #endif @@ -1128,8 +1128,8 @@ setup_config(void) "#update_process_title = off"); #endif - if (strcmp(authmethodlocal, "scram") == 0 || - strcmp(authmethodhost, "scram") == 0) + if (strcmp(authmethodlocal, "sasl") == 0 || + strcmp(authmethodhost, "sasl") == 0) { conflines = replace_token(conflines, "#password_encryption = md5", @@ -2316,16 +2316,16 @@ check_need_password(const char *authmethodlocal, const char *authmethodhost) { if ((strcmp(authmethodlocal, "md5") == 0 || strcmp(authmethodlocal, "password") == 0 || - strcmp(authmethodlocal, "scram") == 0) && + strcmp(authmethodlocal, "sasl") == 0) && (strcmp(authmethodhost, "md5") == 0 || strcmp(authmethodhost, "password") == 0 || - strcmp(authmethodlocal, "scram") == 0) && + strcmp(authmethodlocal, "sasl") == 0) && !(pwprompt || pwfilename)) { fprintf(stderr, _("%s: must specify a password for the superuser to enable %s authentication\n"), progname, (strcmp(authmethodlocal, "md5") == 0 || strcmp(authmethodlocal, "password") == 0 || - strcmp(authmethodlocal, "scram") == 0) + strcmp(authmethodlocal, "sasl") == 0) ? authmethodlocal : authmethodhost); exit(1); diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h index 8f55edb16a..51968c98b8 100644 --- a/src/include/libpq/hba.h +++ b/src/include/libpq/hba.h @@ -93,6 +93,7 @@ typedef struct HbaLine char *radiussecret; char *radiusidentifier; int radiusport; + char *sasl_protocol; } HbaLine; typedef struct IdentLine diff --git a/src/test/recovery/t/010_authentication.pl b/src/test/recovery/t/010_authentication.pl index 16be7f5cc8..c68a1093f6 100644 --- a/src/test/recovery/t/010_authentication.pl +++ b/src/test/recovery/t/010_authentication.pl @@ -16,11 +16,17 @@ use Test::More tests => 12; # and then execute a reload to refresh it. sub reset_pg_hba { - my $node = shift; - my $hba_method = shift; + my ($node, $hba_method, $options) = @_; unlink($node->data_dir . '/pg_hba.conf'); - $node->append_conf('pg_hba.conf', "local all all $hba_method"); + if (defined($options)) + { + $node->append_conf('pg_hba.conf', "local all all $hba_method $options"); + } + else + { + $node->append_conf('pg_hba.conf', "local all all $hba_method"); + } $node->reload; } @@ -71,7 +77,7 @@ SKIP: # For "scram" method, user "plain_role" and "scram_role" should be able to # connect. - reset_pg_hba($node, 'scram'); + reset_pg_hba($node, 'sasl', 'protocol=scram_sha_256'); test_role($node, 'scram_role', 'scram', 0); test_role($node, 'md5_role', 'scram', 2); test_role($node, 'plain_role', 'scram', 0); -- 2.12.0