PostgreSQL Source Code  git master
fe-auth-scram.c File Reference
#include "postgres_fe.h"
#include "common/base64.h"
#include "common/hmac.h"
#include "common/saslprep.h"
#include "common/scram-common.h"
#include "fe-auth.h"
Include dependency graph for fe-auth-scram.c:

Go to the source code of this file.

Data Structures

struct  fe_scram_state
 

Enumerations

enum  fe_scram_state_enum { FE_SCRAM_INIT , FE_SCRAM_NONCE_SENT , FE_SCRAM_PROOF_SENT , FE_SCRAM_FINISHED }
 

Functions

static void * scram_init (PGconn *conn, const char *password, const char *sasl_mechanism)
 
static SASLStatus scram_exchange (void *opaq, char *input, int inputlen, char **output, int *outputlen)
 
static bool scram_channel_bound (void *opaq)
 
static void scram_free (void *opaq)
 
static bool read_server_first_message (fe_scram_state *state, char *input)
 
static bool read_server_final_message (fe_scram_state *state, char *input)
 
static char * build_client_first_message (fe_scram_state *state)
 
static char * build_client_final_message (fe_scram_state *state)
 
static bool verify_server_signature (fe_scram_state *state, bool *match, const char **errstr)
 
static bool calculate_client_proof (fe_scram_state *state, const char *client_final_message_without_proof, uint8 *result, const char **errstr)
 
static char * read_attr_value (char **input, char attr, PQExpBuffer errorMessage)
 
char * pg_fe_scram_build_secret (const char *password, int iterations, const char **errstr)
 

Variables

const pg_fe_sasl_mech pg_scram_mech
 

Enumeration Type Documentation

◆ fe_scram_state_enum

Enumerator
FE_SCRAM_INIT 
FE_SCRAM_NONCE_SENT 
FE_SCRAM_PROOF_SENT 
FE_SCRAM_FINISHED 

Definition at line 43 of file fe-auth-scram.c.

