aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/libpq/auth-scram.c14
-rw-r--r--src/include/libpq/libpq-be.h9
-rw-r--r--src/interfaces/libpq/fe-auth-scram.c83
-rw-r--r--src/interfaces/libpq/fe-auth.c2
-rw-r--r--src/interfaces/libpq/fe-connect.c47
-rw-r--r--src/interfaces/libpq/libpq-int.h6
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 */