PostgreSQL Source Code  git master
auth-scram.c File Reference
#include "postgres.h"
#include <unistd.h>
#include "access/xlog.h"
#include "catalog/pg_control.h"
#include "common/base64.h"
#include "common/hmac.h"
#include "common/saslprep.h"
#include "common/scram-common.h"
#include "common/sha2.h"
#include "libpq/crypt.h"
#include "libpq/sasl.h"
#include "libpq/scram.h"
Include dependency graph for auth-scram.c:

Go to the source code of this file.

Data Structures

struct  scram_state
 

Enumerations

enum  scram_state_enum { SCRAM_AUTH_INIT , SCRAM_AUTH_SALT_SENT , SCRAM_AUTH_FINISHED }
 

Functions

static void scram_get_mechanisms (Port *port, StringInfo buf)
 
static void * scram_init (Port *port, const char *selected_mech, const char *shadow_pass)
 
static int scram_exchange (void *opaq, const char *input, int inputlen, char **output, int *outputlen, const char **logdetail)
 
static void read_client_first_message (scram_state *state, const char *input)
 
static void read_client_final_message (scram_state *state, const char *input)
 
static char * build_server_first_message (scram_state *state)
 
static char * build_server_final_message (scram_state *state)
 
static bool verify_client_proof (scram_state *state)
 
static bool verify_final_nonce (scram_state *state)
 
static void mock_scram_secret (const char *username, pg_cryptohash_type *hash_type, int *iterations, int *key_length, char **salt, uint8 *stored_key, uint8 *server_key)
 
static bool is_scram_printable (char *p)
 
static char * sanitize_char (char c)
 
static char * sanitize_str (const char *s)
 
static char * scram_mock_salt (const char *username, pg_cryptohash_type hash_type, int key_length)
 
char * pg_be_scram_build_secret (const char *password)
 
bool scram_verify_plain_password (const char *username, const char *password, const char *secret)
 
bool parse_scram_secret (const char *secret, int *iterations, pg_cryptohash_type *hash_type, int *key_length, char **salt, uint8 *stored_key, uint8 *server_key)
 
static char * read_attr_value (char **input, char attr)
 
static char * read_any_attr (char **input, char *attr_p)
 

Variables

const pg_be_sasl_mech pg_be_scram_mech
 
int scram_sha_256_iterations = SCRAM_SHA_256_DEFAULT_ITERATIONS
 

Enumeration Type Documentation

◆ scram_state_enum

Enumerator
SCRAM_AUTH_INIT 
SCRAM_AUTH_SALT_SENT 
SCRAM_AUTH_FINISHED 

Definition at line 123 of file auth-scram.c.

