Support SCRAM-SHA-256 authentication (RFC 5802 and 7677).
This introduces a new generic SASL authentication method, similar to the
GSS and SSPI methods. The server first tells the client which SASL
authentication mechanism to use, and then the mechanism-specific SASL
messages are exchanged in AuthenticationSASLcontinue and PasswordMessage
messages. Only SCRAM-SHA-256 is supported at the moment, but this allows
adding more SASL mechanisms in the future, without changing the overall
protocol.
Support for channel binding, aka SCRAM-SHA-256-PLUS is left for later.
The SASLPrep algorithm, for pre-processing the password, is not yet
implemented. That could cause trouble, if you use a password with
non-ASCII characters, and a client library that does implement SASLprep.
That will hopefully be added later.
Authorization identities, as specified in the SCRAM-SHA-256 specification,
are ignored. SET SESSION AUTHORIZATION provides more or less the same
functionality, anyway.
If a user doesn't exist, perform a "mock" authentication, by constructing
an authentic-looking challenge on the fly. The challenge is derived from
a new system-wide random value, "mock authentication nonce", which is
created at initdb, and stored in the control file. We go through these
motions, in order to not give away the information on whether the user
exists, to unauthenticated users.
Bumps PG_CONTROL_VERSION, because of the new field in control file.
Patch by Michael Paquier and Heikki Linnakangas, reviewed at different
stages by Robert Haas, Stephen Frost, David Steele, Aleksander Alekseev,
and many others.
Discussion: https://www.postgresql.org/message-id/CAB7nPqRbR3GmFYdedCAhzukfKrgBLTLtMvENOmPrVWREsZkF8g%40mail.gmail.com
Discussion: https://www.postgresql.org/message-id/CAB7nPqSMXU35g%3DW9X74HVeQp0uvgJxvYOuA4A-A3M%2B0wfEBv-w%40mail.gmail.com
Discussion: https://www.postgresql.org/message-id/55192AFE.6080106@iki.fi
2017-03-07 13:25:40 +01:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* fe-auth-scram.c
|
|
|
|
* The front-end (client) implementation of SCRAM authentication.
|
|
|
|
*
|
2017-03-07 14:22:52 +01:00
|
|
|
* Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
|
Support SCRAM-SHA-256 authentication (RFC 5802 and 7677).
This introduces a new generic SASL authentication method, similar to the
GSS and SSPI methods. The server first tells the client which SASL
authentication mechanism to use, and then the mechanism-specific SASL
messages are exchanged in AuthenticationSASLcontinue and PasswordMessage
messages. Only SCRAM-SHA-256 is supported at the moment, but this allows
adding more SASL mechanisms in the future, without changing the overall
protocol.
Support for channel binding, aka SCRAM-SHA-256-PLUS is left for later.
The SASLPrep algorithm, for pre-processing the password, is not yet
implemented. That could cause trouble, if you use a password with
non-ASCII characters, and a client library that does implement SASLprep.
That will hopefully be added later.
Authorization identities, as specified in the SCRAM-SHA-256 specification,
are ignored. SET SESSION AUTHORIZATION provides more or less the same
functionality, anyway.
If a user doesn't exist, perform a "mock" authentication, by constructing
an authentic-looking challenge on the fly. The challenge is derived from
a new system-wide random value, "mock authentication nonce", which is
created at initdb, and stored in the control file. We go through these
motions, in order to not give away the information on whether the user
exists, to unauthenticated users.
Bumps PG_CONTROL_VERSION, because of the new field in control file.
Patch by Michael Paquier and Heikki Linnakangas, reviewed at different
stages by Robert Haas, Stephen Frost, David Steele, Aleksander Alekseev,
and many others.
Discussion: https://www.postgresql.org/message-id/CAB7nPqRbR3GmFYdedCAhzukfKrgBLTLtMvENOmPrVWREsZkF8g%40mail.gmail.com
Discussion: https://www.postgresql.org/message-id/CAB7nPqSMXU35g%3DW9X74HVeQp0uvgJxvYOuA4A-A3M%2B0wfEBv-w%40mail.gmail.com
Discussion: https://www.postgresql.org/message-id/55192AFE.6080106@iki.fi
2017-03-07 13:25:40 +01:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
|
|
|
* src/interfaces/libpq/fe-auth-scram.c
|
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "postgres_fe.h"
|
|
|
|
|
|
|
|
#include "common/base64.h"
|
Use SASLprep to normalize passwords for SCRAM authentication.
An important step of SASLprep normalization, is to convert the string to
Unicode normalization form NFKC. Unicode normalization requires a fairly
large table of character decompositions, which is generated from data
published by the Unicode consortium. The script to generate the table is
put in src/common/unicode, as well test code for the normalization.
A pre-generated version of the tables is included in src/include/common,
so you don't need the code in src/common/unicode to build PostgreSQL, only
if you wish to modify the normalization tables.
The SASLprep implementation depends on the UTF-8 functions from
src/backend/utils/mb/wchar.c. So to use it, you must also compile and link
that. That doesn't change anything for the current users of these
functions, the backend and libpq, as they both already link with wchar.o.
It would be good to move those functions into a separate file in
src/commmon, but I'll leave that for another day.
No documentation changes included, because there is no details on the
SCRAM mechanism in the docs anyway. An overview on that in the protocol
specification would probably be good, even though SCRAM is documented in
detail in RFC5802. I'll write that as a separate patch. An important thing
to mention there is that we apply SASLprep even on invalid UTF-8 strings,
to support other encodings.
Patch by Michael Paquier and me.
Discussion: https://www.postgresql.org/message-id/CAB7nPqSByyEmAVLtEf1KxTRh=PWNKiWKEKQR=e1yGehz=wbymQ@mail.gmail.com
2017-04-07 13:56:05 +02:00
|
|
|
#include "common/saslprep.h"
|
Support SCRAM-SHA-256 authentication (RFC 5802 and 7677).
This introduces a new generic SASL authentication method, similar to the
GSS and SSPI methods. The server first tells the client which SASL
authentication mechanism to use, and then the mechanism-specific SASL
messages are exchanged in AuthenticationSASLcontinue and PasswordMessage
messages. Only SCRAM-SHA-256 is supported at the moment, but this allows
adding more SASL mechanisms in the future, without changing the overall
protocol.
Support for channel binding, aka SCRAM-SHA-256-PLUS is left for later.
The SASLPrep algorithm, for pre-processing the password, is not yet
implemented. That could cause trouble, if you use a password with
non-ASCII characters, and a client library that does implement SASLprep.
That will hopefully be added later.
Authorization identities, as specified in the SCRAM-SHA-256 specification,
are ignored. SET SESSION AUTHORIZATION provides more or less the same
functionality, anyway.
If a user doesn't exist, perform a "mock" authentication, by constructing
an authentic-looking challenge on the fly. The challenge is derived from
a new system-wide random value, "mock authentication nonce", which is
created at initdb, and stored in the control file. We go through these
motions, in order to not give away the information on whether the user
exists, to unauthenticated users.
Bumps PG_CONTROL_VERSION, because of the new field in control file.
Patch by Michael Paquier and Heikki Linnakangas, reviewed at different
stages by Robert Haas, Stephen Frost, David Steele, Aleksander Alekseev,
and many others.
Discussion: https://www.postgresql.org/message-id/CAB7nPqRbR3GmFYdedCAhzukfKrgBLTLtMvENOmPrVWREsZkF8g%40mail.gmail.com
Discussion: https://www.postgresql.org/message-id/CAB7nPqSMXU35g%3DW9X74HVeQp0uvgJxvYOuA4A-A3M%2B0wfEBv-w%40mail.gmail.com
Discussion: https://www.postgresql.org/message-id/55192AFE.6080106@iki.fi
2017-03-07 13:25:40 +01:00
|
|
|
#include "common/scram-common.h"
|
|
|
|
#include "fe-auth.h"
|
|
|
|
|
|
|
|
/* These are needed for getpid(), in the fallback implementation */
|
|
|
|
#ifndef HAVE_STRONG_RANDOM
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Status of exchange messages used for SCRAM authentication via the
|
|
|
|
* SASL protocol.
|
|
|
|
*/
|
|
|
|
typedef enum
|
|
|
|
{
|
|
|
|
FE_SCRAM_INIT,
|
|
|
|
FE_SCRAM_NONCE_SENT,
|
|
|
|
FE_SCRAM_PROOF_SENT,
|
|
|
|
FE_SCRAM_FINISHED
|
|
|
|
} fe_scram_state_enum;
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
fe_scram_state_enum state;
|
|
|
|
|
|
|
|
/* These are supplied by the user */
|
|
|
|
const char *username;
|
Use SASLprep to normalize passwords for SCRAM authentication.
An important step of SASLprep normalization, is to convert the string to
Unicode normalization form NFKC. Unicode normalization requires a fairly
large table of character decompositions, which is generated from data
published by the Unicode consortium. The script to generate the table is
put in src/common/unicode, as well test code for the normalization.
A pre-generated version of the tables is included in src/include/common,
so you don't need the code in src/common/unicode to build PostgreSQL, only
if you wish to modify the normalization tables.
The SASLprep implementation depends on the UTF-8 functions from
src/backend/utils/mb/wchar.c. So to use it, you must also compile and link
that. That doesn't change anything for the current users of these
functions, the backend and libpq, as they both already link with wchar.o.
It would be good to move those functions into a separate file in
src/commmon, but I'll leave that for another day.
No documentation changes included, because there is no details on the
SCRAM mechanism in the docs anyway. An overview on that in the protocol
specification would probably be good, even though SCRAM is documented in
detail in RFC5802. I'll write that as a separate patch. An important thing
to mention there is that we apply SASLprep even on invalid UTF-8 strings,
to support other encodings.
Patch by Michael Paquier and me.
Discussion: https://www.postgresql.org/message-id/CAB7nPqSByyEmAVLtEf1KxTRh=PWNKiWKEKQR=e1yGehz=wbymQ@mail.gmail.com
2017-04-07 13:56:05 +02:00
|
|
|
char *password;
|
Support SCRAM-SHA-256 authentication (RFC 5802 and 7677).
This introduces a new generic SASL authentication method, similar to the
GSS and SSPI methods. The server first tells the client which SASL
authentication mechanism to use, and then the mechanism-specific SASL
messages are exchanged in AuthenticationSASLcontinue and PasswordMessage
messages. Only SCRAM-SHA-256 is supported at the moment, but this allows
adding more SASL mechanisms in the future, without changing the overall
protocol.
Support for channel binding, aka SCRAM-SHA-256-PLUS is left for later.
The SASLPrep algorithm, for pre-processing the password, is not yet
implemented. That could cause trouble, if you use a password with
non-ASCII characters, and a client library that does implement SASLprep.
That will hopefully be added later.
Authorization identities, as specified in the SCRAM-SHA-256 specification,
are ignored. SET SESSION AUTHORIZATION provides more or less the same
functionality, anyway.
If a user doesn't exist, perform a "mock" authentication, by constructing
an authentic-looking challenge on the fly. The challenge is derived from
a new system-wide random value, "mock authentication nonce", which is
created at initdb, and stored in the control file. We go through these
motions, in order to not give away the information on whether the user
exists, to unauthenticated users.
Bumps PG_CONTROL_VERSION, because of the new field in control file.
Patch by Michael Paquier and Heikki Linnakangas, reviewed at different
stages by Robert Haas, Stephen Frost, David Steele, Aleksander Alekseev,
and many others.
Discussion: https://www.postgresql.org/message-id/CAB7nPqRbR3GmFYdedCAhzukfKrgBLTLtMvENOmPrVWREsZkF8g%40mail.gmail.com
Discussion: https://www.postgresql.org/message-id/CAB7nPqSMXU35g%3DW9X74HVeQp0uvgJxvYOuA4A-A3M%2B0wfEBv-w%40mail.gmail.com
Discussion: https://www.postgresql.org/message-id/55192AFE.6080106@iki.fi
2017-03-07 13:25:40 +01:00
|
|
|
|
|
|
|
/* We construct these */
|
|
|
|
char *client_nonce;
|
|
|
|
char *client_first_message_bare;
|
|
|
|
char *client_final_message_without_proof;
|
|
|
|
|
|
|
|
/* These come from the server-first message */
|
|
|
|
char *server_first_message;
|
|
|
|
char *salt;
|
|
|
|
int saltlen;
|
|
|
|
int iterations;
|
|
|
|
char *nonce;
|
|
|
|
|
|
|
|
/* These come from the server-final message */
|
|
|
|
char *server_final_message;
|
|
|
|
char ServerProof[SCRAM_KEY_LEN];
|
|
|
|
} fe_scram_state;
|
|
|
|
|
|
|
|
static bool read_server_first_message(fe_scram_state *state, char *input,
|
|
|
|
PQExpBuffer errormessage);
|
|
|
|
static bool read_server_final_message(fe_scram_state *state, char *input,
|
|
|
|
PQExpBuffer errormessage);
|
|
|
|
static char *build_client_first_message(fe_scram_state *state,
|
|
|
|
PQExpBuffer errormessage);
|
|
|
|
static char *build_client_final_message(fe_scram_state *state,
|
|
|
|
PQExpBuffer errormessage);
|
|
|
|
static bool verify_server_proof(fe_scram_state *state);
|
|
|
|
static void calculate_client_proof(fe_scram_state *state,
|
|
|
|
const char *client_final_message_without_proof,
|
|
|
|
uint8 *result);
|
|
|
|
static bool pg_frontend_random(char *dst, int len);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize SCRAM exchange status.
|
|
|
|
*/
|
|
|
|
void *
|
|
|
|
pg_fe_scram_init(const char *username, const char *password)
|
|
|
|
{
|
|
|
|
fe_scram_state *state;
|
Use SASLprep to normalize passwords for SCRAM authentication.
An important step of SASLprep normalization, is to convert the string to
Unicode normalization form NFKC. Unicode normalization requires a fairly
large table of character decompositions, which is generated from data
published by the Unicode consortium. The script to generate the table is
put in src/common/unicode, as well test code for the normalization.
A pre-generated version of the tables is included in src/include/common,
so you don't need the code in src/common/unicode to build PostgreSQL, only
if you wish to modify the normalization tables.
The SASLprep implementation depends on the UTF-8 functions from
src/backend/utils/mb/wchar.c. So to use it, you must also compile and link
that. That doesn't change anything for the current users of these
functions, the backend and libpq, as they both already link with wchar.o.
It would be good to move those functions into a separate file in
src/commmon, but I'll leave that for another day.
No documentation changes included, because there is no details on the
SCRAM mechanism in the docs anyway. An overview on that in the protocol
specification would probably be good, even though SCRAM is documented in
detail in RFC5802. I'll write that as a separate patch. An important thing
to mention there is that we apply SASLprep even on invalid UTF-8 strings,
to support other encodings.
Patch by Michael Paquier and me.
Discussion: https://www.postgresql.org/message-id/CAB7nPqSByyEmAVLtEf1KxTRh=PWNKiWKEKQR=e1yGehz=wbymQ@mail.gmail.com
2017-04-07 13:56:05 +02:00
|
|
|
char *prep_password;
|
|
|
|
pg_saslprep_rc rc;
|
Support SCRAM-SHA-256 authentication (RFC 5802 and 7677).
This introduces a new generic SASL authentication method, similar to the
GSS and SSPI methods. The server first tells the client which SASL
authentication mechanism to use, and then the mechanism-specific SASL
messages are exchanged in AuthenticationSASLcontinue and PasswordMessage
messages. Only SCRAM-SHA-256 is supported at the moment, but this allows
adding more SASL mechanisms in the future, without changing the overall
protocol.
Support for channel binding, aka SCRAM-SHA-256-PLUS is left for later.
The SASLPrep algorithm, for pre-processing the password, is not yet
implemented. That could cause trouble, if you use a password with
non-ASCII characters, and a client library that does implement SASLprep.
That will hopefully be added later.
Authorization identities, as specified in the SCRAM-SHA-256 specification,
are ignored. SET SESSION AUTHORIZATION provides more or less the same
functionality, anyway.
If a user doesn't exist, perform a "mock" authentication, by constructing
an authentic-looking challenge on the fly. The challenge is derived from
a new system-wide random value, "mock authentication nonce", which is
created at initdb, and stored in the control file. We go through these
motions, in order to not give away the information on whether the user
exists, to unauthenticated users.
Bumps PG_CONTROL_VERSION, because of the new field in control file.
Patch by Michael Paquier and Heikki Linnakangas, reviewed at different
stages by Robert Haas, Stephen Frost, David Steele, Aleksander Alekseev,
and many others.
Discussion: https://www.postgresql.org/message-id/CAB7nPqRbR3GmFYdedCAhzukfKrgBLTLtMvENOmPrVWREsZkF8g%40mail.gmail.com
Discussion: https://www.postgresql.org/message-id/CAB7nPqSMXU35g%3DW9X74HVeQp0uvgJxvYOuA4A-A3M%2B0wfEBv-w%40mail.gmail.com
Discussion: https://www.postgresql.org/message-id/55192AFE.6080106@iki.fi
2017-03-07 13:25:40 +01:00
|
|
|
|
|
|
|
state = (fe_scram_state *) malloc(sizeof(fe_scram_state));
|
|
|
|
if (!state)
|
|
|
|
return NULL;
|
|
|
|
memset(state, 0, sizeof(fe_scram_state));
|
|
|
|
state->state = FE_SCRAM_INIT;
|
|
|
|
state->username = username;
|
Use SASLprep to normalize passwords for SCRAM authentication.
An important step of SASLprep normalization, is to convert the string to
Unicode normalization form NFKC. Unicode normalization requires a fairly
large table of character decompositions, which is generated from data
published by the Unicode consortium. The script to generate the table is
put in src/common/unicode, as well test code for the normalization.
A pre-generated version of the tables is included in src/include/common,
so you don't need the code in src/common/unicode to build PostgreSQL, only
if you wish to modify the normalization tables.
The SASLprep implementation depends on the UTF-8 functions from
src/backend/utils/mb/wchar.c. So to use it, you must also compile and link
that. That doesn't change anything for the current users of these
functions, the backend and libpq, as they both already link with wchar.o.
It would be good to move those functions into a separate file in
src/commmon, but I'll leave that for another day.
No documentation changes included, because there is no details on the
SCRAM mechanism in the docs anyway. An overview on that in the protocol
specification would probably be good, even though SCRAM is documented in
detail in RFC5802. I'll write that as a separate patch. An important thing
to mention there is that we apply SASLprep even on invalid UTF-8 strings,
to support other encodings.
Patch by Michael Paquier and me.
Discussion: https://www.postgresql.org/message-id/CAB7nPqSByyEmAVLtEf1KxTRh=PWNKiWKEKQR=e1yGehz=wbymQ@mail.gmail.com
2017-04-07 13:56:05 +02:00
|
|
|
|
|
|
|
/* Normalize the password with SASLprep, if possible */
|
|
|
|
rc = pg_saslprep(password, &prep_password);
|
|
|
|
if (rc == SASLPREP_OOM)
|
|
|
|
{
|
|
|
|
free(state);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (rc != SASLPREP_SUCCESS)
|
|
|
|
{
|
|
|
|
prep_password = strdup(password);
|
|
|
|
if (!prep_password)
|
|
|
|
{
|
|
|
|
free(state);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
state->password = prep_password;
|
Support SCRAM-SHA-256 authentication (RFC 5802 and 7677).
This introduces a new generic SASL authentication method, similar to the
GSS and SSPI methods. The server first tells the client which SASL
authentication mechanism to use, and then the mechanism-specific SASL
messages are exchanged in AuthenticationSASLcontinue and PasswordMessage
messages. Only SCRAM-SHA-256 is supported at the moment, but this allows
adding more SASL mechanisms in the future, without changing the overall
protocol.
Support for channel binding, aka SCRAM-SHA-256-PLUS is left for later.
The SASLPrep algorithm, for pre-processing the password, is not yet
implemented. That could cause trouble, if you use a password with
non-ASCII characters, and a client library that does implement SASLprep.
That will hopefully be added later.
Authorization identities, as specified in the SCRAM-SHA-256 specification,
are ignored. SET SESSION AUTHORIZATION provides more or less the same
functionality, anyway.
If a user doesn't exist, perform a "mock" authentication, by constructing
an authentic-looking challenge on the fly. The challenge is derived from
a new system-wide random value, "mock authentication nonce", which is
created at initdb, and stored in the control file. We go through these
motions, in order to not give away the information on whether the user
exists, to unauthenticated users.
Bumps PG_CONTROL_VERSION, because of the new field in control file.
Patch by Michael Paquier and Heikki Linnakangas, reviewed at different
stages by Robert Haas, Stephen Frost, David Steele, Aleksander Alekseev,
and many others.
Discussion: https://www.postgresql.org/message-id/CAB7nPqRbR3GmFYdedCAhzukfKrgBLTLtMvENOmPrVWREsZkF8g%40mail.gmail.com
Discussion: https://www.postgresql.org/message-id/CAB7nPqSMXU35g%3DW9X74HVeQp0uvgJxvYOuA4A-A3M%2B0wfEBv-w%40mail.gmail.com
Discussion: https://www.postgresql.org/message-id/55192AFE.6080106@iki.fi
2017-03-07 13:25:40 +01:00
|
|
|
|
|
|
|
return state;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Free SCRAM exchange status
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
pg_fe_scram_free(void *opaq)
|
|
|
|
{
|
|
|
|
fe_scram_state *state = (fe_scram_state *) opaq;
|
|
|
|
|
Use SASLprep to normalize passwords for SCRAM authentication.
An important step of SASLprep normalization, is to convert the string to
Unicode normalization form NFKC. Unicode normalization requires a fairly
large table of character decompositions, which is generated from data
published by the Unicode consortium. The script to generate the table is
put in src/common/unicode, as well test code for the normalization.
A pre-generated version of the tables is included in src/include/common,
so you don't need the code in src/common/unicode to build PostgreSQL, only
if you wish to modify the normalization tables.
The SASLprep implementation depends on the UTF-8 functions from
src/backend/utils/mb/wchar.c. So to use it, you must also compile and link
that. That doesn't change anything for the current users of these
functions, the backend and libpq, as they both already link with wchar.o.
It would be good to move those functions into a separate file in
src/commmon, but I'll leave that for another day.
No documentation changes included, because there is no details on the
SCRAM mechanism in the docs anyway. An overview on that in the protocol
specification would probably be good, even though SCRAM is documented in
detail in RFC5802. I'll write that as a separate patch. An important thing
to mention there is that we apply SASLprep even on invalid UTF-8 strings,
to support other encodings.
Patch by Michael Paquier and me.
Discussion: https://www.postgresql.org/message-id/CAB7nPqSByyEmAVLtEf1KxTRh=PWNKiWKEKQR=e1yGehz=wbymQ@mail.gmail.com
2017-04-07 13:56:05 +02:00
|
|
|
if (state->password)
|
|
|
|
free(state->password);
|
|
|
|
|
Support SCRAM-SHA-256 authentication (RFC 5802 and 7677).
This introduces a new generic SASL authentication method, similar to the
GSS and SSPI methods. The server first tells the client which SASL
authentication mechanism to use, and then the mechanism-specific SASL
messages are exchanged in AuthenticationSASLcontinue and PasswordMessage
messages. Only SCRAM-SHA-256 is supported at the moment, but this allows
adding more SASL mechanisms in the future, without changing the overall
protocol.
Support for channel binding, aka SCRAM-SHA-256-PLUS is left for later.
The SASLPrep algorithm, for pre-processing the password, is not yet
implemented. That could cause trouble, if you use a password with
non-ASCII characters, and a client library that does implement SASLprep.
That will hopefully be added later.
Authorization identities, as specified in the SCRAM-SHA-256 specification,
are ignored. SET SESSION AUTHORIZATION provides more or less the same
functionality, anyway.
If a user doesn't exist, perform a "mock" authentication, by constructing
an authentic-looking challenge on the fly. The challenge is derived from
a new system-wide random value, "mock authentication nonce", which is
created at initdb, and stored in the control file. We go through these
motions, in order to not give away the information on whether the user
exists, to unauthenticated users.
Bumps PG_CONTROL_VERSION, because of the new field in control file.
Patch by Michael Paquier and Heikki Linnakangas, reviewed at different
stages by Robert Haas, Stephen Frost, David Steele, Aleksander Alekseev,
and many others.
Discussion: https://www.postgresql.org/message-id/CAB7nPqRbR3GmFYdedCAhzukfKrgBLTLtMvENOmPrVWREsZkF8g%40mail.gmail.com
Discussion: https://www.postgresql.org/message-id/CAB7nPqSMXU35g%3DW9X74HVeQp0uvgJxvYOuA4A-A3M%2B0wfEBv-w%40mail.gmail.com
Discussion: https://www.postgresql.org/message-id/55192AFE.6080106@iki.fi
2017-03-07 13:25:40 +01:00
|
|
|
/* client messages */
|
|
|
|
if (state->client_nonce)
|
|
|
|
free(state->client_nonce);
|
|
|
|
if (state->client_first_message_bare)
|
|
|
|
free(state->client_first_message_bare);
|
|
|
|
if (state->client_final_message_without_proof)
|
|
|
|
free(state->client_final_message_without_proof);
|
|
|
|
|
|
|
|
/* first message from server */
|
|
|
|
if (state->server_first_message)
|
|
|
|
free(state->server_first_message);
|
|
|
|
if (state->salt)
|
|
|
|
free(state->salt);
|
|
|
|
if (state->nonce)
|
|
|
|
free(state->nonce);
|
|
|
|
|
|
|
|
/* final message from server */
|
|
|
|
if (state->server_final_message)
|
|
|
|
free(state->server_final_message);
|
|
|
|
|
|
|
|
free(state);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Exchange a SCRAM message with backend.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
pg_fe_scram_exchange(void *opaq, char *input, int inputlen,
|
|
|
|
char **output, int *outputlen,
|
|
|
|
bool *done, bool *success, PQExpBuffer errorMessage)
|
|
|
|
{
|
|
|
|
fe_scram_state *state = (fe_scram_state *) opaq;
|
|
|
|
|
|
|
|
*done = false;
|
|
|
|
*success = false;
|
|
|
|
*output = NULL;
|
|
|
|
*outputlen = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check that the input length agrees with the string length of the input.
|
|
|
|
* We can ignore inputlen after this.
|
|
|
|
*/
|
|
|
|
if (state->state != FE_SCRAM_INIT)
|
|
|
|
{
|
|
|
|
if (inputlen == 0)
|
|
|
|
{
|
|
|
|
printfPQExpBuffer(errorMessage,
|
|
|
|
libpq_gettext("malformed SCRAM message (empty message)\n"));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
if (inputlen != strlen(input))
|
|
|
|
{
|
|
|
|
printfPQExpBuffer(errorMessage,
|
|
|
|
libpq_gettext("malformed SCRAM message (length mismatch)\n"));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (state->state)
|
|
|
|
{
|
|
|
|
case FE_SCRAM_INIT:
|
|
|
|
/* Begin the SCRAM handshake, by sending client nonce */
|
|
|
|
*output = build_client_first_message(state, errorMessage);
|
|
|
|
if (*output == NULL)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
*outputlen = strlen(*output);
|
|
|
|
*done = false;
|
|
|
|
state->state = FE_SCRAM_NONCE_SENT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case FE_SCRAM_NONCE_SENT:
|
|
|
|
/* Receive salt and server nonce, send response. */
|
|
|
|
if (!read_server_first_message(state, input, errorMessage))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
*output = build_client_final_message(state, errorMessage);
|
|
|
|
if (*output == NULL)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
*outputlen = strlen(*output);
|
|
|
|
*done = false;
|
|
|
|
state->state = FE_SCRAM_PROOF_SENT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case FE_SCRAM_PROOF_SENT:
|
|
|
|
/* Receive server proof */
|
|
|
|
if (!read_server_final_message(state, input, errorMessage))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Verify server proof, to make sure we're talking to the genuine
|
|
|
|
* server. XXX: A fake server could simply not require
|
|
|
|
* authentication, though. There is currently no option in libpq
|
|
|
|
* to reject a connection, if SCRAM authentication did not happen.
|
|
|
|
*/
|
|
|
|
if (verify_server_proof(state))
|
|
|
|
*success = true;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*success = false;
|
|
|
|
printfPQExpBuffer(errorMessage,
|
|
|
|
libpq_gettext("invalid server proof\n"));
|
|
|
|
}
|
|
|
|
*done = true;
|
|
|
|
state->state = FE_SCRAM_FINISHED;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
/* shouldn't happen */
|
|
|
|
printfPQExpBuffer(errorMessage,
|
|
|
|
libpq_gettext("invalid SCRAM exchange state\n"));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
|
|
|
error:
|
|
|
|
*done = true;
|
|
|
|
*success = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read value for an attribute part of a SASL message.
|
|
|
|
*/
|
|
|
|
static char *
|
|
|
|
read_attr_value(char **input, char attr, PQExpBuffer errorMessage)
|
|
|
|
{
|
|
|
|
char *begin = *input;
|
|
|
|
char *end;
|
|
|
|
|
|
|
|
if (*begin != attr)
|
|
|
|
{
|
|
|
|
printfPQExpBuffer(errorMessage,
|
|
|
|
libpq_gettext("malformed SCRAM message (%c expected)\n"),
|
|
|
|
attr);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
begin++;
|
|
|
|
|
|
|
|
if (*begin != '=')
|
|
|
|
{
|
|
|
|
printfPQExpBuffer(errorMessage,
|
|
|
|
libpq_gettext("malformed SCRAM message (expected = in attr '%c')\n"),
|
|
|
|
attr);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
begin++;
|
|
|
|
|
|
|
|
end = begin;
|
|
|
|
while (*end && *end != ',')
|
|
|
|
end++;
|
|
|
|
|
|
|
|
if (*end)
|
|
|
|
{
|
|
|
|
*end = '\0';
|
|
|
|
*input = end + 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
*input = end;
|
|
|
|
|
|
|
|
return begin;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Build the first exchange message sent by the client.
|
|
|
|
*/
|
|
|
|
static char *
|
|
|
|
build_client_first_message(fe_scram_state *state, PQExpBuffer errormessage)
|
|
|
|
{
|
|
|
|
char raw_nonce[SCRAM_RAW_NONCE_LEN + 1];
|
|
|
|
char *buf;
|
|
|
|
char buflen;
|
|
|
|
int encoded_len;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Generate a "raw" nonce. This is converted to ASCII-printable form by
|
|
|
|
* base64-encoding it.
|
|
|
|
*/
|
|
|
|
if (!pg_frontend_random(raw_nonce, SCRAM_RAW_NONCE_LEN))
|
|
|
|
{
|
|
|
|
printfPQExpBuffer(errormessage,
|
|
|
|
libpq_gettext("failed to generate nonce\n"));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
state->client_nonce = malloc(pg_b64_enc_len(SCRAM_RAW_NONCE_LEN) + 1);
|
|
|
|
if (state->client_nonce == NULL)
|
|
|
|
{
|
|
|
|
printfPQExpBuffer(errormessage,
|
|
|
|
libpq_gettext("out of memory\n"));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
encoded_len = pg_b64_encode(raw_nonce, SCRAM_RAW_NONCE_LEN, state->client_nonce);
|
|
|
|
state->client_nonce[encoded_len] = '\0';
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Generate message. The username is left empty as the backend uses the
|
|
|
|
* value provided by the startup packet. Also, as this username is not
|
|
|
|
* prepared with SASLprep, the message parsing would fail if it includes
|
|
|
|
* '=' or ',' characters.
|
|
|
|
*/
|
|
|
|
buflen = 8 + strlen(state->client_nonce) + 1;
|
|
|
|
buf = malloc(buflen);
|
|
|
|
if (buf == NULL)
|
|
|
|
{
|
|
|
|
printfPQExpBuffer(errormessage,
|
|
|
|
libpq_gettext("out of memory\n"));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
snprintf(buf, buflen, "n,,n=,r=%s", state->client_nonce);
|
|
|
|
|
|
|
|
state->client_first_message_bare = strdup(buf + 3);
|
|
|
|
if (!state->client_first_message_bare)
|
|
|
|
{
|
|
|
|
free(buf);
|
|
|
|
printfPQExpBuffer(errormessage,
|
|
|
|
libpq_gettext("out of memory\n"));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Build the final exchange message sent from the client.
|
|
|
|
*/
|
|
|
|
static char *
|
|
|
|
build_client_final_message(fe_scram_state *state, PQExpBuffer errormessage)
|
|
|
|
{
|
|
|
|
PQExpBufferData buf;
|
|
|
|
uint8 client_proof[SCRAM_KEY_LEN];
|
|
|
|
char *result;
|
|
|
|
|
|
|
|
initPQExpBuffer(&buf);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Construct client-final-message-without-proof. We need to remember it
|
|
|
|
* for verifying the server proof in the final step of authentication.
|
|
|
|
*/
|
|
|
|
appendPQExpBuffer(&buf, "c=biws,r=%s", state->nonce);
|
|
|
|
if (PQExpBufferDataBroken(buf))
|
|
|
|
goto oom_error;
|
|
|
|
|
|
|
|
state->client_final_message_without_proof = strdup(buf.data);
|
|
|
|
if (state->client_final_message_without_proof == NULL)
|
|
|
|
goto oom_error;
|
|
|
|
|
|
|
|
/* Append proof to it, to form client-final-message. */
|
|
|
|
calculate_client_proof(state,
|
|
|
|
state->client_final_message_without_proof,
|
|
|
|
client_proof);
|
|
|
|
|
|
|
|
appendPQExpBuffer(&buf, ",p=");
|
|
|
|
if (!enlargePQExpBuffer(&buf, pg_b64_enc_len(SCRAM_KEY_LEN)))
|
|
|
|
goto oom_error;
|
|
|
|
buf.len += pg_b64_encode((char *) client_proof,
|
|
|
|
SCRAM_KEY_LEN,
|
|
|
|
buf.data + buf.len);
|
|
|
|
buf.data[buf.len] = '\0';
|
|
|
|
|
|
|
|
result = strdup(buf.data);
|
|
|
|
if (result == NULL)
|
|
|
|
goto oom_error;
|
|
|
|
|
|
|
|
termPQExpBuffer(&buf);
|
|
|
|
return result;
|
|
|
|
|
|
|
|
oom_error:
|
|
|
|
termPQExpBuffer(&buf);
|
|
|
|
printfPQExpBuffer(errormessage,
|
|
|
|
libpq_gettext("out of memory\n"));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read the first exchange message coming from the server.
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
read_server_first_message(fe_scram_state *state, char *input,
|
|
|
|
PQExpBuffer errormessage)
|
|
|
|
{
|
|
|
|
char *iterations_str;
|
|
|
|
char *endptr;
|
|
|
|
char *encoded_salt;
|
|
|
|
char *nonce;
|
|
|
|
|
|
|
|
state->server_first_message = strdup(input);
|
|
|
|
if (state->server_first_message == NULL)
|
|
|
|
{
|
|
|
|
printfPQExpBuffer(errormessage,
|
|
|
|
libpq_gettext("out of memory\n"));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* parse the message */
|
|
|
|
nonce = read_attr_value(&input, 'r', errormessage);
|
|
|
|
if (nonce == NULL)
|
|
|
|
{
|
|
|
|
/* read_attr_value() has generated an error string */
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Verify immediately that the server used our part of the nonce */
|
|
|
|
if (strncmp(nonce, state->client_nonce, strlen(state->client_nonce)) != 0)
|
|
|
|
{
|
|
|
|
printfPQExpBuffer(errormessage,
|
|
|
|
libpq_gettext("invalid SCRAM response (nonce mismatch)\n"));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
state->nonce = strdup(nonce);
|
|
|
|
if (state->nonce == NULL)
|
|
|
|
{
|
|
|
|
printfPQExpBuffer(errormessage,
|
|
|
|
libpq_gettext("out of memory\n"));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
encoded_salt = read_attr_value(&input, 's', errormessage);
|
|
|
|
if (encoded_salt == NULL)
|
|
|
|
{
|
|
|
|
/* read_attr_value() has generated an error string */
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
state->salt = malloc(pg_b64_dec_len(strlen(encoded_salt)));
|
|
|
|
if (state->salt == NULL)
|
|
|
|
{
|
|
|
|
printfPQExpBuffer(errormessage,
|
|
|
|
libpq_gettext("out of memory\n"));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
state->saltlen = pg_b64_decode(encoded_salt,
|
|
|
|
strlen(encoded_salt),
|
|
|
|
state->salt);
|
|
|
|
|
|
|
|
iterations_str = read_attr_value(&input, 'i', errormessage);
|
|
|
|
if (iterations_str == NULL)
|
|
|
|
{
|
|
|
|
/* read_attr_value() has generated an error string */
|
|
|
|
return false;
|
|
|
|
}
|
2017-04-06 16:41:48 +02:00
|
|
|
state->iterations = strtol(iterations_str, &endptr, 10);
|
Support SCRAM-SHA-256 authentication (RFC 5802 and 7677).
This introduces a new generic SASL authentication method, similar to the
GSS and SSPI methods. The server first tells the client which SASL
authentication mechanism to use, and then the mechanism-specific SASL
messages are exchanged in AuthenticationSASLcontinue and PasswordMessage
messages. Only SCRAM-SHA-256 is supported at the moment, but this allows
adding more SASL mechanisms in the future, without changing the overall
protocol.
Support for channel binding, aka SCRAM-SHA-256-PLUS is left for later.
The SASLPrep algorithm, for pre-processing the password, is not yet
implemented. That could cause trouble, if you use a password with
non-ASCII characters, and a client library that does implement SASLprep.
That will hopefully be added later.
Authorization identities, as specified in the SCRAM-SHA-256 specification,
are ignored. SET SESSION AUTHORIZATION provides more or less the same
functionality, anyway.
If a user doesn't exist, perform a "mock" authentication, by constructing
an authentic-looking challenge on the fly. The challenge is derived from
a new system-wide random value, "mock authentication nonce", which is
created at initdb, and stored in the control file. We go through these
motions, in order to not give away the information on whether the user
exists, to unauthenticated users.
Bumps PG_CONTROL_VERSION, because of the new field in control file.
Patch by Michael Paquier and Heikki Linnakangas, reviewed at different
stages by Robert Haas, Stephen Frost, David Steele, Aleksander Alekseev,
and many others.
Discussion: https://www.postgresql.org/message-id/CAB7nPqRbR3GmFYdedCAhzukfKrgBLTLtMvENOmPrVWREsZkF8g%40mail.gmail.com
Discussion: https://www.postgresql.org/message-id/CAB7nPqSMXU35g%3DW9X74HVeQp0uvgJxvYOuA4A-A3M%2B0wfEBv-w%40mail.gmail.com
Discussion: https://www.postgresql.org/message-id/55192AFE.6080106@iki.fi
2017-03-07 13:25:40 +01:00
|
|
|
if (*endptr != '\0' || state->iterations < 1)
|
|
|
|
{
|
|
|
|
printfPQExpBuffer(errormessage,
|
|
|
|
libpq_gettext("malformed SCRAM message (invalid iteration count)\n"));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*input != '\0')
|
|
|
|
printfPQExpBuffer(errormessage,
|
|
|
|
libpq_gettext("malformed SCRAM message (garbage at end of server-first-message)\n"));
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read the final exchange message coming from the server.
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
read_server_final_message(fe_scram_state *state,
|
|
|
|
char *input,
|
|
|
|
PQExpBuffer errormessage)
|
|
|
|
{
|
|
|
|
char *encoded_server_proof;
|
|
|
|
int server_proof_len;
|
|
|
|
|
|
|
|
state->server_final_message = strdup(input);
|
|
|
|
if (!state->server_final_message)
|
|
|
|
{
|
|
|
|
printfPQExpBuffer(errormessage,
|
|
|
|
libpq_gettext("out of memory\n"));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check for error result. */
|
|
|
|
if (*input == 'e')
|
|
|
|
{
|
|
|
|
char *errmsg = read_attr_value(&input, 'e', errormessage);
|
|
|
|
|
|
|
|
printfPQExpBuffer(errormessage,
|
|
|
|
libpq_gettext("error received from server in SASL exchange: %s\n"),
|
|
|
|
errmsg);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Parse the message. */
|
|
|
|
encoded_server_proof = read_attr_value(&input, 'v', errormessage);
|
|
|
|
if (encoded_server_proof == NULL)
|
|
|
|
{
|
|
|
|
/* read_attr_value() has generated an error message */
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*input != '\0')
|
|
|
|
printfPQExpBuffer(errormessage,
|
|
|
|
libpq_gettext("malformed SCRAM message (garbage at end of server-final-message)\n"));
|
|
|
|
|
|
|
|
server_proof_len = pg_b64_decode(encoded_server_proof,
|
|
|
|
strlen(encoded_server_proof),
|
|
|
|
state->ServerProof);
|
|
|
|
if (server_proof_len != SCRAM_KEY_LEN)
|
|
|
|
{
|
|
|
|
printfPQExpBuffer(errormessage,
|
|
|
|
libpq_gettext("malformed SCRAM message (invalid server proof)\n"));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Calculate the client proof, part of the final exchange message sent
|
|
|
|
* by the client.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
calculate_client_proof(fe_scram_state *state,
|
|
|
|
const char *client_final_message_without_proof,
|
|
|
|
uint8 *result)
|
|
|
|
{
|
|
|
|
uint8 StoredKey[SCRAM_KEY_LEN];
|
|
|
|
uint8 ClientKey[SCRAM_KEY_LEN];
|
|
|
|
uint8 ClientSignature[SCRAM_KEY_LEN];
|
|
|
|
int i;
|
|
|
|
scram_HMAC_ctx ctx;
|
|
|
|
|
|
|
|
scram_ClientOrServerKey(state->password, state->salt, state->saltlen,
|
|
|
|
state->iterations, SCRAM_CLIENT_KEY_NAME, ClientKey);
|
|
|
|
scram_H(ClientKey, SCRAM_KEY_LEN, StoredKey);
|
|
|
|
|
|
|
|
scram_HMAC_init(&ctx, StoredKey, SCRAM_KEY_LEN);
|
|
|
|
scram_HMAC_update(&ctx,
|
|
|
|
state->client_first_message_bare,
|
|
|
|
strlen(state->client_first_message_bare));
|
|
|
|
scram_HMAC_update(&ctx, ",", 1);
|
|
|
|
scram_HMAC_update(&ctx,
|
|
|
|
state->server_first_message,
|
|
|
|
strlen(state->server_first_message));
|
|
|
|
scram_HMAC_update(&ctx, ",", 1);
|
|
|
|
scram_HMAC_update(&ctx,
|
|
|
|
client_final_message_without_proof,
|
|
|
|
strlen(client_final_message_without_proof));
|
|
|
|
scram_HMAC_final(ClientSignature, &ctx);
|
|
|
|
|
|
|
|
for (i = 0; i < SCRAM_KEY_LEN; i++)
|
|
|
|
result[i] = ClientKey[i] ^ ClientSignature[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Validate the server proof, received as part of the final exchange message
|
|
|
|
* received from the server.
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
verify_server_proof(fe_scram_state *state)
|
|
|
|
{
|
|
|
|
uint8 ServerSignature[SCRAM_KEY_LEN];
|
|
|
|
uint8 ServerKey[SCRAM_KEY_LEN];
|
|
|
|
scram_HMAC_ctx ctx;
|
|
|
|
|
|
|
|
scram_ClientOrServerKey(state->password, state->salt, state->saltlen,
|
|
|
|
state->iterations, SCRAM_SERVER_KEY_NAME,
|
|
|
|
ServerKey);
|
|
|
|
|
|
|
|
/* calculate ServerSignature */
|
|
|
|
scram_HMAC_init(&ctx, ServerKey, SCRAM_KEY_LEN);
|
|
|
|
scram_HMAC_update(&ctx,
|
|
|
|
state->client_first_message_bare,
|
|
|
|
strlen(state->client_first_message_bare));
|
|
|
|
scram_HMAC_update(&ctx, ",", 1);
|
|
|
|
scram_HMAC_update(&ctx,
|
|
|
|
state->server_first_message,
|
|
|
|
strlen(state->server_first_message));
|
|
|
|
scram_HMAC_update(&ctx, ",", 1);
|
|
|
|
scram_HMAC_update(&ctx,
|
|
|
|
state->client_final_message_without_proof,
|
|
|
|
strlen(state->client_final_message_without_proof));
|
|
|
|
scram_HMAC_final(ServerSignature, &ctx);
|
|
|
|
|
|
|
|
if (memcmp(ServerSignature, state->ServerProof, SCRAM_KEY_LEN) != 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Random number generator.
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
pg_frontend_random(char *dst, int len)
|
|
|
|
{
|
|
|
|
#ifdef HAVE_STRONG_RANDOM
|
|
|
|
return pg_strong_random(dst, len);
|
|
|
|
#else
|
|
|
|
int i;
|
|
|
|
char *end = dst + len;
|
|
|
|
|
|
|
|
static unsigned short seed[3];
|
|
|
|
static int mypid = 0;
|
|
|
|
|
|
|
|
pglock_thread();
|
|
|
|
|
|
|
|
if (mypid != getpid())
|
|
|
|
{
|
|
|
|
struct timeval now;
|
|
|
|
|
|
|
|
gettimeofday(&now, NULL);
|
|
|
|
|
|
|
|
seed[0] = now.tv_sec ^ getpid();
|
|
|
|
seed[1] = (unsigned short) (now.tv_usec);
|
|
|
|
seed[2] = (unsigned short) (now.tv_usec >> 16);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; dst < end; i++)
|
|
|
|
{
|
|
|
|
uint32 r;
|
|
|
|
int j;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* pg_jrand48 returns a 32-bit integer. Fill the next 4 bytes from
|
|
|
|
* it.
|
|
|
|
*/
|
|
|
|
r = (uint32) pg_jrand48(seed);
|
|
|
|
|
|
|
|
for (j = 0; j < 4 && dst < end; j++)
|
|
|
|
{
|
|
|
|
*(dst++) = (char) (r & 0xFF);
|
|
|
|
r >>= 8;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pgunlock_thread();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
#endif
|
|
|
|
}
|