44 {
fe_scram_state_enum
Definition: fe-auth-scram.c:44
@ FE_SCRAM_PROOF_SENT
Definition: fe-auth-scram.c:47
@ FE_SCRAM_NONCE_SENT
Definition: fe-auth-scram.c:46
@ FE_SCRAM_FINISHED
Definition: fe-auth-scram.c:48
@ FE_SCRAM_INIT
Definition: fe-auth-scram.c:45

Function Documentation

◆ build_client_final_message()

static char * build_client_final_message ( fe_scram_state state)
static

Definition at line 450 of file fe-auth-scram.c.

451 {
453  PGconn *conn = state->conn;
454  uint8 client_proof[SCRAM_MAX_KEY_LEN];
455  char *result;
456  int encoded_len;
457  const char *errstr = NULL;
458 
460 
461  /*
462  * Construct client-final-message-without-proof. We need to remember it
463  * for verifying the server proof in the final step of authentication.
464  *
465  * The channel binding flag handling (p/y/n) must be consistent with
466  * build_client_first_message(), because the server will check that it's
467  * the same flag both times.
468  */
469  if (strcmp(state->sasl_mechanism, SCRAM_SHA_256_PLUS_NAME) == 0)
470  {
471 #ifdef USE_SSL
472  char *cbind_data = NULL;
473  size_t cbind_data_len = 0;
474  size_t cbind_header_len;
475  char *cbind_input;
476  size_t cbind_input_len;
477  int encoded_cbind_len;
478 
479  /* Fetch hash data of server's SSL certificate */
480  cbind_data =
482  &cbind_data_len);
483  if (cbind_data == NULL)
484  {
485  /* error message is already set on error */
487  return NULL;
488  }
489 
490  appendPQExpBufferStr(&buf, "c=");
491 
492  /* p=type,, */
493  cbind_header_len = strlen("p=tls-server-end-point,,");
494  cbind_input_len = cbind_header_len + cbind_data_len;
495  cbind_input = malloc(cbind_input_len);
496  if (!cbind_input)
497  {
498  free(cbind_data);
499  goto oom_error;
500  }
501  memcpy(cbind_input, "p=tls-server-end-point,,", cbind_header_len);
502  memcpy(cbind_input + cbind_header_len, cbind_data, cbind_data_len);
503 
504  encoded_cbind_len = pg_b64_enc_len(cbind_input_len);
505  if (!enlargePQExpBuffer(&buf, encoded_cbind_len))
506  {
507  free(cbind_data);
508  free(cbind_input);
509  goto oom_error;
510  }
511  encoded_cbind_len = pg_b64_encode(cbind_input, cbind_input_len,
512  buf.data + buf.len,
513  encoded_cbind_len);
514  if (encoded_cbind_len < 0)
515  {
516  free(cbind_data);
517  free(cbind_input);
520  "could not encode cbind data for channel binding\n");
521  return NULL;
522  }
523  buf.len += encoded_cbind_len;
524  buf.data[buf.len] = '\0';
525 
526  free(cbind_data);
527  free(cbind_input);
528 #else
529  /*
530  * Chose channel binding, but the SSL library doesn't support it.
531  * Shouldn't happen.
532  */
535  "channel binding not supported by this build\n");
536  return NULL;
537 #endif /* USE_SSL */
538  }
539 #ifdef USE_SSL
540  else if (conn->channel_binding[0] != 'd' && /* disable */
541  conn->ssl_in_use)
542  appendPQExpBufferStr(&buf, "c=eSws"); /* base64 of "y,," */
543 #endif
544  else
545  appendPQExpBufferStr(&buf, "c=biws"); /* base64 of "n,," */
546 
548  goto oom_error;
549 
550  appendPQExpBuffer(&buf, ",r=%s", state->nonce);
552  goto oom_error;
553 
554  state->client_final_message_without_proof = strdup(buf.data);
555  if (state->client_final_message_without_proof == NULL)
556  goto oom_error;
557 
558  /* Append proof to it, to form client-final-message. */
560  state->client_final_message_without_proof,
561  client_proof, &errstr))
562  {
564  libpq_append_conn_error(conn, "could not calculate client proof: %s", errstr);
565  return NULL;
566  }
567 
568  appendPQExpBufferStr(&buf, ",p=");
569  encoded_len = pg_b64_enc_len(state->key_length);
570  if (!enlargePQExpBuffer(&buf, encoded_len))
571  goto oom_error;
572  encoded_len = pg_b64_encode((char *) client_proof,
573  state->key_length,
574  buf.data + buf.len,
575  encoded_len);
576  if (encoded_len < 0)
577  {
579  libpq_append_conn_error(conn, "could not encode client proof");
580  return NULL;
581  }
582  buf.len += encoded_len;
583  buf.data[buf.len] = '\0';
584 
585  result = strdup(buf.data);
586  if (result == NULL)
587  goto oom_error;
588 
590  return result;
591 
592 oom_error:
594  libpq_append_conn_error(conn, "out of memory");
595  return NULL;
596 }
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
static bool calculate_client_proof(fe_scram_state *state, const char *client_final_message_without_proof, uint8 *result, const char **errstr)
void libpq_append_conn_error(PGconn *conn, const char *fmt,...)
Definition: fe-misc.c:1325
char * pgtls_get_peer_certificate_hash(PGconn *conn, size_t *len)
#define free(a)
Definition: header.h:65
#define malloc(a)
Definition: header.h:50
static char * buf
Definition: pg_test_fsync.c:73
void initPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:90
int enlargePQExpBuffer(PQExpBuffer str, size_t needed)
Definition: pqexpbuffer.c:172
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:265
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
Definition: pqexpbuffer.c:367
void termPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:129
#define PQExpBufferDataBroken(buf)
Definition: pqexpbuffer.h:67
#define SCRAM_SHA_256_PLUS_NAME
Definition: scram-common.h:21
#define SCRAM_MAX_KEY_LEN
Definition: scram-common.h:30
PGconn * conn
Definition: streamutil.c:55
char * channel_binding
Definition: libpq-int.h:382
PQExpBufferData errorMessage
Definition: libpq-int.h:621
bool ssl_in_use
Definition: libpq-int.h:551
Definition: regguts.h:323

References appendPQExpBuffer(), appendPQExpBufferStr(), buf, calculate_client_proof(), pg_conn::channel_binding, conn, enlargePQExpBuffer(), pg_conn::errorMessage, free, initPQExpBuffer(), libpq_append_conn_error(), malloc, pg_b64_enc_len(), pg_b64_encode(), pgtls_get_peer_certificate_hash(), PQExpBufferDataBroken, SCRAM_MAX_KEY_LEN, SCRAM_SHA_256_PLUS_NAME, pg_conn::ssl_in_use, and termPQExpBuffer().