124 {
scram_state_enum
Definition: auth-scram.c:124
@ SCRAM_AUTH_SALT_SENT
Definition: auth-scram.c:126
@ SCRAM_AUTH_FINISHED
Definition: auth-scram.c:127
@ SCRAM_AUTH_INIT
Definition: auth-scram.c:125

Function Documentation

◆ build_server_final_message()

static char * build_server_final_message ( scram_state state)
static

Definition at line 1399 of file auth-scram.c.

1400 {
1401  uint8 ServerSignature[SCRAM_MAX_KEY_LEN];
1402  char *server_signature_base64;
1403  int siglen;
1404  pg_hmac_ctx *ctx = pg_hmac_create(state->hash_type);
1405 
1406  /* calculate ServerSignature */
1407  if (pg_hmac_init(ctx, state->ServerKey, state->key_length) < 0 ||
1408  pg_hmac_update(ctx,
1409  (uint8 *) state->client_first_message_bare,
1410  strlen(state->client_first_message_bare)) < 0 ||
1411  pg_hmac_update(ctx, (uint8 *) ",", 1) < 0 ||
1412  pg_hmac_update(ctx,
1413  (uint8 *) state->server_first_message,
1414  strlen(state->server_first_message)) < 0 ||
1415  pg_hmac_update(ctx, (uint8 *) ",", 1) < 0 ||
1416  pg_hmac_update(ctx,
1417  (uint8 *) state->client_final_message_without_proof,
1418  strlen(state->client_final_message_without_proof)) < 0 ||
1419  pg_hmac_final(ctx, ServerSignature, state->key_length) < 0)
1420  {
1421  elog(ERROR, "could not calculate server signature: %s",
1422  pg_hmac_error(ctx));
1423  }
1424 
1425  pg_hmac_free(ctx);
1426 
1427  siglen = pg_b64_enc_len(state->key_length);
1428  /* don't forget the zero-terminator */
1429  server_signature_base64 = palloc(siglen + 1);
1430  siglen = pg_b64_encode((const char *) ServerSignature,
1431  state->key_length, server_signature_base64,
1432  siglen);
1433  if (siglen < 0)
1434  elog(ERROR, "could not encode server signature");
1435  server_signature_base64[siglen] = '\0';
1436 
1437  /*------
1438  * The syntax for the server-final-message is: (RFC 5802)
1439  *
1440  * verifier = "v=" base64
1441  * ;; base-64 encoded ServerSignature.
1442  *
1443  * server-final-message = (server-error / verifier)
1444  * ["," extensions]
1445  *
1446  *------
1447  */
1448  return psprintf("v=%s", server_signature_base64);
1449 }
int pg_b64_enc_len(int srclen)
Definition: base64.c:224
int pg_b64_encode(const char *src, int len, char *dst, int dstlen)
Definition: base64.c:49
unsigned char uint8
Definition: c.h:491
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:224
pg_hmac_ctx * pg_hmac_create(pg_cryptohash_type type)
Definition: hmac.c:77
const char * pg_hmac_error(pg_hmac_ctx *ctx)
Definition: hmac.c:306
void pg_hmac_free(pg_hmac_ctx *ctx)
Definition: hmac.c:289
int pg_hmac_update(pg_hmac_ctx *ctx, const uint8 *data, size_t len)
Definition: hmac.c:223
int pg_hmac_init(pg_hmac_ctx *ctx, const uint8 *key, size_t len)
Definition: hmac.c:138
int pg_hmac_final(pg_hmac_ctx *ctx, uint8 *dest, size_t len)
Definition: hmac.c:244
void * palloc(Size size)
Definition: mcxt.c:1304
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
#define SCRAM_MAX_KEY_LEN
Definition: scram-common.h:30
Definition: regguts.h:323

References elog, ERROR, palloc(), pg_b64_enc_len(), pg_b64_encode(), pg_hmac_create(), pg_hmac_error(), pg_hmac_final(), pg_hmac_free(), pg_hmac_init(), pg_hmac_update(), psprintf(), and SCRAM_MAX_KEY_LEN.

Referenced by scram_exchange().

◆ build_server_first_message()

static char * build_server_first_message ( scram_state state)
static

Definition at line 1189 of file auth-scram.c.

1190 {
1191  /*------
1192  * The syntax for the server-first-message is: (RFC 5802)
1193  *
1194  * server-first-message =
1195  * [reserved-mext ","] nonce "," salt ","
1196  * iteration-count ["," extensions]
1197  *
1198  * nonce = "r=" c-nonce [s-nonce]
1199  * ;; Second part provided by server.
1200  *
1201  * c-nonce = printable
1202  *
1203  * s-nonce = printable
1204  *
1205  * salt = "s=" base64
1206  *
1207  * iteration-count = "i=" posit-number
1208  * ;; A positive number.
1209  *
1210  * Example:
1211  *
1212  * r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,s=QSXCR+Q6sek8bf92,i=4096
1213  *------
1214  */
1215 
1216  /*
1217  * Per the spec, the nonce may consist of any printable ASCII characters.
1218  * For convenience, however, we don't use the whole range available,
1219  * rather, we generate some random bytes, and base64 encode them.
1220  */
1221  char raw_nonce[SCRAM_RAW_NONCE_LEN];
1222  int encoded_len;
1223 
1224  if (!pg_strong_random(raw_nonce, SCRAM_RAW_NONCE_LEN))
1225  ereport(ERROR,
1226  (errcode(ERRCODE_INTERNAL_ERROR),
1227  errmsg("could not generate random nonce")));
1228 
1229  encoded_len = pg_b64_enc_len(SCRAM_RAW_NONCE_LEN);
1230  /* don't forget the zero-terminator */
1231  state->server_nonce = palloc(encoded_len + 1);
1232  encoded_len = pg_b64_encode(raw_nonce, SCRAM_RAW_NONCE_LEN,
1233  state->server_nonce, encoded_len);
1234  if (encoded_len < 0)
1235  ereport(ERROR,
1236  (errcode(ERRCODE_INTERNAL_ERROR),
1237  errmsg("could not encode random nonce")));
1238  state->server_nonce[encoded_len] = '\0';
1239 
1240  state->server_first_message =
1241  psprintf("r=%s%s,s=%s,i=%d",
1242  state->client_nonce, state->server_nonce,
1243  state->salt, state->iterations);
1244 
1245  return pstrdup(state->server_first_message);
1246 }
int errcode(int sqlerrcode)
Definition: elog.c:859
int errmsg(const char *fmt,...)
Definition: elog.c:1072
#define ereport(elevel,...)
Definition: elog.h:149
char * pstrdup(const char *in)
Definition: mcxt.c:1683
bool pg_strong_random(void *buf, size_t len)
#define SCRAM_RAW_NONCE_LEN
Definition: scram-common.h:37

References ereport, errcode(), errmsg(), ERROR, palloc(), pg_b64_enc_len(), pg_b64_encode(), pg_strong_random(), psprintf(), pstrdup(), and SCRAM_RAW_NONCE_LEN.

Referenced by scram_exchange().

◆ is_scram_printable()

static bool is_scram_printable ( char *  p)
static

Definition at line 765 of file auth-scram.c.

766 {
767  /*------
768  * Printable characters, as defined by SCRAM spec: (RFC 5802)
769  *
770  * printable = %x21-2B / %x2D-7E
771  * ;; Printable ASCII except ",".
772  * ;; Note that any "printable" is also
773  * ;; a valid "value".
774  *------
775  */
776  for (; *p; p++)
777  {
778  if (*p < 0x21 || *p > 0x7E || *p == 0x2C /* comma */ )
779  return false;
780  }
781  return true;
782 }

Referenced by read_client_first_message().

◆ mock_scram_secret()

static void mock_scram_secret ( const char *  username,
pg_cryptohash_type hash_type,
int *  iterations,
int *  key_length,
char **  salt,
uint8 stored_key,
uint8 server_key 
)
static

Definition at line 683 of file auth-scram.c.

686 {
687  char *raw_salt;
688  char *encoded_salt;
689  int encoded_len;
690 
691  /* Enforce the use of SHA-256, which would be realistic enough */
692  *hash_type = PG_SHA256;
693  *key_length = SCRAM_SHA_256_KEY_LEN;
694 
695  /*
696  * Generate deterministic salt.
697  *
698  * Note that we cannot reveal any information to an attacker here so the
699  * error messages need to remain generic. This should never fail anyway
700  * as the salt generated for mock authentication uses the cluster's nonce
701  * value.
702  */
703  raw_salt = scram_mock_salt(username, *hash_type, *key_length);
704  if (raw_salt == NULL)
705  elog(ERROR, "could not encode salt");
706 
707  encoded_len = pg_b64_enc_len(SCRAM_DEFAULT_SALT_LEN);
708  /* don't forget the zero-terminator */
709  encoded_salt = (char *) palloc(encoded_len + 1);
710  encoded_len = pg_b64_encode(raw_salt, SCRAM_DEFAULT_SALT_LEN, encoded_salt,
711  encoded_len);
712 
713  if (encoded_len < 0)
714  elog(ERROR, "could not encode salt");
715  encoded_salt[encoded_len] = '\0';
716 
717  *salt = encoded_salt;
719 
720  /* StoredKey and ServerKey are not used in a doomed authentication */
721  memset(stored_key, 0, SCRAM_MAX_KEY_LEN);
722  memset(server_key, 0, SCRAM_MAX_KEY_LEN);
723 }
static char * scram_mock_salt(const char *username, pg_cryptohash_type hash_type, int key_length)
Definition: auth-scram.c:1458
@ PG_SHA256
Definition: cryptohash.h:24
const char * username
Definition: pgbench.c:296
#define SCRAM_DEFAULT_SALT_LEN
Definition: scram-common.h:44
#define SCRAM_SHA_256_KEY_LEN
Definition: scram-common.h:24
#define SCRAM_SHA_256_DEFAULT_ITERATIONS
Definition: scram-common.h:50
int iterations
Definition: thread-thread.c:39

References elog, ERROR, iterations, palloc(), pg_b64_enc_len(), pg_b64_encode(), PG_SHA256, SCRAM_DEFAULT_SALT_LEN, SCRAM_MAX_KEY_LEN, scram_mock_salt(), SCRAM_SHA_256_DEFAULT_ITERATIONS, SCRAM_SHA_256_KEY_LEN, and username.

Referenced by scram_init().

◆ parse_scram_secret()

bool parse_scram_secret ( const char *  secret,
int *  iterations,
pg_cryptohash_type hash_type,
int *  key_length,
char **  salt,
uint8 stored_key,
uint8 server_key 
)

Definition at line 589 of file auth-scram.c.

592 {
593  char *v;
594  char *p;
595  char *scheme_str;
596  char *salt_str;
597  char *iterations_str;
598  char *storedkey_str;
599  char *serverkey_str;
600  int decoded_len;
601  char *decoded_salt_buf;
602  char *decoded_stored_buf;
603  char *decoded_server_buf;
604 
605  /*
606  * The secret is of form:
607  *
608  * SCRAM-SHA-256$<iterations>:<salt>$<storedkey>:<serverkey>
609  */
610  v = pstrdup(secret);
611  if ((scheme_str = strtok(v, "$")) == NULL)
612  goto invalid_secret;
613  if ((iterations_str = strtok(NULL, ":")) == NULL)
614  goto invalid_secret;
615  if ((salt_str = strtok(NULL, "$")) == NULL)
616  goto invalid_secret;
617  if ((storedkey_str = strtok(NULL, ":")) == NULL)
618  goto invalid_secret;
619  if ((serverkey_str = strtok(NULL, "")) == NULL)
620  goto invalid_secret;
621 
622  /* Parse the fields */
623  if (strcmp(scheme_str, "SCRAM-SHA-256") != 0)
624  goto invalid_secret;
625  *hash_type = PG_SHA256;
626  *key_length = SCRAM_SHA_256_KEY_LEN;
627 
628  errno = 0;
629  *iterations = strtol(iterations_str, &p, 10);
630  if (*p || errno != 0)
631  goto invalid_secret;
632 
633  /*
634  * Verify that the salt is in Base64-encoded format, by decoding it,
635  * although we return the encoded version to the caller.
636  */
637  decoded_len = pg_b64_dec_len(strlen(salt_str));
638  decoded_salt_buf = palloc(decoded_len);
639  decoded_len = pg_b64_decode(salt_str, strlen(salt_str),
640  decoded_salt_buf, decoded_len);
641  if (decoded_len < 0)
642  goto invalid_secret;
643  *salt = pstrdup(salt_str);
644 
645  /*
646  * Decode StoredKey and ServerKey.
647  */
648  decoded_len = pg_b64_dec_len(strlen(storedkey_str));
649  decoded_stored_buf = palloc(decoded_len);
650  decoded_len = pg_b64_decode(storedkey_str, strlen(storedkey_str),
651  decoded_stored_buf, decoded_len);
652  if (decoded_len != *key_length)
653  goto invalid_secret;
654  memcpy(stored_key, decoded_stored_buf, *key_length);
655 
656  decoded_len = pg_b64_dec_len(strlen(serverkey_str));
657  decoded_server_buf = palloc(decoded_len);
658  decoded_len = pg_b64_decode(serverkey_str, strlen(serverkey_str),
659  decoded_server_buf, decoded_len);
660  if (decoded_len != *key_length)
661  goto invalid_secret;
662  memcpy(server_key, decoded_server_buf, *key_length);
663 
664  return true;
665 
666 invalid_secret:
667  *salt = NULL;
668  return false;
669 }
int pg_b64_decode(const char *src, int len, char *dst, int dstlen)
Definition: base64.c:116
int pg_b64_dec_len(int srclen)
Definition: base64.c:239

References iterations, palloc(), pg_b64_dec_len(), pg_b64_decode(), PG_SHA256, pstrdup(), and SCRAM_SHA_256_KEY_LEN.

Referenced by get_password_type(), scram_init(), and scram_verify_plain_password().

◆ pg_be_scram_build_secret()

char* pg_be_scram_build_secret ( const char *  password)

Definition at line 472 of file auth-scram.c.

473 {
474  char *prep_password;
475  pg_saslprep_rc rc;
476  char saltbuf[SCRAM_DEFAULT_SALT_LEN];
477  char *result;
478  const char *errstr = NULL;
479 
480  /*
481  * Normalize the password with SASLprep. If that doesn't work, because
482  * the password isn't valid UTF-8 or contains prohibited characters, just
483  * proceed with the original password. (See comments at top of file.)
484  */
485  rc = pg_saslprep(password, &prep_password);
486  if (rc == SASLPREP_SUCCESS)
487  password = (const char *) prep_password;
488 
489  /* Generate random salt */
491  ereport(ERROR,
492  (errcode(ERRCODE_INTERNAL_ERROR),
493  errmsg("could not generate random salt")));
494 
496  saltbuf, SCRAM_DEFAULT_SALT_LEN,
498  &errstr);
499 
500  if (prep_password)
501  pfree(prep_password);
502 
503  return result;
504 }
int scram_sha_256_iterations
Definition: auth-scram.c:192
void pfree(void *pointer)
Definition: mcxt.c:1508
pg_saslprep_rc pg_saslprep(const char *input, char **output)
Definition: saslprep.c:1044
pg_saslprep_rc
Definition: saslprep.h:21
@ SASLPREP_SUCCESS
Definition: saslprep.h:22
char * scram_build_secret(pg_cryptohash_type hash_type, int key_length, const char *salt, int saltlen, int iterations, const char *password, const char **errstr)
Definition: scram-common.c:210
static char * password
Definition: streamutil.c:54

