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

468 {
470  PGconn *conn = state->conn;
471  uint8 client_proof[SCRAM_KEY_LEN];
472  char *result;
473  int encoded_len;
474  const char *errstr = NULL;
475 
477 
478  /*
479  * Construct client-final-message-without-proof. We need to remember it
480  * for verifying the server proof in the final step of authentication.
481  *
482  * The channel binding flag handling (p/y/n) must be consistent with
483  * build_client_first_message(), because the server will check that it's
484  * the same flag both times.
485  */
486  if (strcmp(state->sasl_mechanism, SCRAM_SHA_256_PLUS_NAME) == 0)
487  {
488 #ifdef HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH
489  char *cbind_data = NULL;
490  size_t cbind_data_len = 0;
491  size_t cbind_header_len;
492  char *cbind_input;
493  size_t cbind_input_len;
494  int encoded_cbind_len;
495 
496  /* Fetch hash data of server's SSL certificate */
497  cbind_data =
498  pgtls_get_peer_certificate_hash(state->conn,
499  &cbind_data_len);
500  if (cbind_data == NULL)
501  {
502  /* error message is already set on error */
504  return NULL;
505  }
506 
507  appendPQExpBufferStr(&buf, "c=");
508 
509  /* p=type,, */
510  cbind_header_len = strlen("p=tls-server-end-point,,");
511  cbind_input_len = cbind_header_len + cbind_data_len;
512  cbind_input = malloc(cbind_input_len);
513  if (!cbind_input)
514  {
515  free(cbind_data);
516  goto oom_error;
517  }
518  memcpy(cbind_input, "p=tls-server-end-point,,", cbind_header_len);
519  memcpy(cbind_input + cbind_header_len, cbind_data, cbind_data_len);
520 
521  encoded_cbind_len = pg_b64_enc_len(cbind_input_len);
522  if (!enlargePQExpBuffer(&buf, encoded_cbind_len))
523  {
524  free(cbind_data);
525  free(cbind_input);
526  goto oom_error;
527  }
528  encoded_cbind_len = pg_b64_encode(cbind_input, cbind_input_len,
529  buf.data + buf.len,
530  encoded_cbind_len);
531  if (encoded_cbind_len < 0)
532  {
533  free(cbind_data);
534  free(cbind_input);
537  "could not encode cbind data for channel binding\n");
538  return NULL;
539  }
540  buf.len += encoded_cbind_len;
541  buf.data[buf.len] = '\0';
542 
543  free(cbind_data);
544  free(cbind_input);
545 #else
546  /*
547  * Chose channel binding, but the SSL library doesn't support it.
548  * Shouldn't happen.
549  */
552  "channel binding not supported by this build\n");
553  return NULL;
554 #endif /* HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH */
555  }
556 #ifdef HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH
557  else if (conn->channel_binding[0] != 'd' && /* disable */
558  conn->ssl_in_use)
559  appendPQExpBufferStr(&buf, "c=eSws"); /* base64 of "y,," */
560 #endif
561  else
562  appendPQExpBufferStr(&buf, "c=biws"); /* base64 of "n,," */
563 
565  goto oom_error;
566 
567  appendPQExpBuffer(&buf, ",r=%s", state->nonce);
569  goto oom_error;
570 
571  state->client_final_message_without_proof = strdup(buf.data);
572  if (state->client_final_message_without_proof == NULL)
573  goto oom_error;
574 
575  /* Append proof to it, to form client-final-message. */
577  state->client_final_message_without_proof,
578  client_proof, &errstr))
579  {
582  libpq_gettext("could not calculate client proof: %s\n"),
583  errstr);
584  return NULL;
585  }
586 
587  appendPQExpBufferStr(&buf, ",p=");
588  encoded_len = pg_b64_enc_len(SCRAM_KEY_LEN);
589  if (!enlargePQExpBuffer(&buf, encoded_len))
590  goto oom_error;
591  encoded_len = pg_b64_encode((char *) client_proof,
593  buf.data + buf.len,
594  encoded_len);
595  if (encoded_len < 0)
596  {
599  libpq_gettext("could not encode client proof\n"));
600  return NULL;
601  }
602  buf.len += encoded_len;
603  buf.data[buf.len] = '\0';
604 
605  result = strdup(buf.data);
606  if (result == NULL)
607  goto oom_error;
608 
610  return result;
611 
612 oom_error:
615  libpq_gettext("out of memory\n"));
616  return NULL;
617 }
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:439
static bool calculate_client_proof(fe_scram_state *state, const char *client_final_message_without_proof, uint8 *result, const char **errstr)
#define free(a)
Definition: header.h:65
#define malloc(a)
Definition: header.h:50
#define libpq_gettext(x)
Definition: libpq-int.h:878
static char * buf
Definition: pg_test_fsync.c:67
void initPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:92
int enlargePQExpBuffer(PQExpBuffer str, size_t needed)
Definition: pqexpbuffer.c:174
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:267
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
Definition: pqexpbuffer.c:369
void termPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:131
#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:371
PQExpBufferData errorMessage
Definition: libpq-int.h:585
bool ssl_in_use
Definition: libpq-int.h:517
Definition: regguts.h:318

