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 void scram_exchange (void *opaq, char *input, int inputlen, char **output, int *outputlen, bool *done, bool *success)
 
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, 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 44 of file fe-auth-scram.c.

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

Function Documentation

◆ build_client_final_message()

static char * build_client_final_message ( fe_scram_state state)
static

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

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

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(), PQExpBufferDataBroken, SCRAM_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 344 of file fe-auth-scram.c.

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

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 759 of file fe-auth-scram.c.

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

References i, pg_hmac_create(), pg_hmac_error(), pg_hmac_final(), pg_hmac_free(), pg_hmac_init(), pg_hmac_update(), PG_SHA256, scram_ClientKey(), scram_H(), SCRAM_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,
const char **  errstr 
)

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

886 {
887  char *prep_password;
888  pg_saslprep_rc rc;
889  char saltbuf[SCRAM_DEFAULT_SALT_LEN];
890  char *result;
891 
892  /*
893  * Normalize the password with SASLprep. If that doesn't work, because
894  * the password isn't valid UTF-8 or contains prohibited characters, just
895  * proceed with the original password. (See comments at the top of
896  * auth-scram.c.)
897  */
898  rc = pg_saslprep(password, &prep_password);
899  if (rc == SASLPREP_OOM)
900  {
901  *errstr = libpq_gettext("out of memory");
902  return NULL;
903  }
904  if (rc == SASLPREP_SUCCESS)
905  password = (const char *) prep_password;
906 
907  /* Generate a random salt */
909  {
910  *errstr = libpq_gettext("could not generate random salt");
911  free(prep_password);
912  return NULL;
913  }
914 
915  result = scram_build_secret(saltbuf, SCRAM_DEFAULT_SALT_LEN,
917  errstr);
918 
919  free(prep_password);
920 
921  return result;
922 }
#define libpq_gettext(x)
Definition: libpq-int.h:882
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(const char *salt, int saltlen, int iterations, const char *password, const char **errstr)
Definition: scram-common.c:195
#define SCRAM_DEFAULT_ITERATIONS
Definition: scram-common.h:47
#define SCRAM_DEFAULT_SALT_LEN
Definition: scram-common.h:41
static char * password
Definition: streamutil.c:53

References free, libpq_gettext, password, pg_saslprep(), pg_strong_random(), SASLPREP_OOM, SASLPREP_SUCCESS, scram_build_secret(), SCRAM_DEFAULT_ITERATIONS, and SCRAM_DEFAULT_SALT_LEN.

Referenced by PQencryptPasswordConn().

◆ read_attr_value()

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

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

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

References 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 687 of file fe-auth-scram.c.

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

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

Referenced by scram_exchange().

◆ read_server_first_message()

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

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

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

References conn, pg_conn::errorMessage, 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 149 of file fe-auth-scram.c.

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

References FE_SCRAM_FINISHED, and SCRAM_SHA_256_PLUS_NAME.

◆ scram_exchange()

static void scram_exchange ( void *  opaq,
char *  input,
int  inputlen,
char **  output,
int *  outputlen,
bool done,
bool success 
)
static

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