References ereport, errcode(), errmsg(), ERROR, password, pfree(), pg_saslprep(), PG_SHA256, pg_strong_random(), SASLPREP_SUCCESS, scram_build_secret(), SCRAM_DEFAULT_SALT_LEN, scram_sha_256_iterations, and SCRAM_SHA_256_KEY_LEN.

Referenced by encrypt_password().

◆ read_any_attr()

static char* read_any_attr ( char **  input,
char *  attr_p 
)
static

Definition at line 841 of file auth-scram.c.

842 {
843  char *begin = *input;
844  char *end;
845  char attr = *begin;
846 
847  if (attr == '\0')
848  ereport(ERROR,
849  (errcode(ERRCODE_PROTOCOL_VIOLATION),
850  errmsg("malformed SCRAM message"),
851  errdetail("Attribute expected, but found end of string.")));
852 
853  /*------
854  * attr-val = ALPHA "=" value
855  * ;; Generic syntax of any attribute sent
856  * ;; by server or client
857  *------
858  */
859  if (!((attr >= 'A' && attr <= 'Z') ||
860  (attr >= 'a' && attr <= 'z')))
861  ereport(ERROR,
862  (errcode(ERRCODE_PROTOCOL_VIOLATION),
863  errmsg("malformed SCRAM message"),
864  errdetail("Attribute expected, but found invalid character \"%s\".",
865  sanitize_char(attr))));
866  if (attr_p)
867  *attr_p = attr;
868  begin++;
869 
870  if (*begin != '=')
871  ereport(ERROR,
872  (errcode(ERRCODE_PROTOCOL_VIOLATION),
873  errmsg("malformed SCRAM message"),
874  errdetail("Expected character \"=\" for attribute \"%c\".", attr)));
875  begin++;
876 
877  end = begin;
878  while (*end && *end != ',')
879  end++;
880 
881  if (*end)
882  {
883  *end = '\0';
884  *input = end + 1;
885  }
886  else
887  *input = end;
888 
889  return begin;
890 }
static char * sanitize_char(char c)
Definition: auth-scram.c:793
int errdetail(const char *fmt,...)
Definition: elog.c:1205
FILE * input

References ereport, errcode(), errdetail(), errmsg(), ERROR, input, and sanitize_char().

Referenced by read_client_final_message(), and read_client_first_message().

◆ read_attr_value()

static char* read_attr_value ( char **  input,
char  attr 
)
static

Definition at line 729 of file auth-scram.c.

730 {
731  char *begin = *input;
732  char *end;
733 
734  if (*begin != attr)
735  ereport(ERROR,
736  (errcode(ERRCODE_PROTOCOL_VIOLATION),
737  errmsg("malformed SCRAM message"),
738  errdetail("Expected attribute \"%c\" but found \"%s\".",
739  attr, sanitize_char(*begin))));
740  begin++;
741 
742  if (*begin != '=')
743  ereport(ERROR,
744  (errcode(ERRCODE_PROTOCOL_VIOLATION),
745  errmsg("malformed SCRAM message"),
746  errdetail("Expected character \"=\" for attribute \"%c\".", attr)));
747  begin++;
748 
749  end = begin;
750  while (*end && *end != ',')
751  end++;
752 
753  if (*end)
754  {
755  *end = '\0';
756  *input = end + 1;
757  }
758  else
759  *input = end;
760 
761  return begin;
762 }

References ereport, errcode(), errdetail(), errmsg(), ERROR, input, and sanitize_char().

Referenced by read_client_final_message(), and read_client_first_message().

◆ read_client_final_message()

static void read_client_final_message ( scram_state state,
const char *  input 
)
static

Definition at line 1253 of file auth-scram.c.