Referenced by scram_exchange().

◆ build_client_first_message()

static char * build_client_first_message ( fe_scram_state state)
static

Definition at line 345 of file fe-auth-scram.c.

346 {
347  PGconn *conn = state->conn;
348  char raw_nonce[SCRAM_RAW_NONCE_LEN + 1];
349  char *result;
350  int channel_info_len;
351  int encoded_len;
353 
354  /*
355  * Generate a "raw" nonce. This is converted to ASCII-printable form by
356  * base64-encoding it.
357  */
358  if (!pg_strong_random(raw_nonce, SCRAM_RAW_NONCE_LEN))
359  {
360  libpq_append_conn_error(conn, "could not generate nonce");
361  return NULL;
362  }
363 
364  encoded_len = pg_b64_enc_len(SCRAM_RAW_NONCE_LEN);
365  /* don't forget the zero-terminator */
366  state->client_nonce = malloc(encoded_len + 1);
367  if (state->client_nonce == NULL)
368  {
369  libpq_append_conn_error(conn, "out of memory");
370  return NULL;
371  }
372  encoded_len = pg_b64_encode(raw_nonce, SCRAM_RAW_NONCE_LEN,
373  state->client_nonce, encoded_len);
374  if (encoded_len < 0)
375  {
376  libpq_append_conn_error(conn, "could not encode nonce");
377  return NULL;
378  }
379  state->client_nonce[encoded_len] = '\0';
380 
381  /*
382  * Generate message. The username is left empty as the backend uses the
383  * value provided by the startup packet. Also, as this username is not
384  * prepared with SASLprep, the message parsing would fail if it includes
385  * '=' or ',' characters.
386  */
387 
389 
390  /*
391  * First build the gs2-header with channel binding information.
392  */
393  if (strcmp(state->sasl_mechanism, SCRAM_SHA_256_PLUS_NAME) == 0)
394  {
396  appendPQExpBufferStr(&buf, "p=tls-server-end-point");
397  }
398 #ifdef USE_SSL
399  else if (conn->channel_binding[0] != 'd' && /* disable */
400  conn->ssl_in_use)
401  {
402  /*
403  * Client supports channel binding, but thinks the server does not.
404  */
405  appendPQExpBufferChar(&buf, 'y');
406  }
407 #endif
408  else
409  {
410  /*
411  * Client does not support channel binding, or has disabled it.
412  */
413  appendPQExpBufferChar(&buf, 'n');
414  }
415 
417  goto oom_error;
418 
419  channel_info_len = buf.len;
420 
421  appendPQExpBuffer(&buf, ",,n=,r=%s", state->client_nonce);
423  goto oom_error;
424 
425  /*
426  * The first message content needs to be saved without channel binding
427  * information.
428  */
429  state->client_first_message_bare = strdup(buf.data + channel_info_len + 2);
430  if (!state->client_first_message_bare)
431  goto oom_error;
432 
433  result = strdup(buf.data);
434  if (result == NULL)
435  goto oom_error;
436 
438  return result;
439 
440 oom_error:
442  libpq_append_conn_error(conn, "out of memory");
443  return NULL;
444 }
Assert(fmt[strlen(fmt) - 1] !='\n')
bool pg_strong_random(void *buf, size_t len)
void appendPQExpBufferChar(PQExpBuffer str, char ch)
Definition: pqexpbuffer.c:378
#define SCRAM_RAW_NONCE_LEN
Definition: scram-common.h:37

References appendPQExpBuffer(), appendPQExpBufferChar(), appendPQExpBufferStr(), Assert(), buf, pg_conn::channel_binding, conn, initPQExpBuffer(), libpq_append_conn_error(), malloc, pg_b64_enc_len(), pg_b64_encode(), pg_strong_random(), PQExpBufferDataBroken, SCRAM_RAW_NONCE_LEN, SCRAM_SHA_256_PLUS_NAME, pg_conn::ssl_in_use, and termPQExpBuffer().

Referenced by scram_exchange().

◆ calculate_client_proof()