References appendPQExpBuffer(), appendPQExpBufferStr(), buf, calculate_client_proof(), pg_conn::channel_binding, conn, enlargePQExpBuffer(), pg_conn::errorMessage, free, initPQExpBuffer(), libpq_gettext, 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 358 of file fe-auth-scram.c.

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

References appendPQExpBuffer(), appendPQExpBufferChar(), appendPQExpBufferStr(), Assert(), buf, pg_conn::channel_binding, conn, pg_conn::errorMessage, initPQExpBuffer(), libpq_gettext, 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 793 of file fe-auth-scram.c.

796 {
797  uint8 StoredKey[SCRAM_KEY_LEN];
798  uint8 ClientKey[SCRAM_KEY_LEN];
799  uint8 ClientSignature[SCRAM_KEY_LEN];
800  int i;
801  pg_hmac_ctx *ctx;
802 
803  ctx = pg_hmac_create(PG_SHA256);
804  if (ctx == NULL)
805  {
806  *errstr = pg_hmac_error(NULL); /* returns OOM */
807  return false;
808  }
809 
810  /*
811  * Calculate SaltedPassword, and store it in 'state' so that we can reuse
812  * it later in verify_server_signature.
813  */
814  if (scram_SaltedPassword(state->password, state->salt, state->saltlen,
815  state->iterations, state->SaltedPassword,
816  errstr) < 0 ||
817  scram_ClientKey(state->SaltedPassword, ClientKey, errstr) < 0 ||
818  scram_H(ClientKey, SCRAM_KEY_LEN, StoredKey, errstr) < 0)
819  {
820  /* errstr is already filled here */
821  pg_hmac_free(ctx);
822  return false;
823  }
824 
825  if (pg_hmac_init(ctx, StoredKey, SCRAM_KEY_LEN) < 0 ||
826  pg_hmac_update(ctx,
827  (uint8 *) state->client_first_message_bare,
828  strlen(state->client_first_message_bare)) < 0 ||
829  pg_hmac_update(ctx, (uint8 *) ",", 1) < 0 ||
830  pg_hmac_update(ctx,
831  (uint8 *) state->server_first_message,
832  strlen(state->server_first_message)) < 0 ||
833  pg_hmac_update(ctx, (uint8 *) ",", 1) < 0 ||
834  pg_hmac_update(ctx,
835  (uint8 *) client_final_message_without_proof,
836  strlen(client_final_message_without_proof)) < 0 ||
837  pg_hmac_final(ctx, ClientSignature, sizeof(ClientSignature)) < 0)
838  {
839  *errstr = pg_hmac_error(ctx);
840  pg_hmac_free(ctx);
841  return false;
842  }
843 
844  for (i = 0; i < SCRAM_KEY_LEN; i++)
845  result[i] = ClientKey[i] ^ ClientSignature[i];
846 
847  pg_hmac_free(ctx);
848  return true;
849 }
@ 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 919 of file fe-auth-scram.c.

920 {
921  char *prep_password;
922  pg_saslprep_rc rc;
923  char saltbuf[SCRAM_DEFAULT_SALT_LEN];
924  char *result;
925 
926  /*
927  * Normalize the password with SASLprep. If that doesn't work, because
928  * the password isn't valid UTF-8 or contains prohibited characters, just
929  * proceed with the original password. (See comments at top of file.)
930  */
931  rc = pg_saslprep(password, &prep_password);
932  if (rc == SASLPREP_OOM)
933  {
934  *errstr = _("out of memory");
935  return NULL;
936  }
937  if (rc == SASLPREP_SUCCESS)
938  password = (const char *) prep_password;
939 
940  /* Generate a random salt */
942  {
943  *errstr = _("failed to generate random salt");
944  if (prep_password)
945  free(prep_password);
946  return NULL;
947  }
948 
949  result = scram_build_secret(saltbuf, SCRAM_DEFAULT_SALT_LEN,
951  errstr);
952 
953  if (prep_password)
954  free(prep_password);
955 
956  return result;
957 }
#define _(x)
Definition: elog.c:89
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, 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 316 of file fe-auth-scram.c.