1254 {
1255  char attr;
1256  char *channel_binding;
1257  char *value;
1258  char *begin,
1259  *proof;
1260  char *p;
1261  char *client_proof;
1262  int client_proof_len;
1263 
1264  begin = p = pstrdup(input);
1265 
1266  /*------
1267  * The syntax for the server-first-message is: (RFC 5802)
1268  *
1269  * gs2-header = gs2-cbind-flag "," [ authzid ] ","
1270  * ;; GS2 header for SCRAM
1271  * ;; (the actual GS2 header includes an optional
1272  * ;; flag to indicate that the GSS mechanism is not
1273  * ;; "standard", but since SCRAM is "standard", we
1274  * ;; don't include that flag).
1275  *
1276  * cbind-input = gs2-header [ cbind-data ]
1277  * ;; cbind-data MUST be present for
1278  * ;; gs2-cbind-flag of "p" and MUST be absent
1279  * ;; for "y" or "n".
1280  *
1281  * channel-binding = "c=" base64
1282  * ;; base64 encoding of cbind-input.
1283  *
1284  * proof = "p=" base64
1285  *
1286  * client-final-message-without-proof =
1287  * channel-binding "," nonce [","
1288  * extensions]
1289  *
1290  * client-final-message =
1291  * client-final-message-without-proof "," proof
1292  *------
1293  */
1294 
1295  /*
1296  * Read channel binding. This repeats the channel-binding flags and is
1297  * then followed by the actual binding data depending on the type.
1298  */
1299  channel_binding = read_attr_value(&p, 'c');
1300  if (state->channel_binding_in_use)
1301  {
1302 #ifdef USE_SSL
1303  const char *cbind_data = NULL;
1304  size_t cbind_data_len = 0;
1305  size_t cbind_header_len;
1306  char *cbind_input;
1307  size_t cbind_input_len;
1308  char *b64_message;
1309  int b64_message_len;
1310 
1311  Assert(state->cbind_flag == 'p');
1312 
1313  /* Fetch hash data of server's SSL certificate */
1314  cbind_data = be_tls_get_certificate_hash(state->port,
1315  &cbind_data_len);
1316 
1317  /* should not happen */
1318  if (cbind_data == NULL || cbind_data_len == 0)
1319  elog(ERROR, "could not get server certificate hash");
1320 
1321  cbind_header_len = strlen("p=tls-server-end-point,,"); /* p=type,, */
1322  cbind_input_len = cbind_header_len + cbind_data_len;
1323  cbind_input = palloc(cbind_input_len);
1324  snprintf(cbind_input, cbind_input_len, "p=tls-server-end-point,,");
1325  memcpy(cbind_input + cbind_header_len, cbind_data, cbind_data_len);
1326 
1327  b64_message_len = pg_b64_enc_len(cbind_input_len);
1328  /* don't forget the zero-terminator */
1329  b64_message = palloc(b64_message_len + 1);
1330  b64_message_len = pg_b64_encode(cbind_input, cbind_input_len,
1331  b64_message, b64_message_len);
1332  if (b64_message_len < 0)
1333  elog(ERROR, "could not encode channel binding data");
1334  b64_message[b64_message_len] = '\0';
1335 
1336  /*
1337  * Compare the value sent by the client with the value expected by the
1338  * server.
1339  */
1340  if (strcmp(channel_binding, b64_message) != 0)
1341  ereport(ERROR,
1342  (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
1343  errmsg("SCRAM channel binding check failed")));
1344 #else
1345  /* shouldn't happen, because we checked this earlier already */
1346  elog(ERROR, "channel binding not supported by this build");
1347 #endif
1348  }
1349  else
1350  {
1351  /*
1352  * If we are not using channel binding, the binding data is expected
1353  * to always be "biws", which is "n,," base64-encoded, or "eSws",
1354  * which is "y,,". We also have to check whether the flag is the same
1355  * one that the client originally sent.
1356  */
1357  if (!(strcmp(channel_binding, "biws") == 0 && state->cbind_flag == 'n') &&
1358  !(strcmp(channel_binding, "eSws") == 0 && state->cbind_flag == 'y'))
1359  ereport(ERROR,
1360  (errcode(ERRCODE_PROTOCOL_VIOLATION),
1361  errmsg("unexpected SCRAM channel-binding attribute in client-final-message")));
1362  }
1363 
1364  state->client_final_nonce = read_attr_value(&p, 'r');
1365 
1366  /* ignore optional extensions, read until we find "p" attribute */
1367  do
1368  {
1369  proof = p - 1;
1370  value = read_any_attr(&p, &attr);
1371  } while (attr != 'p');
1372 
1373  client_proof_len = pg_b64_dec_len(strlen(value));
1374  client_proof = palloc(client_proof_len);
1375  if (pg_b64_decode(value, strlen(value), client_proof,
1376  client_proof_len) != state->key_length)
1377  ereport(ERROR,
1378  (errcode(ERRCODE_PROTOCOL_VIOLATION),
1379  errmsg("malformed SCRAM message"),
1380  errdetail("Malformed proof in client-final-message.")));
1381  memcpy(state->ClientProof, client_proof, state->key_length);
1382  pfree(client_proof);
1383 
1384  if (*p != '\0')
1385  ereport(ERROR,
1386  (errcode(ERRCODE_PROTOCOL_VIOLATION),
1387  errmsg("malformed SCRAM message"),
1388  errdetail("Garbage found at the end of client-final-message.")));
1389 
1390  state->client_final_message_without_proof = palloc(proof - begin + 1);
1391  memcpy(state->client_final_message_without_proof, input, proof - begin);
1392  state->client_final_message_without_proof[proof - begin] = '\0';
1393 }
static char * read_attr_value(char **input, char attr)
Definition: auth-scram.c:729
static char * read_any_attr(char **input, char *attr_p)
Definition: auth-scram.c:841
char * be_tls_get_certificate_hash(Port *port, size_t *len)
static struct @154 value
Assert(fmt[strlen(fmt) - 1] !='\n')
#define snprintf
Definition: port.h:238

References Assert(), be_tls_get_certificate_hash(), elog, ereport, errcode(), errdetail(), errmsg(), ERROR, input, palloc(), pfree(), pg_b64_dec_len(), pg_b64_decode(), pg_b64_enc_len(), pg_b64_encode(), pstrdup(), read_any_attr(), read_attr_value(), snprintf, and value.

Referenced by scram_exchange().

◆ read_client_first_message()

static void read_client_first_message ( scram_state state,
const char *  input 
)
static

Definition at line 899 of file auth-scram.c.