static bool calculate_client_proof ( fe_scram_state state,
const char *  client_final_message_without_proof,
uint8 result,
const char **  errstr 
)
static

Definition at line 761 of file fe-auth-scram.c.

764 {
765  uint8 StoredKey[SCRAM_MAX_KEY_LEN];
766  uint8 ClientKey[SCRAM_MAX_KEY_LEN];
767  uint8 ClientSignature[SCRAM_MAX_KEY_LEN];
768  int i;
769  pg_hmac_ctx *ctx;
770 
771  ctx = pg_hmac_create(state->hash_type);
772  if (ctx == NULL)
773  {
774  *errstr = pg_hmac_error(NULL); /* returns OOM */
775  return false;
776  }
777 
778  /*
779  * Calculate SaltedPassword, and store it in 'state' so that we can reuse
780  * it later in verify_server_signature.
781  */
782  if (scram_SaltedPassword(state->password, state->hash_type,
783  state->key_length, state->salt, state->saltlen,
784  state->iterations, state->SaltedPassword,
785  errstr) < 0 ||
786  scram_ClientKey(state->SaltedPassword, state->hash_type,
787  state->key_length, ClientKey, errstr) < 0 ||
788  scram_H(ClientKey, state->hash_type, state->key_length,
789  StoredKey, errstr) < 0)
790  {
791  /* errstr is already filled here */
792  pg_hmac_free(ctx);
793  return false;
794  }
795 
796  if (pg_hmac_init(ctx, StoredKey, state->key_length) < 0 ||
797  pg_hmac_update(ctx,
798  (uint8 *) state->client_first_message_bare,
799  strlen(state->client_first_message_bare)) < 0 ||
800  pg_hmac_update(ctx, (uint8 *) ",", 1) < 0 ||
801  pg_hmac_update(ctx,
802  (uint8 *) state->server_first_message,
803  strlen(state->server_first_message)) < 0 ||
804  pg_hmac_update(ctx, (uint8 *) ",", 1) < 0 ||
805  pg_hmac_update(ctx,
806  (uint8 *) client_final_message_without_proof,
807  strlen(client_final_message_without_proof)) < 0 ||
808  pg_hmac_final(ctx, ClientSignature, state->key_length) < 0)
809  {
810  *errstr = pg_hmac_error(ctx);
811  pg_hmac_free(ctx);
812  return false;
813  }
814 
815  for (i = 0; i < state->key_length; i++)
816  result[i] = ClientKey[i] ^ ClientSignature[i];
817 
818  pg_hmac_free(ctx);
819  return true;
820 }
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
int i
Definition: isn.c:73
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
int scram_ClientKey(const uint8 *salted_password, pg_cryptohash_type hash_type, int key_length, uint8 *result, const char **errstr)
Definition: scram-common.c:142
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 i, pg_hmac_create(), pg_hmac_error(), pg_hmac_final(), pg_hmac_free(), pg_hmac_init(), pg_hmac_update(), scram_ClientKey(), scram_H(), SCRAM_MAX_KEY_LEN, and scram_SaltedPassword().

Referenced by build_client_final_message().

◆ pg_fe_scram_build_secret()

char* pg_fe_scram_build_secret ( const char *  password,
int  iterations,
const char **  errstr 
)

Definition at line 892 of file fe-auth-scram.c.

893 {
894  char *prep_password;
895  pg_saslprep_rc rc;
896  char saltbuf[SCRAM_DEFAULT_SALT_LEN];
897  char *result;
898 
899  /*
900  * Normalize the password with SASLprep. If that doesn't work, because
901  * the password isn't valid UTF-8 or contains prohibited characters, just
902  * proceed with the original password. (See comments at the top of
903  * auth-scram.c.)
904  */
905  rc = pg_saslprep(password, &prep_password);
906  if (rc == SASLPREP_OOM)
907  {
908  *errstr = libpq_gettext("out of memory");
909  return NULL;
910  }
911  if (rc == SASLPREP_SUCCESS)
912  password = (const char *) prep_password;
913 
914  /* Generate a random salt */
916  {
917  *errstr = libpq_gettext("could not generate random salt");
918  free(prep_password);
919  return NULL;
920  }
921 
925  errstr);
926 
927  free(prep_password);
928 
929  return result;
930 }
@ PG_SHA256
Definition: cryptohash.h:24
#define libpq_gettext(x)
Definition: libpq-int.h:899
pg_saslprep_rc pg_saslprep(const char *input, char **output)
Definition: saslprep.c:1044
pg_saslprep_rc
Definition: saslprep.h:21
@ SASLPREP_OOM
Definition: saslprep.h:23
@ 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
#define SCRAM_DEFAULT_SALT_LEN
Definition: scram-common.h:44
#define SCRAM_SHA_256_KEY_LEN
Definition: scram-common.h:24
static char * password
Definition: streamutil.c:54
int iterations
Definition: thread-thread.c:39