317 {
318  char *begin = *input;
319  char *end;
320 
321  if (*begin != attr)
322  {
323  appendPQExpBuffer(errorMessage,
324  libpq_gettext("malformed SCRAM message (attribute \"%c\" expected)\n"),
325  attr);
326  return NULL;
327  }
328  begin++;
329 
330  if (*begin != '=')
331  {
332  appendPQExpBuffer(errorMessage,
333  libpq_gettext("malformed SCRAM message (expected character \"=\" for attribute \"%c\")\n"),
334  attr);
335  return NULL;
336  }
337  begin++;
338 
339  end = begin;
340  while (*end && *end != ',')
341  end++;
342 
343  if (*end)
344  {
345  *end = '\0';
346  *input = end + 1;
347  }
348  else
349  *input = end;
350 
351  return begin;
352 }

References appendPQExpBuffer(), and libpq_gettext.

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

717 {
718  PGconn *conn = state->conn;
719  char *encoded_server_signature;
720  char *decoded_server_signature;
721  int server_signature_len;
722 
723  state->server_final_message = strdup(input);
724  if (!state->server_final_message)
725  {
727  libpq_gettext("out of memory\n"));
728  return false;
729  }
730 
731  /* Check for error result. */
732  if (*input == 'e')
733  {
734  char *errmsg = read_attr_value(&input, 'e',
735  &conn->errorMessage);
736 
737  if (errmsg == NULL)
738  {
739  /* read_attr_value() has appended an error message */
740  return false;
741  }
743  libpq_gettext("error received from server in SCRAM exchange: %s\n"),
744  errmsg);
745  return false;
746  }
747 
748  /* Parse the message. */
749  encoded_server_signature = read_attr_value(&input, 'v',
750  &conn->errorMessage);
751  if (encoded_server_signature == NULL)
752  {
753  /* read_attr_value() has appended an error message */
754  return false;
755  }
756 
757  if (*input != '\0')
759  libpq_gettext("malformed SCRAM message (garbage at end of server-final-message)\n"));
760 
761  server_signature_len = pg_b64_dec_len(strlen(encoded_server_signature));
762  decoded_server_signature = malloc(server_signature_len);
763  if (!decoded_server_signature)
764  {
766  libpq_gettext("out of memory\n"));
767  return false;
768  }
769 
770  server_signature_len = pg_b64_decode(encoded_server_signature,
771  strlen(encoded_server_signature),
772  decoded_server_signature,
773  server_signature_len);
774  if (server_signature_len != SCRAM_KEY_LEN)
775  {
776  free(decoded_server_signature);
778  libpq_gettext("malformed SCRAM message (invalid server signature)\n"));
779  return false;
780  }
781  memcpy(state->ServerSignature, decoded_server_signature, SCRAM_KEY_LEN);
782  free(decoded_server_signature);
783 
784  return true;
785 }
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:904
static char * read_attr_value(char **input, char attr, PQExpBuffer errorMessage)

References appendPQExpBuffer(), appendPQExpBufferStr(), conn, errmsg(), pg_conn::errorMessage, free, libpq_gettext, 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 623 of file fe-auth-scram.c.