900 {
901  char *p = pstrdup(input);
902  char *channel_binding_type;
903 
904 
905  /*------
906  * The syntax for the client-first-message is: (RFC 5802)
907  *
908  * saslname = 1*(value-safe-char / "=2C" / "=3D")
909  * ;; Conforms to <value>.
910  *
911  * authzid = "a=" saslname
912  * ;; Protocol specific.
913  *
914  * cb-name = 1*(ALPHA / DIGIT / "." / "-")
915  * ;; See RFC 5056, Section 7.
916  * ;; E.g., "tls-server-end-point" or
917  * ;; "tls-unique".
918  *
919  * gs2-cbind-flag = ("p=" cb-name) / "n" / "y"
920  * ;; "n" -> client doesn't support channel binding.
921  * ;; "y" -> client does support channel binding
922  * ;; but thinks the server does not.
923  * ;; "p" -> client requires channel binding.
924  * ;; The selected channel binding follows "p=".
925  *
926  * gs2-header = gs2-cbind-flag "," [ authzid ] ","
927  * ;; GS2 header for SCRAM
928  * ;; (the actual GS2 header includes an optional
929  * ;; flag to indicate that the GSS mechanism is not
930  * ;; "standard", but since SCRAM is "standard", we
931  * ;; don't include that flag).
932  *
933  * username = "n=" saslname
934  * ;; Usernames are prepared using SASLprep.
935  *
936  * reserved-mext = "m=" 1*(value-char)
937  * ;; Reserved for signaling mandatory extensions.
938  * ;; The exact syntax will be defined in
939  * ;; the future.
940  *
941  * nonce = "r=" c-nonce [s-nonce]
942  * ;; Second part provided by server.
943  *
944  * c-nonce = printable
945  *
946  * client-first-message-bare =
947  * [reserved-mext ","]
948  * username "," nonce ["," extensions]
949  *
950  * client-first-message =
951  * gs2-header client-first-message-bare
952  *
953  * For example:
954  * n,,n=user,r=fyko+d2lbbFgONRv9qkxdawL
955  *
956  * The "n,," in the beginning means that the client doesn't support
957  * channel binding, and no authzid is given. "n=user" is the username.
958  * However, in PostgreSQL the username is sent in the startup packet, and
959  * the username in the SCRAM exchange is ignored. libpq always sends it
960  * as an empty string. The last part, "r=fyko+d2lbbFgONRv9qkxdawL" is
961  * the client nonce.
962  *------
963  */
964 
965  /*
966  * Read gs2-cbind-flag. (For details see also RFC 5802 Section 6 "Channel
967  * Binding".)
968  */
969  state->cbind_flag = *p;
970  switch (*p)
971  {
972  case 'n':
973 
974  /*
975  * The client does not support channel binding or has simply
976  * decided to not use it. In that case just let it go.
977  */
978  if (state->channel_binding_in_use)
979  ereport(ERROR,
980  (errcode(ERRCODE_PROTOCOL_VIOLATION),
981  errmsg("malformed SCRAM message"),
982  errdetail("The client selected SCRAM-SHA-256-PLUS, but the SCRAM message does not include channel binding data.")));
983 
984  p++;
985  if (*p != ',')
986  ereport(ERROR,
987  (errcode(ERRCODE_PROTOCOL_VIOLATION),
988  errmsg("malformed SCRAM message"),
989  errdetail("Comma expected, but found character \"%s\".",
990  sanitize_char(*p))));
991  p++;
992  break;
993  case 'y':
994 
995  /*
996  * The client supports channel binding and thinks that the server
997  * does not. In this case, the server must fail authentication if
998  * it supports channel binding.
999  */
1000  if (state->channel_binding_in_use)
1001  ereport(ERROR,
1002  (errcode(ERRCODE_PROTOCOL_VIOLATION),
1003  errmsg("malformed SCRAM message"),
1004  errdetail("The client selected SCRAM-SHA-256-PLUS, but the SCRAM message does not include channel binding data.")));
1005 
1006 #ifdef USE_SSL
1007  if (state->port->ssl_in_use)
1008  ereport(ERROR,
1009  (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
1010  errmsg("SCRAM channel binding negotiation error"),
1011  errdetail("The client supports SCRAM channel binding but thinks the server does not. "
1012  "However, this server does support channel binding.")));
1013 #endif
1014  p++;
1015  if (*p != ',')
1016  ereport(ERROR,
1017  (errcode(ERRCODE_PROTOCOL_VIOLATION),
1018  errmsg("malformed SCRAM message"),
1019  errdetail("Comma expected, but found character \"%s\".",
1020  sanitize_char(*p))));
1021  p++;
1022  break;
1023  case 'p':
1024 
1025  /*
1026  * The client requires channel binding. Channel binding type
1027  * follows, e.g., "p=tls-server-end-point".
1028  */
1029  if (!state->channel_binding_in_use)
1030  ereport(ERROR,
1031  (errcode(ERRCODE_PROTOCOL_VIOLATION),
1032  errmsg("malformed SCRAM message"),
1033  errdetail("The client selected SCRAM-SHA-256 without channel binding, but the SCRAM message includes channel binding data.")));
1034 
1035  channel_binding_type = read_attr_value(&p, 'p');
1036 
1037  /*
1038  * The only channel binding type we support is
1039  * tls-server-end-point.
1040  */
1041  if (strcmp(channel_binding_type, "tls-server-end-point") != 0)
1042  ereport(ERROR,
1043  (errcode(ERRCODE_PROTOCOL_VIOLATION),
1044  errmsg("unsupported SCRAM channel-binding type \"%s\"",
1045  sanitize_str(channel_binding_type))));
1046  break;
1047  default:
1048  ereport(ERROR,
1049  (errcode(ERRCODE_PROTOCOL_VIOLATION),
1050  errmsg("malformed SCRAM message"),
1051  errdetail("Unexpected channel-binding flag \"%s\".",
1052  sanitize_char(*p))));
1053  }
1054 
1055  /*
1056  * Forbid optional authzid (authorization identity). We don't support it.
1057  */
1058  if (*p == 'a')
1059  ereport(ERROR,
1060  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1061  errmsg("client uses authorization identity, but it is not supported")));
1062  if (*p != ',')
1063  ereport(ERROR,
1064  (errcode(ERRCODE_PROTOCOL_VIOLATION),
1065  errmsg("malformed SCRAM message"),
1066  errdetail("Unexpected attribute \"%s\" in client-first-message.",
1067  sanitize_char(*p))));
1068  p++;
1069 
1070  state->client_first_message_bare = pstrdup(p);
1071 
1072  /*
1073  * Any mandatory extensions would go here. We don't support any.
1074  *
1075  * RFC 5802 specifies error code "e=extensions-not-supported" for this,
1076  * but it can only be sent in the server-final message. We prefer to fail
1077  * immediately (which the RFC also allows).
1078  */
1079  if (*p == 'm')
1080  ereport(ERROR,
1081  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1082  errmsg("client requires an unsupported SCRAM extension")));
1083 
1084  /*
1085  * Read username. Note: this is ignored. We use the username from the
1086  * startup message instead, still it is kept around if provided as it
1087  * proves to be useful for debugging purposes.
1088  */
1089  state->client_username = read_attr_value(&p, 'n');
1090 
1091  /* read nonce and check that it is made of only printable characters */
1092  state->client_nonce = read_attr_value(&p, 'r');
1093  if (!is_scram_printable(state->client_nonce))
1094  ereport(ERROR,
1095  (errcode(ERRCODE_PROTOCOL_VIOLATION),
1096  errmsg("non-printable characters in SCRAM nonce")));
1097 
1098  /*
1099  * There can be any number of optional extensions after this. We don't
1100  * support any extensions, so ignore them.
1101  */
1102  while (*p != '\0')
1103  read_any_attr(&p, NULL);
1104 
1105  /* success! */
1106 }
static char * sanitize_str(const char *s)
Definition: auth-scram.c:813
static bool is_scram_printable(char *p)
Definition: auth-scram.c:765

References ereport, errcode(), errdetail(), errmsg(), ERROR, input, is_scram_printable(), pstrdup(), read_any_attr(), read_attr_value(), sanitize_char(), and sanitize_str().

Referenced by scram_exchange().