References free, iterations, libpq_gettext, password, pg_saslprep(), PG_SHA256, pg_strong_random(), SASLPREP_OOM, SASLPREP_SUCCESS, scram_build_secret(), SCRAM_DEFAULT_SALT_LEN, and SCRAM_SHA_256_KEY_LEN.

Referenced by PQencryptPasswordConn().

◆ read_attr_value()

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

Definition at line 303 of file fe-auth-scram.c.

304 {
305  char *begin = *input;
306  char *end;
307 
308  if (*begin != attr)
309  {
310  libpq_append_error(errorMessage,
311  "malformed SCRAM message (attribute \"%c\" expected)",
312  attr);
313  return NULL;
314  }
315  begin++;
316 
317  if (*begin != '=')
318  {
319  libpq_append_error(errorMessage,
320  "malformed SCRAM message (expected character \"=\" for attribute \"%c\")",
321  attr);
322  return NULL;
323  }
324  begin++;
325 
326  end = begin;
327  while (*end && *end != ',')
328  end++;
329 
330  if (*end)
331  {
332  *end = '\0';
333  *input = end + 1;
334  }
335  else
336  *input = end;
337 
338  return begin;
339 }
void libpq_append_error(PQExpBuffer errorMessage, const char *fmt,...)
Definition: fe-misc.c:1296
FILE * input

References input, and libpq_append_error().

Referenced by read_server_final_message(), and read_server_first_message().

◆ read_server_final_message()

static bool read_server_final_message ( fe_scram_state state,
char *  input 
)
static

Definition at line 688 of file fe-auth-scram.c.

689 {
690  PGconn *conn = state->conn;
691  char *encoded_server_signature;
692  char *decoded_server_signature;
693  int server_signature_len;
694 
695  state->server_final_message = strdup(input);
696  if (!state->server_final_message)
697  {
698  libpq_append_conn_error(conn, "out of memory");
699  return false;
700  }
701 
702  /* Check for error result. */
703  if (*input == 'e')
704  {
705  char *errmsg = read_attr_value(&input, 'e',
706  &conn->errorMessage);
707 
708  if (errmsg == NULL)
709  {
710  /* read_attr_value() has appended an error message */
711  return false;
712  }
713  libpq_append_conn_error(conn, "error received from server in SCRAM exchange: %s",
714  errmsg);
715  return false;
716  }
717 
718  /* Parse the message. */
719  encoded_server_signature = read_attr_value(&input, 'v',
720  &conn->errorMessage);
721  if (encoded_server_signature == NULL)
722  {
723  /* read_attr_value() has appended an error message */
724  return false;
725  }
726 
727  if (*input != '\0')
728  libpq_append_conn_error(conn, "malformed SCRAM message (garbage at end of server-final-message)");
729 
730  server_signature_len = pg_b64_dec_len(strlen(encoded_server_signature));
731  decoded_server_signature = malloc(server_signature_len);
732  if (!decoded_server_signature)
733  {
734  libpq_append_conn_error(conn, "out of memory");
735  return false;
736  }
737 
738  server_signature_len = pg_b64_decode(encoded_server_signature,
739  strlen(encoded_server_signature),
740  decoded_server_signature,
741  server_signature_len);
742  if (server_signature_len != state->key_length)
743  {
744  free(decoded_server_signature);
745  libpq_append_conn_error(conn, "malformed SCRAM message (invalid server signature)");
746  return false;
747  }
748  memcpy(state->ServerSignature, decoded_server_signature,
749  state->key_length);
750  free(decoded_server_signature);
751 
752  return true;
753 }
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
int errmsg(const char *fmt,...)
Definition: elog.c:1072
static char * read_attr_value(char **input, char attr, PQExpBuffer errorMessage)