624 {
625  PGconn *conn = state->conn;
626  char *iterations_str;
627  char *endptr;
628  char *encoded_salt;
629  char *nonce;
630  int decoded_salt_len;
631 
632  state->server_first_message = strdup(input);
633  if (state->server_first_message == NULL)
634  {
636  libpq_gettext("out of memory\n"));
637  return false;
638  }
639 
640  /* parse the message */
641  nonce = read_attr_value(&input, 'r',
642  &conn->errorMessage);
643  if (nonce == NULL)
644  {
645  /* read_attr_value() has appended an error string */
646  return false;
647  }
648 
649  /* Verify immediately that the server used our part of the nonce */
650  if (strlen(nonce) < strlen(state->client_nonce) ||
651  memcmp(nonce, state->client_nonce, strlen(state->client_nonce)) != 0)
652  {
654  libpq_gettext("invalid SCRAM response (nonce mismatch)\n"));
655  return false;
656  }
657 
658  state->nonce = strdup(nonce);
659  if (state->nonce == NULL)
660  {
662  libpq_gettext("out of memory\n"));
663  return false;
664  }
665 
666  encoded_salt = read_attr_value(&input, 's', &conn->errorMessage);
667  if (encoded_salt == NULL)
668  {
669  /* read_attr_value() has appended an error string */
670  return false;
671  }
672  decoded_salt_len = pg_b64_dec_len(strlen(encoded_salt));
673  state->salt = malloc(decoded_salt_len);
674  if (state->salt == NULL)
675  {
677  libpq_gettext("out of memory\n"));
678  return false;
679  }
680  state->saltlen = pg_b64_decode(encoded_salt,
681  strlen(encoded_salt),
682  state->salt,
683  decoded_salt_len);
684  if (state->saltlen < 0)
685  {
687  libpq_gettext("malformed SCRAM message (invalid salt)\n"));
688  return false;
689  }
690 
691  iterations_str = read_attr_value(&input, 'i', &conn->errorMessage);
692  if (iterations_str == NULL)
693  {
694  /* read_attr_value() has appended an error string */
695  return false;
696  }
697  state->iterations = strtol(iterations_str, &endptr, 10);
698  if (*endptr != '\0' || state->iterations < 1)
699  {
701  libpq_gettext("malformed SCRAM message (invalid iteration count)\n"));
702  return false;
703  }
704 
705  if (*input != '\0')
707  libpq_gettext("malformed SCRAM message (garbage at end of server-first-message)\n"));
708 
709  return true;
710 }

References appendPQExpBufferStr(), conn, pg_conn::errorMessage, libpq_gettext, 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 209 of file fe-auth-scram.c.

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

References appendPQExpBuffer(), appendPQExpBufferStr(), build_client_final_message(), build_client_first_message(), conn, error(), pg_conn::errorMessage, FE_SCRAM_FINISHED, FE_SCRAM_INIT, FE_SCRAM_NONCE_SENT, FE_SCRAM_PROOF_SENT, libpq_gettext, 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  if (state->password)
178  free(state->password);
179  if (state->sasl_mechanism)
180  free(state->sasl_mechanism);
181 
182  /* client messages */
183  if (state->client_nonce)
184  free(state->client_nonce);
185  if (state->client_first_message_bare)
186  free(state->client_first_message_bare);
187  if (state->client_final_message_without_proof)
188  free(state->client_final_message_without_proof);
189 
190  /* first message from server */
191  if (state->server_first_message)
192  free(state->server_first_message);
193  if (state->salt)
194  free(state->salt);
195  if (state->nonce)
196  free(state->nonce);
197 
198  /* final message from server */
199  if (state->server_final_message)
200  free(state->server_final_message);
201 
202  free(state);
203 }

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

861 {
862  uint8 expected_ServerSignature[SCRAM_KEY_LEN];
863  uint8 ServerKey[SCRAM_KEY_LEN];
864  pg_hmac_ctx *ctx;
865 
866  ctx = pg_hmac_create(PG_SHA256);
867  if (ctx == NULL)
868  {
869  *errstr = pg_hmac_error(NULL); /* returns OOM */
870  return false;
871  }
872 
873  if (scram_ServerKey(state->SaltedPassword, ServerKey, errstr) < 0)
874  {
875  /* errstr is filled already */
876  pg_hmac_free(ctx);
877  return false;
878  }
879 
880  /* calculate ServerSignature */
881  if (pg_hmac_init(ctx, ServerKey, SCRAM_KEY_LEN) < 0 ||
882  pg_hmac_update(ctx,
883  (uint8 *) state->client_first_message_bare,
884  strlen(state->client_first_message_bare)) < 0 ||
885  pg_hmac_update(ctx, (uint8 *) ",", 1) < 0 ||
886  pg_hmac_update(ctx,
887  (uint8 *) state->server_first_message,
888  strlen(state->server_first_message)) < 0 ||
889  pg_hmac_update(ctx, (uint8 *) ",", 1) < 0 ||
890  pg_hmac_update(ctx,
891  (uint8 *) state->client_final_message_without_proof,
892  strlen(state->client_final_message_without_proof)) < 0 ||
893  pg_hmac_final(ctx, expected_ServerSignature,
894  sizeof(expected_ServerSignature)) < 0)
895  {
896  *errstr = pg_hmac_error(ctx);
897  pg_hmac_free(ctx);
898  return false;
899  }
900 
901  pg_hmac_free(ctx);
902 
903  /* signature processed, so now check after it */
904  if (memcmp(expected_ServerSignature, state->ServerSignature, SCRAM_KEY_LEN) != 0)
905  *match = false;
906  else
907  *match = true;
908 
909  return true;
910 }
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().