◆ sanitize_char()

static char * sanitize_char ( char  c)
static

Definition at line 793 of file auth-scram.c.

794 {
795  static char buf[5];
796 
797  if (c >= 0x21 && c <= 0x7E)
798  snprintf(buf, sizeof(buf), "'%c'", c);
799  else
800  snprintf(buf, sizeof(buf), "0x%02x", (unsigned char) c);
801  return buf;
802 }
static char * buf
Definition: pg_test_fsync.c:73
char * c

References buf, and snprintf.

Referenced by read_any_attr(), read_attr_value(), and read_client_first_message().

◆ sanitize_str()

static char * sanitize_str ( const char *  s)
static

Definition at line 813 of file auth-scram.c.

814 {
815  static char buf[30 + 1];
816  int i;
817 
818  for (i = 0; i < sizeof(buf) - 1; i++)
819  {
820  char c = s[i];
821 
822  if (c == '\0')
823  break;
824 
825  if (c >= 0x21 && c <= 0x7E)
826  buf[i] = c;
827  else
828  buf[i] = '?';
829  }
830  buf[i] = '\0';
831  return buf;
832 }
int i
Definition: isn.c:73

References buf, and i.

Referenced by read_client_first_message().

◆ scram_exchange()

static int scram_exchange ( void *  opaq,
const char *  input,
int  inputlen,
char **  output,
int *  outputlen,
const char **  logdetail 
)
static

Definition at line 348 of file auth-scram.c.

350 {
351  scram_state *state = (scram_state *) opaq;
352  int result;
353 
354  *output = NULL;
355 
356  /*
357  * If the client didn't include an "Initial Client Response" in the
358  * SASLInitialResponse message, send an empty challenge, to which the
359  * client will respond with the same data that usually comes in the
360  * Initial Client Response.
361  */
362  if (input == NULL)
363  {
364  Assert(state->state == SCRAM_AUTH_INIT);
365 
366  *output = pstrdup("");
367  *outputlen = 0;
369  }
370 
371  /*
372  * Check that the input length agrees with the string length of the input.
373  * We can ignore inputlen after this.
374  */
375  if (inputlen == 0)
376  ereport(ERROR,
377  (errcode(ERRCODE_PROTOCOL_VIOLATION),
378  errmsg("malformed SCRAM message"),
379  errdetail("The message is empty.")));
380  if (inputlen != strlen(input))
381  ereport(ERROR,
382  (errcode(ERRCODE_PROTOCOL_VIOLATION),
383  errmsg("malformed SCRAM message"),
384  errdetail("Message length does not match input length.")));
385 
386  switch (state->state)
387  {
388  case SCRAM_AUTH_INIT:
389 
390  /*
391  * Initialization phase. Receive the first message from client
392  * and be sure that it parsed correctly. Then send the challenge
393  * to the client.
394  */
396 
397  /* prepare message to send challenge */
399 
400  state->state = SCRAM_AUTH_SALT_SENT;
401  result = PG_SASL_EXCHANGE_CONTINUE;
402  break;
403 
405 
406  /*
407  * Final phase for the server. Receive the response to the
408  * challenge previously sent, verify, and let the client know that
409  * everything went well (or not).
410  */
412 
414  ereport(ERROR,
415  (errcode(ERRCODE_PROTOCOL_VIOLATION),
416  errmsg("invalid SCRAM response"),
417  errdetail("Nonce does not match.")));
418 
419  /*
420  * Now check the final nonce and the client proof.
421  *
422  * If we performed a "mock" authentication that we knew would fail
423  * from the get go, this is where we fail.
424  *
425  * The SCRAM specification includes an error code,
426  * "invalid-proof", for authentication failure, but it also allows
427  * erroring out in an application-specific way. We choose to do
428  * the latter, so that the error message for invalid password is
429  * the same for all authentication methods. The caller will call
430  * ereport(), when we return PG_SASL_EXCHANGE_FAILURE with no
431  * output.
432  *
433  * NB: the order of these checks is intentional. We calculate the
434  * client proof even in a mock authentication, even though it's
435  * bound to fail, to thwart timing attacks to determine if a role
436  * with the given name exists or not.
437  */
438  if (!verify_client_proof(state) || state->doomed)
439  {
440  result = PG_SASL_EXCHANGE_FAILURE;
441  break;
442  }
443 
444  /* Build final message for client */
446 
447  /* Success! */
448  result = PG_SASL_EXCHANGE_SUCCESS;
449  state->state = SCRAM_AUTH_FINISHED;
450  break;
451 
452  default:
453  elog(ERROR, "invalid SCRAM exchange state");
454  result = PG_SASL_EXCHANGE_FAILURE;
455  }
456 
457  if (result == PG_SASL_EXCHANGE_FAILURE && state->logdetail && logdetail)
458  *logdetail = state->logdetail;
459 
460  if (*output)
461  *outputlen = strlen(*output);
462 
463  return result;
464 }
static char * build_server_first_message(scram_state *state)
Definition: auth-scram.c:1189
static void read_client_first_message(scram_state *state, const char *input)
Definition: auth-scram.c:899
static bool verify_client_proof(scram_state *state)
Definition: auth-scram.c:1135
static bool verify_final_nonce(scram_state *state)
Definition: auth-scram.c:1113
static void read_client_final_message(scram_state *state, const char *input)
Definition: auth-scram.c:1253
static char * build_server_final_message(scram_state *state)
Definition: auth-scram.c:1399
FILE * output
#define PG_SASL_EXCHANGE_FAILURE
Definition: sasl.h:27
#define PG_SASL_EXCHANGE_CONTINUE
Definition: sasl.h:25
#define PG_SASL_EXCHANGE_SUCCESS
Definition: sasl.h:26

References Assert(), build_server_final_message(), build_server_first_message(), elog, ereport, errcode(), errdetail(), errmsg(), ERROR, input, output, PG_SASL_EXCHANGE_CONTINUE, PG_SASL_EXCHANGE_FAILURE, PG_SASL_EXCHANGE_SUCCESS, pstrdup(), read_client_final_message(), read_client_first_message(), SCRAM_AUTH_FINISHED, SCRAM_AUTH_INIT, SCRAM_AUTH_SALT_SENT, verify_client_proof(), and verify_final_nonce().

◆ scram_get_mechanisms()

static void scram_get_mechanisms ( Port port,
StringInfo  buf 
)
static

Definition at line 202 of file auth-scram.c.

203 {
204  /*
205  * Advertise the mechanisms in decreasing order of importance. So the
206  * channel-binding variants go first, if they are supported. Channel
207  * binding is only supported with SSL.
208  */
209 #ifdef USE_SSL
210  if (port->ssl_in_use)
211  {
213  appendStringInfoChar(buf, '\0');
214  }
215 #endif
217  appendStringInfoChar(buf, '\0');
218 }
static int port
Definition: pg_regress.c:116
#define SCRAM_SHA_256_PLUS_NAME
Definition: scram-common.h:21
#define SCRAM_SHA_256_NAME
Definition: scram-common.h:20
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:182
void appendStringInfoChar(StringInfo str, char ch)
Definition: stringinfo.c:194

References appendStringInfoChar(), appendStringInfoString(), buf, port, SCRAM_SHA_256_NAME, and SCRAM_SHA_256_PLUS_NAME.

◆ scram_init()

static void * scram_init ( Port port,
const char *  selected_mech,
const char *  shadow_pass 
)
static