References conn, errmsg(), pg_conn::errorMessage, free, input, libpq_append_conn_error(), malloc, pg_b64_dec_len(), pg_b64_decode(), and read_attr_value().

Referenced by scram_exchange().

◆ read_server_first_message()

static bool read_server_first_message ( fe_scram_state state,
char *  input 
)
static

Definition at line 602 of file fe-auth-scram.c.

603 {
604  PGconn *conn = state->conn;
605  char *iterations_str;
606  char *endptr;
607  char *encoded_salt;
608  char *nonce;
609  int decoded_salt_len;
610 
611  state->server_first_message = strdup(input);
612  if (state->server_first_message == NULL)
613  {
614  libpq_append_conn_error(conn, "out of memory");
615  return false;
616  }
617 
618  /* parse the message */
619  nonce = read_attr_value(&input, 'r',
620  &conn->errorMessage);
621  if (nonce == NULL)
622  {
623  /* read_attr_value() has appended an error string */
624  return false;
625  }
626 
627  /* Verify immediately that the server used our part of the nonce */
628  if (strlen(nonce) < strlen(state->client_nonce) ||
629  memcmp(nonce, state->client_nonce, strlen(state->client_nonce)) != 0)
630  {
631  libpq_append_conn_error(conn, "invalid SCRAM response (nonce mismatch)");
632  return false;
633  }
634 
635  state->nonce = strdup(nonce);
636  if (state->nonce == NULL)
637  {
638  libpq_append_conn_error(conn, "out of memory");
639  return false;
640  }
641 
642  encoded_salt = read_attr_value(&input, 's', &conn->errorMessage);
643  if (encoded_salt == NULL)
644  {
645  /* read_attr_value() has appended an error string */
646  return false;
647  }
648  decoded_salt_len = pg_b64_dec_len(strlen(encoded_salt));
649  state->salt = malloc(decoded_salt_len);
650  if (state->salt == NULL)
651  {
652  libpq_append_conn_error(conn, "out of memory");
653  return false;
654  }
655  state->saltlen = pg_b64_decode(encoded_salt,
656  strlen(encoded_salt),
657  state->salt,
658  decoded_salt_len);
659  if (state->saltlen < 0)
660  {
661  libpq_append_conn_error(conn, "malformed SCRAM message (invalid salt)");
662  return false;
663  }
664 
665  iterations_str = read_attr_value(&input, 'i', &conn->errorMessage);
666  if (iterations_str == NULL)
667  {
668  /* read_attr_value() has appended an error string */
669  return false;
670  }
671  state->iterations = strtol(iterations_str, &endptr, 10);
672  if (*endptr != '\0' || state->iterations < 1)
673  {
674  libpq_append_conn_error(conn, "malformed SCRAM message (invalid iteration count)");
675  return false;
676  }
677 
678  if (*input != '\0')
679  libpq_append_conn_error(conn, "malformed SCRAM message (garbage at end of server-first-message)");
680 
681  return true;
682 }

References conn, pg_conn::errorMessage, input, libpq_append_conn_error(), malloc, pg_b64_dec_len(), pg_b64_decode(), and read_attr_value().

Referenced by scram_exchange().

◆ scram_channel_bound()

static bool scram_channel_bound ( void *  opaq)
static

Definition at line 154 of file fe-auth-scram.c.

155 {
156  fe_scram_state *state = (fe_scram_state *) opaq;
157 
158  /* no SCRAM exchange done */
159  if (state == NULL)
160  return false;
161 
162  /* SCRAM exchange not completed */
163  if (state->state != FE_SCRAM_FINISHED)
164  return false;
165 
166  /* channel binding mechanism not used */
167  if (strcmp(state->sasl_mechanism, SCRAM_SHA_256_PLUS_NAME) != 0)
168  return false;
169 
170  /* all clear! */
171  return true;
172 }

References FE_SCRAM_FINISHED, and SCRAM_SHA_256_PLUS_NAME.

◆ scram_exchange()

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

Definition at line 205 of file fe-auth-scram.c.

