Skip to content

Commit bee9be2

Browse files
committed
apply 0006-Support-for-SCRAM-SHA-256-authentication-RFC-5802-an.patch + cherry-pick 8d3b9cc and fa87870
1 parent 736b823 commit bee9be2

File tree

29 files changed

+2204
-82
lines changed

29 files changed

+2204
-82
lines changed

contrib/passwordcheck/passwordcheck.c

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#endif
2222

2323
#include "commands/user.h"
24+
#include "libpq/scram.h"
2425
#include "fmgr.h"
2526
#include "libpq/md5.h"
2627

@@ -57,14 +58,15 @@ check_password(const char *username,
5758
{
5859
int namelen = strlen(username);
5960
int pwdlen = strlen(password);
60-
char encrypted[MD5_PASSWD_LEN + 1];
61+
char *encrypted;
6162
int i;
6263
bool pwd_has_letter,
6364
pwd_has_nonletter;
6465

6566
switch (password_type)
6667
{
6768
case PASSWORD_TYPE_MD5:
69+
case PASSWORD_TYPE_SCRAM:
6870

6971
/*
7072
* Unfortunately we cannot perform exhaustive checks on encrypted
@@ -74,12 +76,23 @@ check_password(const char *username,
7476
*
7577
* We only check for username = password.
7678
*/
77-
if (!pg_md5_encrypt(username, username, namelen, encrypted))
78-
elog(ERROR, "password encryption failed");
79+
if (password_type == PASSWORD_TYPE_MD5)
80+
{
81+
encrypted = palloc(MD5_PASSWD_LEN + 1);
82+
if (pg_md5_encrypt(username, username, namelen, encrypted))
83+
elog(ERROR, "password encryption failed");
84+
}
85+
else if (password_type == PASSWORD_TYPE_SCRAM)
86+
{
87+
encrypted = scram_build_verifier(username, password, 0);
88+
}
89+
else
90+
Assert(0); /* should not happen */
7991
if (strcmp(password, encrypted) == 0)
8092
ereport(ERROR,
8193
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8294
errmsg("password must not contain user name")));
95+
pfree(encrypted);
8396
break;
8497

8598
case PASSWORD_TYPE_PLAINTEXT:

doc/src/sgml/config.sgml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1181,7 +1181,8 @@ include_dir 'conf.d'
11811181
<para>
11821182
A value set to <literal>on</> or <literal>md5</> corresponds to a
11831183
MD5-encrypted password, <literal>off</> or <literal>plain</>
1184-
corresponds to an unencrypted password.
1184+
corresponds to an unencrypted password. Setting this parameter to
1185+
<literal>scram</> will encrypt the password with SCRAM-SHA-256.
11851186
</para>
11861187

11871188
<para>
@@ -1259,8 +1260,10 @@ include_dir 'conf.d'
12591260
Authentication checks are always done with the server's user name
12601261
so authentication methods must be configured for the
12611262
server's user name, not the client's. Because
1262-
<literal>md5</> uses the user name as salt on both the
1263-
client and server, <literal>md5</> cannot be used with
1263+
<literal>md5</>uses the user name as salt on both the
1264+
client and server, and <literal>scram</> uses the user name as
1265+
a portion of the salt used on both the client and server,
1266+
<literal>md5</> and <literal>scram</> cannot be used with
12641267
<varname>db_user_namespace</>.
12651268
</para>
12661269

doc/src/sgml/protocol.sgml

Lines changed: 142 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -228,11 +228,11 @@
228228
The server then sends an appropriate authentication request message,
229229
to which the frontend must reply with an appropriate authentication
230230
response message (such as a password).
231-
For all authentication methods except GSSAPI and SSPI, there is at most
232-
one request and one response. In some methods, no response
231+
For all authentication methods except GSSAPI, SSPI and SASL, there is at
232+
most one request and one response. In some methods, no response
233233
at all is needed from the frontend, and so no authentication request
234-
occurs. For GSSAPI and SSPI, multiple exchanges of packets may be needed
235-
to complete the authentication.
234+
occurs. For GSSAPI, SSPI and SASL, multiple exchanges of packets may be
235+
needed to complete the authentication.
236236
</para>
237237

238238
<para>
@@ -366,6 +366,35 @@
366366
</listitem>
367367
</varlistentry>
368368

369+
<varlistentry>
370+
<term>AuthenticationSASL</term>
371+
<listitem>
372+
<para>
373+
The frontend must now initiate a SASL negotiation, using the SASL
374+
mechanism specified in the message. The frontend will send a
375+
PasswordMessage with the first part of the SASL data stream in
376+
response to this. If further messages are needed, the server will
377+
respond with AuthenticationSASLContinue.
378+
</para>
379+
</listitem>
380+
381+
</varlistentry>
382+
<varlistentry>
383+
<term>AuthenticationSASLContinue</term>
384+
<listitem>
385+
<para>
386+
This message contains the response data from the previous step
387+
of SASL negotiation (AuthenticationSASL, or a previous
388+
AuthenticationSASLContinue). If the SASL data in this message
389+
indicates more data is needed to complete the authentication,
390+
the frontend must send that data as another PasswordMessage. If
391+
SASL authentication is completed by this message, the server
392+
will next send AuthenticationOk to indicate successful authentication
393+
or ErrorResponse to indicate failure.
394+
</para>
395+
</listitem>
396+
</varlistentry>
397+
369398
</variablelist>
370399
</para>
371400

@@ -2578,6 +2607,114 @@ AuthenticationGSSContinue (B)
25782607
</listitem>
25792608
</varlistentry>
25802609

2610+
<varlistentry>
2611+
<term>
2612+
AuthenticationSASL (B)
2613+
</term>
2614+
<listitem>
2615+
<para>
2616+
2617+
<variablelist>
2618+
<varlistentry>
2619+
<term>
2620+
Byte1('R')
2621+
</term>
2622+
<listitem>
2623+
<para>
2624+
Identifies the message as an authentication request.
2625+
</para>
2626+
</listitem>
2627+
</varlistentry>
2628+
<varlistentry>
2629+
<term>
2630+
Int32
2631+
</term>
2632+
<listitem>
2633+
<para>
2634+
Length of message contents in bytes, including self.
2635+
</para>
2636+
</listitem>
2637+
</varlistentry>
2638+
<varlistentry>
2639+
<term>
2640+
Int32(10)
2641+
</term>
2642+
<listitem>
2643+
<para>
2644+
Specifies that SASL authentication is started.
2645+
</para>
2646+
</listitem>
2647+
</varlistentry>
2648+
<varlistentry>
2649+
<term>
2650+
String
2651+
</term>
2652+
<listitem>
2653+
<para>
2654+
Name of a SASL authentication mechanism.
2655+
</para>
2656+
</listitem>
2657+
</varlistentry>
2658+
</variablelist>
2659+
2660+
</para>
2661+
</listitem>
2662+
</varlistentry>
2663+
2664+
<varlistentry>
2665+
<term>
2666+
AuthenticationSASLContinue (B)
2667+
</term>
2668+
<listitem>
2669+
<para>
2670+
2671+
<variablelist>
2672+
<varlistentry>
2673+
<term>
2674+
Byte1('R')
2675+
</term>
2676+
<listitem>
2677+
<para>
2678+
Identifies the message as an authentication request.
2679+
</para>
2680+
</listitem>
2681+
</varlistentry>
2682+
<varlistentry>
2683+
<term>
2684+
Int32
2685+
</term>
2686+
<listitem>
2687+
<para>
2688+
Length of message contents in bytes, including self.
2689+
</para>
2690+
</listitem>
2691+
</varlistentry>
2692+
<varlistentry>
2693+
<term>
2694+
Int32(11)
2695+
</term>
2696+
<listitem>
2697+
<para>
2698+
Specifies that this message contains SASL-mechanism specific
2699+
data.
2700+
</para>
2701+
</listitem>
2702+
</varlistentry>
2703+
<varlistentry>
2704+
<term>
2705+
Byte<replaceable>n</replaceable>
2706+
</term>
2707+
<listitem>
2708+
<para>
2709+
SASL data, specific to the SASL mechanism being used.
2710+
</para>
2711+
</listitem>
2712+
</varlistentry>
2713+
</variablelist>
2714+
2715+
</para>
2716+
</listitem>
2717+
</varlistentry>
25812718

25822719
<varlistentry>
25832720
<term>
@@ -4340,7 +4477,7 @@ PasswordMessage (F)
43404477
<listitem>
43414478
<para>
43424479
Identifies the message as a password response. Note that
4343-
this is also used for GSSAPI and SSPI response messages
4480+
this is also used for GSSAPI, SSPI and SASL response messages
43444481
(which is really a design error, since the contained data
43454482
is not a null-terminated string in that case, but can be
43464483
arbitrary binary data).

doc/src/sgml/ref/create_role.sgml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -228,16 +228,16 @@ CREATE ROLE <replaceable class="PARAMETER">name</replaceable> [ [ WITH ] <replac
228228
encrypted in the system catalogs. (If neither is specified,
229229
the default behavior is determined by the configuration
230230
parameter <xref linkend="guc-password-encryption">.) If the
231-
presented password string is already in MD5-encrypted format,
232-
then it is stored encrypted as-is, regardless of whether
233-
<literal>ENCRYPTED</> or <literal>UNENCRYPTED</> is specified
234-
(since the system cannot decrypt the specified encrypted
235-
password string). This allows reloading of encrypted
236-
passwords during dump/restore.
231+
presented password string is already in MD5-encrypted or
232+
SCRAM-encrypted format, then it is stored encrypted as-is,
233+
regardless of whether <literal>ENCRYPTED</> or <literal>UNENCRYPTED</>
234+
is specified (since the system cannot decrypt the specified encrypted
235+
password string). This allows reloading of encrypted passwords
236+
during dump/restore.
237237
</para>
238238

239239
<para>
240-
Note that older clients might lack support for the MD5
240+
Note that older clients might lack support for the MD5 or SCRAM
241241
authentication mechanism that is needed to work with passwords
242242
that are stored encrypted.
243243
</para>

src/backend/commands/user.c

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "commands/seclabel.h"
3131
#include "commands/user.h"
3232
#include "libpq/md5.h"
33+
#include "libpq/scram.h"
3334
#include "miscadmin.h"
3435
#include "storage/lmgr.h"
3536
#include "utils/acl.h"
@@ -69,9 +70,9 @@ have_createrole_privilege(void)
6970
/*
7071
* Encrypt a password if necessary for insertion in pg_authid.
7172
*
72-
* If a password is found as already MD5-encrypted, no error is raised
73-
* to ease the dump and reload of such data. Returns a palloc'ed string
74-
* holding the encrypted password.
73+
* If a password is found as already MD5-encrypted or SCRAM-encrypted, no
74+
* error is raised to ease the dump and reload of such data. Returns a
75+
* palloc'ed string holding the encrypted password.
7576
*/
7677
static char *
7778
encrypt_password(char *password, char *rolname, int passwd_type)
@@ -81,11 +82,11 @@ encrypt_password(char *password, char *rolname, int passwd_type)
8182
Assert(password != NULL);
8283

8384
/*
84-
* If a password is already identified as MD5-encrypted, it is used
85-
* as such. If the password given is not encrypted, adapt it depending
86-
* on the type wanted by the caller of this routine.
85+
* A password already identified as a SCRAM-encrypted or MD5-encrypted
86+
* those are used as such. If the password given is not encrypted,
87+
* adapt it depending on the type wanted by the caller of this routine.
8788
*/
88-
if (isMD5(password))
89+
if (isMD5(password) || is_scram_verifier(password))
8990
res = pstrdup(password);
9091
else
9192
{
@@ -101,6 +102,9 @@ encrypt_password(char *password, char *rolname, int passwd_type)
101102
res))
102103
elog(ERROR, "password encryption failed");
103104
break;
105+
case PASSWORD_TYPE_SCRAM:
106+
res = scram_build_verifier(rolname, password, 0);
107+
break;
104108
default:
105109
Assert(0); /* should not come here */
106110
}

src/backend/libpq/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ include $(top_builddir)/src/Makefile.global
1515
# be-fsstubs is here for historical reasons, probably belongs elsewhere
1616

1717
OBJS = be-fsstubs.o be-secure.o auth.o crypt.o hba.o ip.o md5.o pqcomm.o \
18-
pqformat.o pqmq.o pqsignal.o
18+
pqformat.o pqmq.o pqsignal.o auth-scram.o
1919

2020
ifeq ($(with_openssl),yes)
2121
OBJS += be-secure-openssl.o

0 commit comments

Comments
 (0)