diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/libpq/auth-scram.c | 14 | ||||
-rw-r--r-- | src/include/libpq/libpq-be.h | 9 | ||||
-rw-r--r-- | src/interfaces/libpq/fe-auth-scram.c | 83 | ||||
-rw-r--r-- | src/interfaces/libpq/fe-auth.c | 2 | ||||
-rw-r--r-- | src/interfaces/libpq/fe-connect.c | 47 | ||||
-rw-r--r-- | src/interfaces/libpq/libpq-int.h | 6 |
6 files changed, 126 insertions, 35 deletions
diff --git a/src/backend/libpq/auth-scram.c b/src/backend/libpq/auth-scram.c index 1514133acdc..26dd241efa9 100644 --- a/src/backend/libpq/auth-scram.c +++ b/src/backend/libpq/auth-scram.c @@ -101,6 +101,7 @@ #include "libpq/crypt.h" #include "libpq/sasl.h" #include "libpq/scram.h" +#include "miscadmin.h" static void scram_get_mechanisms(Port *port, StringInfo buf); static void *scram_init(Port *port, const char *selected_mech, @@ -144,6 +145,7 @@ typedef struct int iterations; char *salt; /* base64-encoded */ + uint8 ClientKey[SCRAM_MAX_KEY_LEN]; uint8 StoredKey[SCRAM_MAX_KEY_LEN]; uint8 ServerKey[SCRAM_MAX_KEY_LEN]; @@ -462,6 +464,13 @@ scram_exchange(void *opaq, const char *input, int inputlen, if (*output) *outputlen = strlen(*output); + if (result == PG_SASL_EXCHANGE_SUCCESS && state->state == SCRAM_AUTH_FINISHED) + { + memcpy(MyProcPort->scram_ClientKey, state->ClientKey, sizeof(MyProcPort->scram_ClientKey)); + memcpy(MyProcPort->scram_ServerKey, state->ServerKey, sizeof(MyProcPort->scram_ServerKey)); + MyProcPort->has_scram_keys = true; + } + return result; } @@ -1140,7 +1149,6 @@ static bool verify_client_proof(scram_state *state) { uint8 ClientSignature[SCRAM_MAX_KEY_LEN]; - uint8 ClientKey[SCRAM_MAX_KEY_LEN]; uint8 client_StoredKey[SCRAM_MAX_KEY_LEN]; pg_hmac_ctx *ctx = pg_hmac_create(state->hash_type); int i; @@ -1173,10 +1181,10 @@ verify_client_proof(scram_state *state) /* Extract the ClientKey that the client calculated from the proof */ for (i = 0; i < state->key_length; i++) - ClientKey[i] = state->ClientProof[i] ^ ClientSignature[i]; + state->ClientKey[i] = state->ClientProof[i] ^ ClientSignature[i]; /* Hash it one more time, and compare with StoredKey */ - if (scram_H(ClientKey, state->hash_type, state->key_length, + if (scram_H(state->ClientKey, state->hash_type, state->key_length, client_StoredKey, &errstr) < 0) elog(ERROR, "could not hash stored key: %s", errstr); diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h index 16da6f89ef1..2f6c29200ba 100644 --- a/src/include/libpq/libpq-be.h +++ b/src/include/libpq/libpq-be.h @@ -18,6 +18,8 @@ #ifndef LIBPQ_BE_H #define LIBPQ_BE_H +#include "common/scram-common.h" + #include <sys/time.h> #ifdef USE_OPENSSL #include <openssl/ssl.h> @@ -182,6 +184,13 @@ typedef struct Port int tcp_user_timeout; /* + * SCRAM structures. + */ + uint8 scram_ClientKey[SCRAM_MAX_KEY_LEN]; + uint8 scram_ServerKey[SCRAM_MAX_KEY_LEN]; + bool has_scram_keys; /* true if the above two are valid */ + + /* * GSSAPI structures. */ #if defined(ENABLE_GSS) || defined(ENABLE_SSPI) diff --git a/src/interfaces/libpq/fe-auth-scram.c b/src/interfaces/libpq/fe-auth-scram.c index 59bf87d2213..557e9c568b6 100644 --- a/src/interfaces/libpq/fe-auth-scram.c +++ b/src/interfaces/libpq/fe-auth-scram.c @@ -119,25 +119,28 @@ scram_init(PGconn *conn, return NULL; } - /* Normalize the password with SASLprep, if possible */ - rc = pg_saslprep(password, &prep_password); - if (rc == SASLPREP_OOM) - { - free(state->sasl_mechanism); - free(state); - return NULL; - } - if (rc != SASLPREP_SUCCESS) + if (password) { - prep_password = strdup(password); - if (!prep_password) + /* Normalize the password with SASLprep, if possible */ + rc = pg_saslprep(password, &prep_password); + if (rc == SASLPREP_OOM) { free(state->sasl_mechanism); free(state); return NULL; } + if (rc != SASLPREP_SUCCESS) + { + prep_password = strdup(password); + if (!prep_password) + { + free(state->sasl_mechanism); + free(state); + return NULL; + } + } + state->password = prep_password; } - state->password = prep_password; return state; } @@ -775,20 +778,31 @@ calculate_client_proof(fe_scram_state *state, return false; } - /* - * Calculate SaltedPassword, and store it in 'state' so that we can reuse - * it later in verify_server_signature. - */ - if (scram_SaltedPassword(state->password, state->hash_type, - state->key_length, state->salt, state->saltlen, - state->iterations, state->SaltedPassword, - errstr) < 0 || - scram_ClientKey(state->SaltedPassword, state->hash_type, - state->key_length, ClientKey, errstr) < 0 || - scram_H(ClientKey, state->hash_type, state->key_length, - StoredKey, errstr) < 0) - { - /* errstr is already filled here */ + if (state->conn->scram_client_key_binary) + { + memcpy(ClientKey, state->conn->scram_client_key_binary, SCRAM_MAX_KEY_LEN); + } + else + { + /* + * Calculate SaltedPassword, and store it in 'state' so that we can + * reuse it later in verify_server_signature. + */ + if (scram_SaltedPassword(state->password, state->hash_type, + state->key_length, state->salt, state->saltlen, + state->iterations, state->SaltedPassword, + errstr) < 0 || + scram_ClientKey(state->SaltedPassword, state->hash_type, + state->key_length, ClientKey, errstr) < 0) + { + /* errstr is already filled here */ + pg_hmac_free(ctx); + return false; + } + } + + if (scram_H(ClientKey, state->hash_type, state->key_length, StoredKey, errstr) < 0) + { pg_hmac_free(ctx); return false; } @@ -841,12 +855,19 @@ verify_server_signature(fe_scram_state *state, bool *match, return false; } - if (scram_ServerKey(state->SaltedPassword, state->hash_type, - state->key_length, ServerKey, errstr) < 0) + if (state->conn->scram_server_key_binary) { - /* errstr is filled already */ - pg_hmac_free(ctx); - return false; + memcpy(ServerKey, state->conn->scram_server_key_binary, SCRAM_MAX_KEY_LEN); + } + else + { + if (scram_ServerKey(state->SaltedPassword, state->hash_type, + state->key_length, ServerKey, errstr) < 0) + { + /* errstr is filled already */ + pg_hmac_free(ctx); + return false; + } } /* calculate ServerSignature */ diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c index 14a9a862f51..7e478489b71 100644 --- a/src/interfaces/libpq/fe-auth.c +++ b/src/interfaces/libpq/fe-auth.c @@ -559,7 +559,7 @@ pg_SASL_init(PGconn *conn, int payloadlen) * First, select the password to use for the exchange, complaining if * there isn't one and the selected SASL mechanism needs it. */ - if (conn->password_needed) + if (conn->password_needed && !conn->scram_client_key_binary) { password = conn->connhost[conn->whichhost].password; if (password == NULL) diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index 8f211821eb2..c7943d549e8 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -22,6 +22,7 @@ #include <time.h> #include <unistd.h> +#include "common/base64.h" #include "common/ip.h" #include "common/link-canary.h" #include "common/scram-common.h" @@ -366,6 +367,12 @@ static const internalPQconninfoOption PQconninfoOptions[] = { "Load-Balance-Hosts", "", 8, /* sizeof("disable") = 8 */ offsetof(struct pg_conn, load_balance_hosts)}, + {"scram_client_key", NULL, NULL, NULL, "SCRAM-Client-Key", "D", SCRAM_MAX_KEY_LEN * 2, + offsetof(struct pg_conn, scram_client_key)}, + + {"scram_server_key", NULL, NULL, NULL, "SCRAM-Server-Key", "D", SCRAM_MAX_KEY_LEN * 2, + offsetof(struct pg_conn, scram_server_key)}, + /* Terminating entry --- MUST BE LAST */ {NULL, NULL, NULL, NULL, NULL, NULL, 0} @@ -1793,6 +1800,44 @@ pqConnectOptions2(PGconn *conn) else conn->target_server_type = SERVER_TYPE_ANY; + if (conn->scram_client_key) + { + int len; + + len = pg_b64_dec_len(strlen(conn->scram_client_key)); + /* Consider the zero-terminator */ + if (len != SCRAM_MAX_KEY_LEN + 1) + { + libpq_append_conn_error(conn, "invalid SCRAM client key length: %d", len); + return false; + } + conn->scram_client_key_len = len; + conn->scram_client_key_binary = malloc(len); + if (!conn->scram_client_key_binary) + goto oom_error; + pg_b64_decode(conn->scram_client_key, strlen(conn->scram_client_key), + conn->scram_client_key_binary, len); + } + + if (conn->scram_server_key) + { + int len; + + len = pg_b64_dec_len(strlen(conn->scram_server_key)); + /* Consider the zero-terminator */ + if (len != SCRAM_MAX_KEY_LEN + 1) + { + libpq_append_conn_error(conn, "invalid SCRAM server key length: %d", len); + return false; + } + conn->scram_server_key_len = len; + conn->scram_server_key_binary = malloc(len); + if (!conn->scram_server_key_binary) + goto oom_error; + pg_b64_decode(conn->scram_server_key, strlen(conn->scram_server_key), + conn->scram_server_key_binary, len); + } + /* * validate load_balance_hosts option, and set load_balance_type */ @@ -4704,6 +4749,8 @@ freePGconn(PGconn *conn) free(conn->rowBuf); free(conn->target_session_attrs); free(conn->load_balance_hosts); + free(conn->scram_client_key); + free(conn->scram_server_key); termPQExpBuffer(&conn->errorMessage); termPQExpBuffer(&conn->workBuffer); diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index 4a5a7c8b5e3..031b93c535b 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -428,6 +428,8 @@ struct pg_conn char *target_session_attrs; /* desired session properties */ char *require_auth; /* name of the expected auth method */ char *load_balance_hosts; /* load balance over hosts */ + char *scram_client_key; /* base64-encoded SCRAM client key */ + char *scram_server_key; /* base64-encoded SCRAM server key */ bool cancelRequest; /* true if this connection is used to send a * cancel request, instead of being a normal @@ -518,6 +520,10 @@ struct pg_conn AddrInfo *addr; /* the array of addresses for the currently * tried host */ bool send_appname; /* okay to send application_name? */ + size_t scram_client_key_len; + void *scram_client_key_binary; /* binary SCRAM client key */ + size_t scram_server_key_len; + void *scram_server_key_binary; /* binary SCRAM server key */ /* Miscellaneous stuff */ int be_pid; /* PID of backend --- needed for cancels */ |