207 {
208  fe_scram_state *state = (fe_scram_state *) opaq;
209  PGconn *conn = state->conn;
210  const char *errstr = NULL;
211 
212  *output = NULL;
213  *outputlen = 0;
214 
215  /*
216  * Check that the input length agrees with the string length of the input.
217  * We can ignore inputlen after this.
218  */
219  if (state->state != FE_SCRAM_INIT)
220  {
221  if (inputlen == 0)
222  {
223  libpq_append_conn_error(conn, "malformed SCRAM message (empty message)");
224  return SASL_FAILED;
225  }
226  if (inputlen != strlen(input))
227  {
228  libpq_append_conn_error(conn, "malformed SCRAM message (length mismatch)");
229  return SASL_FAILED;
230  }
231  }
232 
233  switch (state->state)
234  {
235  case FE_SCRAM_INIT:
236  /* Begin the SCRAM handshake, by sending client nonce */
238  if (*output == NULL)
239  return SASL_FAILED;
240 
241  *outputlen = strlen(*output);
242  state->state = FE_SCRAM_NONCE_SENT;
243  return SASL_CONTINUE;
244 
245  case FE_SCRAM_NONCE_SENT:
246  /* Receive salt and server nonce, send response. */
248  return SASL_FAILED;
249 
251  if (*output == NULL)
252  return SASL_FAILED;
253 
254  *outputlen = strlen(*output);
255  state->state = FE_SCRAM_PROOF_SENT;
256  return SASL_CONTINUE;
257 
258  case FE_SCRAM_PROOF_SENT:
259  {
260  bool match;
261 
262  /* Receive server signature */
264  return SASL_FAILED;
265 
266  /*
267  * Verify server signature, to make sure we're talking to the
268  * genuine server.
269  */
270  if (!verify_server_signature(state, &match, &errstr))
271  {
272  libpq_append_conn_error(conn, "could not verify server signature: %s", errstr);
273  return SASL_FAILED;
274  }
275 
276  if (!match)
277  {
278  libpq_append_conn_error(conn, "incorrect server signature");
279  }
280  state->state = FE_SCRAM_FINISHED;
281  state->conn->client_finished_auth = true;
282  return match ? SASL_COMPLETE : SASL_FAILED;
283  }
284 
285  default:
286  /* shouldn't happen */
287  libpq_append_conn_error(conn, "invalid SCRAM exchange state");
288  break;
289  }
290 
291  return SASL_FAILED;
292 }
@ SASL_CONTINUE
Definition: fe-auth-sasl.h:32
@ SASL_COMPLETE
Definition: fe-auth-sasl.h:30
@ SASL_FAILED
Definition: fe-auth-sasl.h:31
static char * build_client_first_message(fe_scram_state *state)
static bool verify_server_signature(fe_scram_state *state, bool *match, const char **errstr)
static bool read_server_first_message(fe_scram_state *state, char *input)
static char * build_client_final_message(fe_scram_state *state)
static bool read_server_final_message(fe_scram_state *state, char *input)
FILE * output

References build_client_final_message(), build_client_first_message(), conn, FE_SCRAM_FINISHED, FE_SCRAM_INIT, FE_SCRAM_NONCE_SENT, FE_SCRAM_PROOF_SENT, input, libpq_append_conn_error(), output, read_server_final_message(), read_server_first_message(), SASL_COMPLETE, SASL_CONTINUE, SASL_FAILED, and verify_server_signature().

◆ scram_free()

static void scram_free ( void *  opaq)
static

Definition at line 178 of file fe-auth-scram.c.

179 {
180  fe_scram_state *state = (fe_scram_state *) opaq;
181 
182  free(state->password);
183  free(state->sasl_mechanism);
184 
185  /* client messages */
186  free(state->client_nonce);
187  free(state->client_first_message_bare);
188  free(state->client_final_message_without_proof);
189 
190  /* first message from server */
191  free(state->server_first_message);
192  free(state->salt);
193  free(state->nonce);
194 
195  /* final message from server */
196  free(state->server_final_message);
197 
198  free(state);
199 }

References free.

◆ scram_init()

static void * scram_init ( PGconn conn,
const char *  password,
const char *  sasl_mechanism 
)
static

Definition at line 96 of file fe-auth-scram.c.