Definition at line 236 of file auth-scram.c.

237 {
239  bool got_secret;
240 
241  state = (scram_state *) palloc0(sizeof(scram_state));
242  state->port = port;
243  state->state = SCRAM_AUTH_INIT;
244 
245  /*
246  * Parse the selected mechanism.
247  *
248  * Note that if we don't support channel binding, or if we're not using
249  * SSL at all, we would not have advertised the PLUS variant in the first
250  * place. If the client nevertheless tries to select it, it's a protocol
251  * violation like selecting any other SASL mechanism we don't support.
252  */
253 #ifdef USE_SSL
254  if (strcmp(selected_mech, SCRAM_SHA_256_PLUS_NAME) == 0 && port->ssl_in_use)
255  state->channel_binding_in_use = true;
256  else
257 #endif
258  if (strcmp(selected_mech, SCRAM_SHA_256_NAME) == 0)
259  state->channel_binding_in_use = false;
260  else
261  ereport(ERROR,
262  (errcode(ERRCODE_PROTOCOL_VIOLATION),
263  errmsg("client selected an invalid SASL authentication mechanism")));
264 
265  /*
266  * Parse the stored secret.
267  */
268  if (shadow_pass)
269  {
270  int password_type = get_password_type(shadow_pass);
271 
272  if (password_type == PASSWORD_TYPE_SCRAM_SHA_256)
273  {
274  if (parse_scram_secret(shadow_pass, &state->iterations,
275  &state->hash_type, &state->key_length,
276  &state->salt,
277  state->StoredKey,
278  state->ServerKey))
279  got_secret = true;
280  else
281  {
282  /*
283  * The password looked like a SCRAM secret, but could not be
284  * parsed.
285  */
286  ereport(LOG,
287  (errmsg("invalid SCRAM secret for user \"%s\"",
288  state->port->user_name)));
289  got_secret = false;
290  }
291  }
292  else
293  {
294  /*
295  * The user doesn't have SCRAM secret. (You cannot do SCRAM
296  * authentication with an MD5 hash.)
297  */
298  state->logdetail = psprintf(_("User \"%s\" does not have a valid SCRAM secret."),
299  state->port->user_name);
300  got_secret = false;
301  }
302  }
303  else
304  {
305  /*
306  * The caller requested us to perform a dummy authentication. This is
307  * considered normal, since the caller requested it, so don't set log
308  * detail.
309  */
310  got_secret = false;
311  }
312 
313  /*
314  * If the user did not have a valid SCRAM secret, we still go through the
315  * motions with a mock one, and fail as if the client supplied an
316  * incorrect password. This is to avoid revealing information to an
317  * attacker.
318  */
319  if (!got_secret)
320  {
321  mock_scram_secret(state->port->user_name, &state->hash_type,
322  &state->iterations, &state->key_length,
323  &state->salt,
324  state->StoredKey, state->ServerKey);
325  state->doomed = true;
326  }
327 
328  return state;
329 }
bool parse_scram_secret(const char *secret, int *iterations, pg_cryptohash_type *hash_type, int *key_length, char **salt, uint8 *stored_key, uint8 *server_key)
Definition: auth-scram.c:589
static void mock_scram_secret(const char *username, pg_cryptohash_type *hash_type, int *iterations, int *key_length, char **salt, uint8 *stored_key, uint8 *server_key)
Definition: auth-scram.c:683
PasswordType get_password_type(const char *shadow_pass)
Definition: crypt.c:88
@ PASSWORD_TYPE_SCRAM_SHA_256
Definition: crypt.h:31
#define _(x)
Definition: elog.c:90
#define LOG
Definition: elog.h:31
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:77
void * palloc0(Size size)
Definition: mcxt.c:1334

References _, ereport, errcode(), errmsg(), ERROR, get_password_type(), if(), LOG, mock_scram_secret(), palloc0(), parse_scram_secret(), PASSWORD_TYPE_SCRAM_SHA_256, port, psprintf(), SCRAM_AUTH_INIT, SCRAM_SHA_256_NAME, and SCRAM_SHA_256_PLUS_NAME.

◆ scram_mock_salt()

static char * scram_mock_salt ( const char *  username,
pg_cryptohash_type  hash_type,
int  key_length 
)
static

Definition at line 1458 of file auth-scram.c.

1460 {
1461  pg_cryptohash_ctx *ctx;
1462  static uint8 sha_digest[SCRAM_MAX_KEY_LEN];
1463  char *mock_auth_nonce = GetMockAuthenticationNonce();
1464 
1465  /*
1466  * Generate salt using a SHA256 hash of the username and the cluster's
1467  * mock authentication nonce. (This works as long as the salt length is
1468  * not larger than the SHA256 digest length. If the salt is smaller, the
1469  * caller will just ignore the extra data.)
1470  */
1472  "salt length greater than SHA256 digest length");
1473 
1474  /*
1475  * This may be worth refreshing if support for more hash methods is\
1476  * added.
1477  */
1478  Assert(hash_type == PG_SHA256);
1479 
1480  ctx = pg_cryptohash_create(hash_type);
1481  if (pg_cryptohash_init(ctx) < 0 ||
1482  pg_cryptohash_update(ctx, (uint8 *) username, strlen(username)) < 0 ||
1483  pg_cryptohash_update(ctx, (uint8 *) mock_auth_nonce, MOCK_AUTH_NONCE_LEN) < 0 ||
1484  pg_cryptohash_final(ctx, sha_digest, key_length) < 0)
1485  {
1486  pg_cryptohash_free(ctx);
1487  return NULL;
1488  }
1489  pg_cryptohash_free(ctx);
1490 
1491  return (char *) sha_digest;
1492 }
#define StaticAssertDecl(condition, errmessage)
Definition: c.h:923
int pg_cryptohash_update(pg_cryptohash_ctx *ctx, const uint8 *data, size_t len)
Definition: cryptohash.c:136
int pg_cryptohash_init(pg_cryptohash_ctx *ctx)
Definition: cryptohash.c:100
void pg_cryptohash_free(pg_cryptohash_ctx *ctx)
Definition: cryptohash.c:238
pg_cryptohash_ctx * pg_cryptohash_create(pg_cryptohash_type type)
Definition: cryptohash.c:74
int pg_cryptohash_final(pg_cryptohash_ctx *ctx, uint8 *dest, size_t len)
Definition: cryptohash.c:172
#define MOCK_AUTH_NONCE_LEN
Definition: pg_control.h:28
#define PG_SHA256_DIGEST_LENGTH
Definition: sha2.h:23
char * GetMockAuthenticationNonce(void)
Definition: xlog.c:4468

References Assert(), GetMockAuthenticationNonce(), MOCK_AUTH_NONCE_LEN, pg_cryptohash_create(), pg_cryptohash_final(), pg_cryptohash_free(), pg_cryptohash_init(), pg_cryptohash_update(), PG_SHA256, PG_SHA256_DIGEST_LENGTH, SCRAM_DEFAULT_SALT_LEN, SCRAM_MAX_KEY_LEN, StaticAssertDecl, and username.

Referenced by mock_scram_secret().

◆ scram_verify_plain_password()

bool scram_verify_plain_password ( const char *  username,
const char *  password,
const char *  secret 
)

Definition at line 512 of file auth-scram.c.