203 {
204  fe_scram_state *state = (fe_scram_state *) opaq;
205  PGconn *conn = state->conn;
206  const char *errstr = NULL;
207 
208  *done = false;
209  *success = false;
210  *output = NULL;
211  *outputlen = 0;
212 
213  /*
214  * Check that the input length agrees with the string length of the input.
215  * We can ignore inputlen after this.
216  */
217  if (state->state != FE_SCRAM_INIT)
218  {
219  if (inputlen == 0)
220  {
221  libpq_append_conn_error(conn, "malformed SCRAM message (empty message)");
222  goto error;
223  }
224  if (inputlen != strlen(input))
225  {
226  libpq_append_conn_error(conn, "malformed SCRAM message (length mismatch)");
227  goto error;
228  }
229  }
230 
231  switch (state->state)
232  {
233  case FE_SCRAM_INIT:
234  /* Begin the SCRAM handshake, by sending client nonce */
236  if (*output == NULL)
237  goto error;
238 
239  *outputlen = strlen(*output);
240  *done = false;
241  state->state = FE_SCRAM_NONCE_SENT;
242  break;
243 
244  case FE_SCRAM_NONCE_SENT:
245  /* Receive salt and server nonce, send response. */
246  if (!read_server_first_message(state, input))
247  goto error;
248 
250  if (*output == NULL)
251  goto error;
252 
253  *outputlen = strlen(*output);
254  *done = false;
255  state->state = FE_SCRAM_PROOF_SENT;
256  break;
257 
258  case FE_SCRAM_PROOF_SENT:
259  /* Receive server signature */
260  if (!read_server_final_message(state, input))
261  goto error;
262 
263  /*
264  * Verify server signature, to make sure we're talking to the
265  * genuine server.
266  */
267  if (!verify_server_signature(state, success, &errstr))
268  {
269  libpq_append_conn_error(conn, "could not verify server signature: %s", errstr);
270  goto error;
271  }
272 
273  if (!*success)
274  {
275  libpq_append_conn_error(conn, "incorrect server signature");
276  }
277  *done = true;
278  state->state = FE_SCRAM_FINISHED;
279  break;
280 
281  default:
282  /* shouldn't happen */
283  libpq_append_conn_error(conn, "invalid SCRAM exchange state");
284  goto error;
285  }
286  return;
287 
288 error:
289  *done = true;
290  *success = false;
291 }
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)
static bool success
Definition: initdb.c:170
static void output(uint64 loop_count)
static void error(void)
Definition: sql-dyntest.c:147

References build_client_final_message(), build_client_first_message(), conn, error(), FE_SCRAM_FINISHED, FE_SCRAM_INIT, FE_SCRAM_NONCE_SENT, FE_SCRAM_PROOF_SENT, libpq_append_conn_error(), output(), read_server_final_message(), read_server_first_message(), success, and verify_server_signature().

◆ scram_free()

static void scram_free ( void *  opaq)
static

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

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

References free.

◆ scram_init()

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

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

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

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

◆ verify_server_signature()

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

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

827 {
828  uint8 expected_ServerSignature[SCRAM_KEY_LEN];
829  uint8 ServerKey[SCRAM_KEY_LEN];
830  pg_hmac_ctx *ctx;
831 
832  ctx = pg_hmac_create(PG_SHA256);
833  if (ctx == NULL)
834  {
835  *errstr = pg_hmac_error(NULL); /* returns OOM */
836  return false;
837  }
838 
839  if (scram_ServerKey(state->SaltedPassword, ServerKey, errstr) < 0)
840  {
841  /* errstr is filled already */
842  pg_hmac_free(ctx);
843  return false;
844  }
845 
846  /* calculate ServerSignature */
847  if (pg_hmac_init(ctx, ServerKey, SCRAM_KEY_LEN) < 0 ||
848  pg_hmac_update(ctx,
849  (uint8 *) state->client_first_message_bare,
850  strlen(state->client_first_message_bare)) < 0 ||
851  pg_hmac_update(ctx, (uint8 *) ",", 1) < 0 ||
852  pg_hmac_update(ctx,
853  (uint8 *) state->server_first_message,
854  strlen(state->server_first_message)) < 0 ||
855  pg_hmac_update(ctx, (uint8 *) ",", 1) < 0 ||
856  pg_hmac_update(ctx,
857  (uint8 *) state->client_final_message_without_proof,
858  strlen(state->client_final_message_without_proof)) < 0 ||
859  pg_hmac_final(ctx, expected_ServerSignature,
860  sizeof(expected_ServerSignature)) < 0)
861  {
862  *errstr = pg_hmac_error(ctx);
863  pg_hmac_free(ctx);
864  return false;
865  }
866 
867  pg_hmac_free(ctx);
868 
869  /* signature processed, so now check after it */
870  if (memcmp(expected_ServerSignature, state->ServerSignature, SCRAM_KEY_LEN) != 0)
871  *match = false;
872  else
873  *match = true;
874 
875  return true;
876 }
int scram_ServerKey(const uint8 *salted_password, uint8 *result, const char **errstr)
Definition: scram-common.c:158

References pg_hmac_create(), pg_hmac_error(), pg_hmac_final(), pg_hmac_free(), pg_hmac_init(), pg_hmac_update(), PG_SHA256, SCRAM_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:93
static void scram_free(void *opaq)
static void scram_exchange(void *opaq, char *input, int inputlen, char **output, int *outputlen, bool *done, bool *success)
static bool scram_channel_bound(void *opaq)

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

Referenced by pg_SASL_init().