99 {
101  char *prep_password;
102  pg_saslprep_rc rc;
103 
104  Assert(sasl_mechanism != NULL);
105 
106  state = (fe_scram_state *) malloc(sizeof(fe_scram_state));
107  if (!state)
108  return NULL;
109  memset(state, 0, sizeof(fe_scram_state));
110  state->conn = conn;
111  state->state = FE_SCRAM_INIT;
112  state->key_length = SCRAM_SHA_256_KEY_LEN;
113  state->hash_type = PG_SHA256;
114 
115  state->sasl_mechanism = strdup(sasl_mechanism);
116  if (!state->sasl_mechanism)
117  {
118  free(state);
119  return NULL;
120  }
121 
122  /* Normalize the password with SASLprep, if possible */
123  rc = pg_saslprep(password, &prep_password);
124  if (rc == SASLPREP_OOM)
125  {
126  free(state->sasl_mechanism);
127  free(state);
128  return NULL;
129  }
130  if (rc != SASLPREP_SUCCESS)
131  {
132  prep_password = strdup(password);
133  if (!prep_password)
134  {
135  free(state->sasl_mechanism);
136  free(state);
137  return NULL;
138  }
139  }
140  state->password = prep_password;
141 
142  return state;
143 }

References Assert(), conn, FE_SCRAM_INIT, free, malloc, password, pg_saslprep(), PG_SHA256, SASLPREP_OOM, SASLPREP_SUCCESS, and SCRAM_SHA_256_KEY_LEN.

◆ verify_server_signature()

static bool verify_server_signature ( fe_scram_state state,
bool match,
const char **  errstr 
)
static

Definition at line 830 of file fe-auth-scram.c.

832 {
833  uint8 expected_ServerSignature[SCRAM_MAX_KEY_LEN];
834  uint8 ServerKey[SCRAM_MAX_KEY_LEN];
835  pg_hmac_ctx *ctx;
836 
837  ctx = pg_hmac_create(state->hash_type);
838  if (ctx == NULL)
839  {
840  *errstr = pg_hmac_error(NULL); /* returns OOM */
841  return false;
842  }
843 
844  if (scram_ServerKey(state->SaltedPassword, state->hash_type,
845  state->key_length, ServerKey, errstr) < 0)
846  {
847  /* errstr is filled already */
848  pg_hmac_free(ctx);
849  return false;
850  }
851 
852  /* calculate ServerSignature */
853  if (pg_hmac_init(ctx, ServerKey, state->key_length) < 0 ||
854  pg_hmac_update(ctx,
855  (uint8 *) state->client_first_message_bare,
856  strlen(state->client_first_message_bare)) < 0 ||
857  pg_hmac_update(ctx, (uint8 *) ",", 1) < 0 ||
858  pg_hmac_update(ctx,
859  (uint8 *) state->server_first_message,
860  strlen(state->server_first_message)) < 0 ||
861  pg_hmac_update(ctx, (uint8 *) ",", 1) < 0 ||
862  pg_hmac_update(ctx,
863  (uint8 *) state->client_final_message_without_proof,
864  strlen(state->client_final_message_without_proof)) < 0 ||
865  pg_hmac_final(ctx, expected_ServerSignature,
866  state->key_length) < 0)
867  {
868  *errstr = pg_hmac_error(ctx);
869  pg_hmac_free(ctx);
870  return false;
871  }
872 
873  pg_hmac_free(ctx);
874 
875  /* signature processed, so now check after it */
876  if (memcmp(expected_ServerSignature, state->ServerSignature,
877  state->key_length) != 0)
878  *match = false;
879  else
880  *match = true;
881 
882  return true;
883 }
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

References pg_hmac_create(), pg_hmac_error(), pg_hmac_final(), pg_hmac_free(), pg_hmac_init(), pg_hmac_update(), SCRAM_MAX_KEY_LEN, and scram_ServerKey().

Referenced by scram_exchange().

Variable Documentation

◆ pg_scram_mech

const pg_fe_sasl_mech pg_scram_mech
Initial value:
= {
}
static void * scram_init(PGconn *conn, const char *password, const char *sasl_mechanism)
Definition: fe-auth-scram.c:96
static void scram_free(void *opaq)
static bool scram_channel_bound(void *opaq)
static SASLStatus scram_exchange(void *opaq, char *input, int inputlen, char **output, int *outputlen)

Definition at line 32 of file fe-auth-scram.c.

Referenced by pg_SASL_init().