514 {
515  char *encoded_salt;
516  char *salt;
517  int saltlen;
518  int iterations;
519  int key_length = 0;
520  pg_cryptohash_type hash_type;
521  uint8 salted_password[SCRAM_MAX_KEY_LEN];
522  uint8 stored_key[SCRAM_MAX_KEY_LEN];
523  uint8 server_key[SCRAM_MAX_KEY_LEN];
524  uint8 computed_key[SCRAM_MAX_KEY_LEN];
525  char *prep_password;
526  pg_saslprep_rc rc;
527  const char *errstr = NULL;
528 
529  if (!parse_scram_secret(secret, &iterations, &hash_type, &key_length,
530  &encoded_salt, stored_key, server_key))
531  {
532  /*
533  * The password looked like a SCRAM secret, but could not be parsed.
534  */
535  ereport(LOG,
536  (errmsg("invalid SCRAM secret for user \"%s\"", username)));
537  return false;
538  }
539 
540  saltlen = pg_b64_dec_len(strlen(encoded_salt));
541  salt = palloc(saltlen);
542  saltlen = pg_b64_decode(encoded_salt, strlen(encoded_salt), salt,
543  saltlen);
544  if (saltlen < 0)
545  {
546  ereport(LOG,
547  (errmsg("invalid SCRAM secret for user \"%s\"", username)));
548  return false;
549  }
550 
551  /* Normalize the password */
552  rc = pg_saslprep(password, &prep_password);
553  if (rc == SASLPREP_SUCCESS)
554  password = prep_password;
555 
556  /* Compute Server Key based on the user-supplied plaintext password */
557  if (scram_SaltedPassword(password, hash_type, key_length,
558  salt, saltlen, iterations,
559  salted_password, &errstr) < 0 ||
560  scram_ServerKey(salted_password, hash_type, key_length,
561  computed_key, &errstr) < 0)
562  {
563  elog(ERROR, "could not compute server key: %s", errstr);
564  }
565 
566  if (prep_password)
567  pfree(prep_password);
568 
569  /*
570  * Compare the secret's Server Key with the one computed from the
571  * user-supplied password.
572  */
573  return memcmp(computed_key, server_key, key_length) == 0;
574 }
pg_cryptohash_type
Definition: cryptohash.h:20
int scram_ServerKey(const uint8 *salted_password, pg_cryptohash_type hash_type, int key_length, uint8 *result, const char **errstr)
Definition: scram-common.c:172
int scram_SaltedPassword(const char *password, pg_cryptohash_type hash_type, int key_length, const char *salt, int saltlen, int iterations, uint8 *result, const char **errstr)
Definition: scram-common.c:38

References elog, ereport, errmsg(), ERROR, iterations, LOG, palloc(), parse_scram_secret(), password, pfree(), pg_b64_dec_len(), pg_b64_decode(), pg_saslprep(), SASLPREP_SUCCESS, SCRAM_MAX_KEY_LEN, scram_SaltedPassword(), scram_ServerKey(), and username.

Referenced by plain_crypt_verify().

◆ verify_client_proof()

static bool verify_client_proof ( scram_state state)
static

Definition at line 1135 of file auth-scram.c.

1136 {
1137  uint8 ClientSignature[SCRAM_MAX_KEY_LEN];
1138  uint8 ClientKey[SCRAM_MAX_KEY_LEN];
1139  uint8 client_StoredKey[SCRAM_MAX_KEY_LEN];
1140  pg_hmac_ctx *ctx = pg_hmac_create(state->hash_type);
1141  int i;
1142  const char *errstr = NULL;
1143 
1144  /*
1145  * Calculate ClientSignature. Note that we don't log directly a failure
1146  * here even when processing the calculations as this could involve a mock
1147  * authentication.
1148  */
1149  if (pg_hmac_init(ctx, state->StoredKey, state->key_length) < 0 ||
1150  pg_hmac_update(ctx,
1151  (uint8 *) state->client_first_message_bare,
1152  strlen(state->client_first_message_bare)) < 0 ||
1153  pg_hmac_update(ctx, (uint8 *) ",", 1) < 0 ||
1154  pg_hmac_update(ctx,
1155  (uint8 *) state->server_first_message,
1156  strlen(state->server_first_message)) < 0 ||
1157  pg_hmac_update(ctx, (uint8 *) ",", 1) < 0 ||
1158  pg_hmac_update(ctx,
1159  (uint8 *) state->client_final_message_without_proof,
1160  strlen(state->client_final_message_without_proof)) < 0 ||
1161  pg_hmac_final(ctx, ClientSignature, state->key_length) < 0)
1162  {
1163  elog(ERROR, "could not calculate client signature: %s",
1164  pg_hmac_error(ctx));
1165  }
1166 
1167  pg_hmac_free(ctx);
1168 
1169  /* Extract the ClientKey that the client calculated from the proof */
1170  for (i = 0; i < state->key_length; i++)
1171  ClientKey[i] = state->ClientProof[i] ^ ClientSignature[i];
1172 
1173  /* Hash it one more time, and compare with StoredKey */
1174  if (scram_H(ClientKey, state->hash_type, state->key_length,
1175  client_StoredKey, &errstr) < 0)
1176  elog(ERROR, "could not hash stored key: %s", errstr);
1177 
1178  if (memcmp(client_StoredKey, state->StoredKey, state->key_length) != 0)
1179  return false;
1180 
1181  return true;
1182 }
int scram_H(const uint8 *input, pg_cryptohash_type hash_type, int key_length, uint8 *result, const char **errstr)
Definition: scram-common.c:112

References elog, ERROR, i, if(), pg_hmac_create(), pg_hmac_error(), pg_hmac_final(), pg_hmac_free(), pg_hmac_init(), pg_hmac_update(), scram_H(), and SCRAM_MAX_KEY_LEN.

Referenced by scram_exchange().

◆ verify_final_nonce()

static bool verify_final_nonce ( scram_state state)
static

Definition at line 1113 of file auth-scram.c.

1114 {
1115  int client_nonce_len = strlen(state->client_nonce);
1116  int server_nonce_len = strlen(state->server_nonce);
1117  int final_nonce_len = strlen(state->client_final_nonce);
1118 
1119  if (final_nonce_len != client_nonce_len + server_nonce_len)
1120  return false;
1121  if (memcmp(state->client_final_nonce, state->client_nonce, client_nonce_len) != 0)
1122  return false;
1123  if (memcmp(state->client_final_nonce + client_nonce_len, state->server_nonce, server_nonce_len) != 0)
1124  return false;
1125 
1126  return true;
1127 }

Referenced by scram_exchange().

Variable Documentation

◆ pg_be_scram_mech

const pg_be_sasl_mech pg_be_scram_mech
Initial value:
= {
}
static void * scram_init(Port *port, const char *selected_mech, const char *shadow_pass)
Definition: auth-scram.c:236
static int scram_exchange(void *opaq, const char *input, int inputlen, char **output, int *outputlen, const char **logdetail)
Definition: auth-scram.c:348
static void scram_get_mechanisms(Port *port, StringInfo buf)
Definition: auth-scram.c:202

Definition at line 113 of file auth-scram.c.

Referenced by CheckPWChallengeAuth().

◆ scram_sha_256_iterations

int scram_sha_256_iterations = SCRAM_SHA_256_DEFAULT_ITERATIONS

Definition at line 192 of file auth-scram.c.

Referenced by pg_be_scram_build_secret().