PostgreSQL Source Code  git master
fe-auth-scram.c File Reference
#include "postgres_fe.h"
#include "common/base64.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 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)
 
static void calculate_client_proof (fe_scram_state *state, const char *client_final_message_without_proof, uint8 *result)
 
void * pg_fe_scram_init (PGconn *conn, const char *password, const char *sasl_mechanism)
 
bool pg_fe_scram_channel_bound (void *opaq)
 
void pg_fe_scram_free (void *opaq)
 
void pg_fe_scram_exchange (void *opaq, char *input, int inputlen, char **output, int *outputlen, bool *done, bool *success)
 
static char * read_attr_value (char **input, char attr, PQExpBuffer errorMessage)
 
char * pg_fe_scram_build_secret (const char *password)
 

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

Function Documentation

◆ build_client_final_message()

static char * build_client_final_message ( fe_scram_state state)
static

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

References appendPQExpBuffer(), appendPQExpBufferStr(), buf, calculate_client_proof(), pg_conn::channel_binding, fe_scram_state::client_final_message_without_proof, fe_scram_state::conn, conn, PQExpBufferData::data, enlargePQExpBuffer(), pg_conn::errorMessage, free, initPQExpBuffer(), PQExpBufferData::len, libpq_gettext, malloc, fe_scram_state::nonce, pg_b64_enc_len(), pg_b64_encode(), PQExpBufferDataBroken, printfPQExpBuffer(), fe_scram_state::sasl_mechanism, SCRAM_KEY_LEN, SCRAM_SHA_256_PLUS_NAME, pg_conn::ssl_in_use, and termPQExpBuffer().

Referenced by pg_fe_scram_exchange().

440 {
442  PGconn *conn = state->conn;
443  uint8 client_proof[SCRAM_KEY_LEN];
444  char *result;
445  int encoded_len;
446 
447  initPQExpBuffer(&buf);
448 
449  /*
450  * Construct client-final-message-without-proof. We need to remember it
451  * for verifying the server proof in the final step of authentication.
452  *
453  * The channel binding flag handling (p/y/n) must be consistent with
454  * build_client_first_message(), because the server will check that it's
455  * the same flag both times.
456  */
457  if (strcmp(state->sasl_mechanism, SCRAM_SHA_256_PLUS_NAME) == 0)
458  {
459 #ifdef HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH
460  char *cbind_data = NULL;
461  size_t cbind_data_len = 0;
462  size_t cbind_header_len;
463  char *cbind_input;
464  size_t cbind_input_len;
465  int encoded_cbind_len;
466 
467  /* Fetch hash data of server's SSL certificate */
468  cbind_data =
469  pgtls_get_peer_certificate_hash(state->conn,
470  &cbind_data_len);
471  if (cbind_data == NULL)
472  {
473  /* error message is already set on error */
474  termPQExpBuffer(&buf);
475  return NULL;
476  }
477 
478  appendPQExpBufferStr(&buf, "c=");
479 
480  /* p=type,, */
481  cbind_header_len = strlen("p=tls-server-end-point,,");
482  cbind_input_len = cbind_header_len + cbind_data_len;
483  cbind_input = malloc(cbind_input_len);
484  if (!cbind_input)
485  {
486  free(cbind_data);
487  goto oom_error;
488  }
489  memcpy(cbind_input, "p=tls-server-end-point,,", cbind_header_len);
490  memcpy(cbind_input + cbind_header_len, cbind_data, cbind_data_len);
491 
492  encoded_cbind_len = pg_b64_enc_len(cbind_input_len);
493  if (!enlargePQExpBuffer(&buf, encoded_cbind_len))
494  {
495  free(cbind_data);
496  free(cbind_input);
497  goto oom_error;
498  }
499  encoded_cbind_len = pg_b64_encode(cbind_input, cbind_input_len,
500  buf.data + buf.len,
501  encoded_cbind_len);
502  if (encoded_cbind_len < 0)
503  {
504  free(cbind_data);
505  free(cbind_input);
506  termPQExpBuffer(&buf);
508  "could not encode cbind data for channel binding\n");
509  return NULL;
510  }
511  buf.len += encoded_cbind_len;
512  buf.data[buf.len] = '\0';
513 
514  free(cbind_data);
515  free(cbind_input);
516 #else
517  /*
518  * Chose channel binding, but the SSL library doesn't support it.
519  * Shouldn't happen.
520  */
521  termPQExpBuffer(&buf);
523  "channel binding not supported by this build\n");
524  return NULL;
525 #endif /* HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH */
526  }
527 #ifdef HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH
528  else if (conn->channel_binding[0] != 'd' && /* disable */
529  conn->ssl_in_use)
530  appendPQExpBufferStr(&buf, "c=eSws"); /* base64 of "y,," */
531 #endif
532  else
533  appendPQExpBufferStr(&buf, "c=biws"); /* base64 of "n,," */
534 
535  if (PQExpBufferDataBroken(buf))
536  goto oom_error;
537 
538  appendPQExpBuffer(&buf, ",r=%s", state->nonce);
539  if (PQExpBufferDataBroken(buf))
540  goto oom_error;
541 
542  state->client_final_message_without_proof = strdup(buf.data);
543  if (state->client_final_message_without_proof == NULL)
544  goto oom_error;
545 
546  /* Append proof to it, to form client-final-message. */
549  client_proof);
550 
551  appendPQExpBufferStr(&buf, ",p=");
552  encoded_len = pg_b64_enc_len(SCRAM_KEY_LEN);
553  if (!enlargePQExpBuffer(&buf, encoded_len))
554  goto oom_error;
555  encoded_len = pg_b64_encode((char *) client_proof,
557  buf.data + buf.len,
558  encoded_len);
559  if (encoded_len < 0)
560  {
561  termPQExpBuffer(&buf);
563  libpq_gettext("could not encode client proof\n"));
564  return NULL;
565  }
566  buf.len += encoded_len;
567  buf.data[buf.len] = '\0';
568 
569  result = strdup(buf.data);
570  if (result == NULL)
571  goto oom_error;
572 
573  termPQExpBuffer(&buf);
574  return result;
575 
576 oom_error:
577  termPQExpBuffer(&buf);
579  libpq_gettext("out of memory\n"));
580  return NULL;
581 }
void printfPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:237
int pg_b64_encode(const char *src, int len, char *dst, int dstlen)
Definition: base64.c:49
void termPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:131
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
Definition: pqexpbuffer.c:369
unsigned char uint8
Definition: c.h:357
#define malloc(a)
Definition: header.h:50
PGconn * conn
Definition: streamutil.c:54
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:267
static void calculate_client_proof(fe_scram_state *state, const char *client_final_message_without_proof, uint8 *result)
bool ssl_in_use
Definition: libpq-int.h:470
static char * buf
Definition: pg_test_fsync.c:67
#define SCRAM_SHA_256_PLUS_NAME
Definition: scram-common.h:20
char * channel_binding
Definition: libpq-int.h:350
char * client_final_message_without_proof
Definition: fe-auth-scram.c:48
PQExpBufferData errorMessage
Definition: libpq-int.h:524
#define free(a)
Definition: header.h:65
#define PQExpBufferDataBroken(buf)
Definition: pqexpbuffer.h:67
int pg_b64_enc_len(int srclen)
Definition: base64.c:224
char * sasl_mechanism
Definition: fe-auth-scram.c:42
#define SCRAM_KEY_LEN
Definition: scram-common.h:23
void initPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:92
#define libpq_gettext(x)
Definition: libpq-int.h:803
int enlargePQExpBuffer(PQExpBuffer str, size_t needed)
Definition: pqexpbuffer.c:174

◆ build_client_first_message()

static char * build_client_first_message ( fe_scram_state state)
static

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

References appendPQExpBuffer(), appendPQExpBufferChar(), appendPQExpBufferStr(), Assert, buf, pg_conn::channel_binding, fe_scram_state::client_first_message_bare, fe_scram_state::client_nonce, fe_scram_state::conn, conn, PQExpBufferData::data, pg_conn::errorMessage, initPQExpBuffer(), PQExpBufferData::len, libpq_gettext, malloc, pg_b64_enc_len(), pg_b64_encode(), pg_strong_random(), PQExpBufferDataBroken, printfPQExpBuffer(), fe_scram_state::sasl_mechanism, SCRAM_RAW_NONCE_LEN, SCRAM_SHA_256_PLUS_NAME, pg_conn::ssl_in_use, and termPQExpBuffer().

Referenced by pg_fe_scram_exchange().

331 {
332  PGconn *conn = state->conn;
333  char raw_nonce[SCRAM_RAW_NONCE_LEN + 1];
334  char *result;
335  int channel_info_len;
336  int encoded_len;
338 
339  /*
340  * Generate a "raw" nonce. This is converted to ASCII-printable form by
341  * base64-encoding it.
342  */
343  if (!pg_strong_random(raw_nonce, SCRAM_RAW_NONCE_LEN))
344  {
346  libpq_gettext("could not generate nonce\n"));
347  return NULL;
348  }
349 
350  encoded_len = pg_b64_enc_len(SCRAM_RAW_NONCE_LEN);
351  /* don't forget the zero-terminator */
352  state->client_nonce = malloc(encoded_len + 1);
353  if (state->client_nonce == NULL)
354  {
356  libpq_gettext("out of memory\n"));
357  return NULL;
358  }
359  encoded_len = pg_b64_encode(raw_nonce, SCRAM_RAW_NONCE_LEN,
360  state->client_nonce, encoded_len);
361  if (encoded_len < 0)
362  {
364  libpq_gettext("could not encode nonce\n"));
365  return NULL;
366  }
367  state->client_nonce[encoded_len] = '\0';
368 
369  /*
370  * Generate message. The username is left empty as the backend uses the
371  * value provided by the startup packet. Also, as this username is not
372  * prepared with SASLprep, the message parsing would fail if it includes
373  * '=' or ',' characters.
374  */
375 
376  initPQExpBuffer(&buf);
377 
378  /*
379  * First build the gs2-header with channel binding information.
380  */
381  if (strcmp(state->sasl_mechanism, SCRAM_SHA_256_PLUS_NAME) == 0)
382  {
383  Assert(conn->ssl_in_use);
384  appendPQExpBufferStr(&buf, "p=tls-server-end-point");
385  }
386 #ifdef HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH
387  else if (conn->channel_binding[0] != 'd' && /* disable */
388  conn->ssl_in_use)
389  {
390  /*
391  * Client supports channel binding, but thinks the server does not.
392  */
393  appendPQExpBufferChar(&buf, 'y');
394  }
395 #endif
396  else
397  {
398  /*
399  * Client does not support channel binding, or has disabled it.
400  */
401  appendPQExpBufferChar(&buf, 'n');
402  }
403 
404  if (PQExpBufferDataBroken(buf))
405  goto oom_error;
406 
407  channel_info_len = buf.len;
408 
409  appendPQExpBuffer(&buf, ",,n=,r=%s", state->client_nonce);
410  if (PQExpBufferDataBroken(buf))
411  goto oom_error;
412 
413  /*
414  * The first message content needs to be saved without channel binding
415  * information.
416  */
417  state->client_first_message_bare = strdup(buf.data + channel_info_len + 2);
418  if (!state->client_first_message_bare)
419  goto oom_error;
420 
421  result = strdup(buf.data);
422  if (result == NULL)
423  goto oom_error;
424 
425  termPQExpBuffer(&buf);
426  return result;
427 
428 oom_error:
429  termPQExpBuffer(&buf);
431  libpq_gettext("out of memory\n"));
432  return NULL;
433 }
char * client_nonce
Definition: fe-auth-scram.c:46
void printfPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:237
int pg_b64_encode(const char *src, int len, char *dst, int dstlen)
Definition: base64.c:49
void termPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:131
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
Definition: pqexpbuffer.c:369
#define malloc(a)
Definition: header.h:50
PGconn * conn
Definition: streamutil.c:54
void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:267
bool ssl_in_use
Definition: libpq-int.h:470
static char * buf
Definition: pg_test_fsync.c:67
#define SCRAM_SHA_256_PLUS_NAME
Definition: scram-common.h:20
char * channel_binding
Definition: libpq-int.h:350
void appendPQExpBufferChar(PQExpBuffer str, char ch)
Definition: pqexpbuffer.c:380
PQExpBufferData errorMessage
Definition: libpq-int.h:524
#define SCRAM_RAW_NONCE_LEN
Definition: scram-common.h:33
#define PQExpBufferDataBroken(buf)
Definition: pqexpbuffer.h:67
bool pg_strong_random(void *buf, size_t len)
#define Assert(condition)
Definition: c.h:739
char * client_first_message_bare
Definition: fe-auth-scram.c:47
int pg_b64_enc_len(int srclen)
Definition: base64.c:224
char * sasl_mechanism
Definition: fe-auth-scram.c:42
void initPQExpBuffer(PQExpBuffer str)
Definition: pqexpbuffer.c:92
#define libpq_gettext(x)
Definition: libpq-int.h:803

◆ calculate_client_proof()

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

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

References fe_scram_state::client_first_message_bare, i, fe_scram_state::iterations, fe_scram_state::password, fe_scram_state::salt, fe_scram_state::SaltedPassword, fe_scram_state::saltlen, scram_ClientKey(), scram_H(), scram_HMAC_final(), scram_HMAC_init(), scram_HMAC_update(), SCRAM_KEY_LEN, scram_SaltedPassword(), and fe_scram_state::server_first_message.

Referenced by build_client_final_message().

754 {
755  uint8 StoredKey[SCRAM_KEY_LEN];
756  uint8 ClientKey[SCRAM_KEY_LEN];
757  uint8 ClientSignature[SCRAM_KEY_LEN];
758  int i;
759  scram_HMAC_ctx ctx;
760 
761  /*
762  * Calculate SaltedPassword, and store it in 'state' so that we can reuse
763  * it later in verify_server_signature.
764  */
765  scram_SaltedPassword(state->password, state->salt, state->saltlen,
766  state->iterations, state->SaltedPassword);
767 
768  scram_ClientKey(state->SaltedPassword, ClientKey);
769  scram_H(ClientKey, SCRAM_KEY_LEN, StoredKey);
770 
771  scram_HMAC_init(&ctx, StoredKey, SCRAM_KEY_LEN);
772  scram_HMAC_update(&ctx,
774  strlen(state->client_first_message_bare));
775  scram_HMAC_update(&ctx, ",", 1);
776  scram_HMAC_update(&ctx,
777  state->server_first_message,
778  strlen(state->server_first_message));
779  scram_HMAC_update(&ctx, ",", 1);
780  scram_HMAC_update(&ctx,
781  client_final_message_without_proof,
782  strlen(client_final_message_without_proof));
783  scram_HMAC_final(ClientSignature, &ctx);
784 
785  for (i = 0; i < SCRAM_KEY_LEN; i++)
786  result[i] = ClientKey[i] ^ ClientSignature[i];
787 }
void scram_H(const uint8 *input, int len, uint8 *result)
Definition: scram-common.c:147
unsigned char uint8
Definition: c.h:357
void scram_SaltedPassword(const char *password, const char *salt, int saltlen, int iterations, uint8 *result)
Definition: scram-common.c:104
uint8 SaltedPassword[SCRAM_KEY_LEN]
Definition: fe-auth-scram.c:45
void scram_ClientKey(const uint8 *salted_password, uint8 *result)
Definition: scram-common.c:160
char * server_first_message
Definition: fe-auth-scram.c:51
char * client_first_message_bare
Definition: fe-auth-scram.c:47
void scram_HMAC_final(uint8 *result, scram_HMAC_ctx *ctx)
Definition: scram-common.c:85
void scram_HMAC_init(scram_HMAC_ctx *ctx, const uint8 *key, int keylen)
Definition: scram-common.c:35
void scram_HMAC_update(scram_HMAC_ctx *ctx, const char *str, int slen)
Definition: scram-common.c:75
int i
#define SCRAM_KEY_LEN
Definition: scram-common.h:23

◆ pg_fe_scram_build_secret()

char* pg_fe_scram_build_secret ( const char *  password)

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

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

Referenced by PQencryptPasswordConn().

828 {
829  char *prep_password;
830  pg_saslprep_rc rc;
831  char saltbuf[SCRAM_DEFAULT_SALT_LEN];
832  char *result;
833 
834  /*
835  * Normalize the password with SASLprep. If that doesn't work, because
836  * the password isn't valid UTF-8 or contains prohibited characters, just
837  * proceed with the original password. (See comments at top of file.)
838  */
839  rc = pg_saslprep(password, &prep_password);
840  if (rc == SASLPREP_OOM)
841  return NULL;
842  if (rc == SASLPREP_SUCCESS)
843  password = (const char *) prep_password;
844 
845  /* Generate a random salt */
847  {
848  if (prep_password)
849  free(prep_password);
850  return NULL;
851  }
852 
853  result = scram_build_secret(saltbuf, SCRAM_DEFAULT_SALT_LEN,
855 
856  if (prep_password)
857  free(prep_password);
858 
859  return result;
860 }
static char password[100]
Definition: streamutil.c:53
char * scram_build_secret(const char *salt, int saltlen, int iterations, const char *password)
Definition: scram-common.c:192
pg_saslprep_rc pg_saslprep(const char *input, char **output)
Definition: saslprep.c:1065
pg_saslprep_rc
Definition: saslprep.h:20
#define SCRAM_DEFAULT_SALT_LEN
Definition: scram-common.h:40
#define free(a)
Definition: header.h:65
bool pg_strong_random(void *buf, size_t len)
#define SCRAM_DEFAULT_ITERATIONS
Definition: scram-common.h:46

◆ pg_fe_scram_channel_bound()

bool pg_fe_scram_channel_bound ( void *  opaq)

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

References FE_SCRAM_FINISHED, fe_scram_state::sasl_mechanism, SCRAM_SHA_256_PLUS_NAME, and fe_scram_state::state.

Referenced by check_expected_areq().

132 {
133  fe_scram_state *state = (fe_scram_state *) opaq;
134 
135  /* no SCRAM exchange done */
136  if (state == NULL)
137  return false;
138 
139  /* SCRAM exchange not completed */
140  if (state->state != FE_SCRAM_FINISHED)
141  return false;
142 
143  /* channel binding mechanism not used */
144  if (strcmp(state->sasl_mechanism, SCRAM_SHA_256_PLUS_NAME) != 0)
145  return false;
146 
147  /* all clear! */
148  return true;
149 }
#define SCRAM_SHA_256_PLUS_NAME
Definition: scram-common.h:20
Definition: regguts.h:298
char * sasl_mechanism
Definition: fe-auth-scram.c:42
fe_scram_state_enum state
Definition: fe-auth-scram.c:37

◆ pg_fe_scram_exchange()

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

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

References build_client_final_message(), build_client_first_message(), fe_scram_state::conn, conn, error(), pg_conn::errorMessage, FE_SCRAM_FINISHED, FE_SCRAM_INIT, FE_SCRAM_NONCE_SENT, FE_SCRAM_PROOF_SENT, libpq_gettext, printfPQExpBuffer(), read_server_final_message(), read_server_first_message(), fe_scram_state::state, and verify_server_signature().

Referenced by pg_SASL_continue(), and pg_SASL_init().

194 {
195  fe_scram_state *state = (fe_scram_state *) opaq;
196  PGconn *conn = state->conn;
197 
198  *done = false;
199  *success = false;
200  *output = NULL;
201  *outputlen = 0;
202 
203  /*
204  * Check that the input length agrees with the string length of the input.
205  * We can ignore inputlen after this.
206  */
207  if (state->state != FE_SCRAM_INIT)
208  {
209  if (inputlen == 0)
210  {
212  libpq_gettext("malformed SCRAM message (empty message)\n"));
213  goto error;
214  }
215  if (inputlen != strlen(input))
216  {
218  libpq_gettext("malformed SCRAM message (length mismatch)\n"));
219  goto error;
220  }
221  }
222 
223  switch (state->state)
224  {
225  case FE_SCRAM_INIT:
226  /* Begin the SCRAM handshake, by sending client nonce */
228  if (*output == NULL)
229  goto error;
230 
231  *outputlen = strlen(*output);
232  *done = false;
233  state->state = FE_SCRAM_NONCE_SENT;
234  break;
235 
236  case FE_SCRAM_NONCE_SENT:
237  /* Receive salt and server nonce, send response. */
238  if (!read_server_first_message(state, input))
239  goto error;
240 
242  if (*output == NULL)
243  goto error;
244 
245  *outputlen = strlen(*output);
246  *done = false;
247  state->state = FE_SCRAM_PROOF_SENT;
248  break;
249 
250  case FE_SCRAM_PROOF_SENT:
251  /* Receive server signature */
252  if (!read_server_final_message(state, input))
253  goto error;
254 
255  /*
256  * Verify server signature, to make sure we're talking to the
257  * genuine server.
258  */
259  if (verify_server_signature(state))
260  *success = true;
261  else
262  {
263  *success = false;
265  libpq_gettext("incorrect server signature\n"));
266  }
267  *done = true;
268  state->state = FE_SCRAM_FINISHED;
269  break;
270 
271  default:
272  /* shouldn't happen */
274  libpq_gettext("invalid SCRAM exchange state\n"));
275  goto error;
276  }
277  return;
278 
279 error:
280  *done = true;
281  *success = false;
282 }
void printfPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:237
static void error(void)
Definition: sql-dyntest.c:147
static void output(uint64 loop_count)
static char * build_client_final_message(fe_scram_state *state)
PGconn * conn
Definition: streamutil.c:54
static bool read_server_final_message(fe_scram_state *state, char *input)
PQExpBufferData errorMessage
Definition: libpq-int.h:524
Definition: regguts.h:298
static bool read_server_first_message(fe_scram_state *state, char *input)
static char * build_client_first_message(fe_scram_state *state)
static bool verify_server_signature(fe_scram_state *state)
static bool success
Definition: initdb.c:161
#define libpq_gettext(x)
Definition: libpq-int.h:803
fe_scram_state_enum state
Definition: fe-auth-scram.c:37

◆ pg_fe_scram_free()

void pg_fe_scram_free ( void *  opaq)

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

References fe_scram_state::client_final_message_without_proof, fe_scram_state::client_first_message_bare, fe_scram_state::client_nonce, free, fe_scram_state::nonce, fe_scram_state::password, fe_scram_state::salt, fe_scram_state::sasl_mechanism, fe_scram_state::server_final_message, and fe_scram_state::server_first_message.

Referenced by pqDropConnection().

156 {
157  fe_scram_state *state = (fe_scram_state *) opaq;
158 
159  if (state->password)
160  free(state->password);
161  if (state->sasl_mechanism)
162  free(state->sasl_mechanism);
163 
164  /* client messages */
165  if (state->client_nonce)
166  free(state->client_nonce);
167  if (state->client_first_message_bare)
171 
172  /* first message from server */
173  if (state->server_first_message)
174  free(state->server_first_message);
175  if (state->salt)
176  free(state->salt);
177  if (state->nonce)
178  free(state->nonce);
179 
180  /* final message from server */
181  if (state->server_final_message)
182  free(state->server_final_message);
183 
184  free(state);
185 }
char * client_nonce
Definition: fe-auth-scram.c:46
char * server_first_message
Definition: fe-auth-scram.c:51
char * client_final_message_without_proof
Definition: fe-auth-scram.c:48
#define free(a)
Definition: header.h:65
Definition: regguts.h:298
char * client_first_message_bare
Definition: fe-auth-scram.c:47
char * sasl_mechanism
Definition: fe-auth-scram.c:42
char * server_final_message
Definition: fe-auth-scram.c:58

◆ pg_fe_scram_init()

void* pg_fe_scram_init ( PGconn conn,
const char *  password,
const char *  sasl_mechanism 
)

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

References Assert, fe_scram_state::conn, conn, FE_SCRAM_INIT, free, malloc, fe_scram_state::password, pg_saslprep(), fe_scram_state::sasl_mechanism, SASLPREP_OOM, SASLPREP_SUCCESS, and fe_scram_state::state.

Referenced by pg_SASL_init().

78 {
80  char *prep_password;
81  pg_saslprep_rc rc;
82 
83  Assert(sasl_mechanism != NULL);
84 
85  state = (fe_scram_state *) malloc(sizeof(fe_scram_state));
86  if (!state)
87  return NULL;
88  memset(state, 0, sizeof(fe_scram_state));
89  state->conn = conn;
90  state->state = FE_SCRAM_INIT;
91  state->sasl_mechanism = strdup(sasl_mechanism);
92 
93  if (!state->sasl_mechanism)
94  {
95  free(state);
96  return NULL;
97  }
98 
99  /* Normalize the password with SASLprep, if possible */
100  rc = pg_saslprep(password, &prep_password);
101  if (rc == SASLPREP_OOM)
102  {
103  free(state->sasl_mechanism);
104  free(state);
105  return NULL;
106  }
107  if (rc != SASLPREP_SUCCESS)
108  {
109  prep_password = strdup(password);
110  if (!prep_password)
111  {
112  free(state->sasl_mechanism);
113  free(state);
114  return NULL;
115  }
116  }
117  state->password = prep_password;
118 
119  return state;
120 }
static char password[100]
Definition: streamutil.c:53
pg_saslprep_rc pg_saslprep(const char *input, char **output)
Definition: saslprep.c:1065
#define malloc(a)
Definition: header.h:50
pg_saslprep_rc
Definition: saslprep.h:20
PGconn * conn
Definition: streamutil.c:54
#define free(a)
Definition: header.h:65
#define Assert(condition)
Definition: c.h:739
Definition: regguts.h:298
char * sasl_mechanism
Definition: fe-auth-scram.c:42
fe_scram_state_enum state
Definition: fe-auth-scram.c:37

◆ read_attr_value()

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

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

References libpq_gettext, and printfPQExpBuffer().

Referenced by read_server_final_message(), and read_server_first_message().

289 {
290  char *begin = *input;
291  char *end;
292 
293  if (*begin != attr)
294  {
295  printfPQExpBuffer(errorMessage,
296  libpq_gettext("malformed SCRAM message (attribute \"%c\" expected)\n"),
297  attr);
298  return NULL;
299  }
300  begin++;
301 
302  if (*begin != '=')
303  {
304  printfPQExpBuffer(errorMessage,
305  libpq_gettext("malformed SCRAM message (expected character \"=\" for attribute \"%c\")\n"),
306  attr);
307  return NULL;
308  }
309  begin++;
310 
311  end = begin;
312  while (*end && *end != ',')
313  end++;
314 
315  if (*end)
316  {
317  *end = '\0';
318  *input = end + 1;
319  }
320  else
321  *input = end;
322 
323  return begin;
324 }
void printfPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:237
#define libpq_gettext(x)
Definition: libpq-int.h:803

◆ read_server_final_message()

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

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

References fe_scram_state::conn, conn, errmsg(), pg_conn::errorMessage, free, libpq_gettext, malloc, pg_b64_dec_len(), pg_b64_decode(), printfPQExpBuffer(), read_attr_value(), SCRAM_KEY_LEN, fe_scram_state::server_final_message, and fe_scram_state::ServerSignature.

Referenced by pg_fe_scram_exchange().

681 {
682  PGconn *conn = state->conn;
683  char *encoded_server_signature;
684  char *decoded_server_signature;
685  int server_signature_len;
686 
687  state->server_final_message = strdup(input);
688  if (!state->server_final_message)
689  {
691  libpq_gettext("out of memory\n"));
692  return false;
693  }
694 
695  /* Check for error result. */
696  if (*input == 'e')
697  {
698  char *errmsg = read_attr_value(&input, 'e',
699  &conn->errorMessage);
700 
702  libpq_gettext("error received from server in SCRAM exchange: %s\n"),
703  errmsg);
704  return false;
705  }
706 
707  /* Parse the message. */
708  encoded_server_signature = read_attr_value(&input, 'v',
709  &conn->errorMessage);
710  if (encoded_server_signature == NULL)
711  {
712  /* read_attr_value() has generated an error message */
713  return false;
714  }
715 
716  if (*input != '\0')
718  libpq_gettext("malformed SCRAM message (garbage at end of server-final-message)\n"));
719 
720  server_signature_len = pg_b64_dec_len(strlen(encoded_server_signature));
721  decoded_server_signature = malloc(server_signature_len);
722  if (!decoded_server_signature)
723  {
725  libpq_gettext("out of memory\n"));
726  return false;
727  }
728 
729  server_signature_len = pg_b64_decode(encoded_server_signature,
730  strlen(encoded_server_signature),
731  decoded_server_signature,
732  server_signature_len);
733  if (server_signature_len != SCRAM_KEY_LEN)
734  {
735  free(decoded_server_signature);
737  libpq_gettext("malformed SCRAM message (invalid server signature)\n"));
738  return false;
739  }
740  memcpy(state->ServerSignature, decoded_server_signature, SCRAM_KEY_LEN);
741  free(decoded_server_signature);
742 
743  return true;
744 }
void printfPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:237
char ServerSignature[SCRAM_KEY_LEN]
Definition: fe-auth-scram.c:59
int pg_b64_dec_len(int srclen)
Definition: base64.c:239
#define malloc(a)
Definition: header.h:50
PGconn * conn
Definition: streamutil.c:54
int pg_b64_decode(const char *src, int len, char *dst, int dstlen)
Definition: base64.c:116
PQExpBufferData errorMessage
Definition: libpq-int.h:524
#define free(a)
Definition: header.h:65
int errmsg(const char *fmt,...)
Definition: elog.c:822
char * server_final_message
Definition: fe-auth-scram.c:58
#define SCRAM_KEY_LEN
Definition: scram-common.h:23
static char * read_attr_value(char **input, char attr, PQExpBuffer errorMessage)
#define libpq_gettext(x)
Definition: libpq-int.h:803

◆ read_server_first_message()

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

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

References fe_scram_state::client_nonce, fe_scram_state::conn, conn, pg_conn::errorMessage, fe_scram_state::iterations, libpq_gettext, malloc, fe_scram_state::nonce, pg_b64_dec_len(), pg_b64_decode(), printfPQExpBuffer(), read_attr_value(), fe_scram_state::salt, fe_scram_state::saltlen, and fe_scram_state::server_first_message.

Referenced by pg_fe_scram_exchange().

588 {
589  PGconn *conn = state->conn;
590  char *iterations_str;
591  char *endptr;
592  char *encoded_salt;
593  char *nonce;
594  int decoded_salt_len;
595 
596  state->server_first_message = strdup(input);
597  if (state->server_first_message == NULL)
598  {
600  libpq_gettext("out of memory\n"));
601  return false;
602  }
603 
604  /* parse the message */
605  nonce = read_attr_value(&input, 'r',
606  &conn->errorMessage);
607  if (nonce == NULL)
608  {
609  /* read_attr_value() has generated an error string */
610  return false;
611  }
612 
613  /* Verify immediately that the server used our part of the nonce */
614  if (strlen(nonce) < strlen(state->client_nonce) ||
615  memcmp(nonce, state->client_nonce, strlen(state->client_nonce)) != 0)
616  {
618  libpq_gettext("invalid SCRAM response (nonce mismatch)\n"));
619  return false;
620  }
621 
622  state->nonce = strdup(nonce);
623  if (state->nonce == NULL)
624  {
626  libpq_gettext("out of memory\n"));
627  return false;
628  }
629 
630  encoded_salt = read_attr_value(&input, 's', &conn->errorMessage);
631  if (encoded_salt == NULL)
632  {
633  /* read_attr_value() has generated an error string */
634  return false;
635  }
636  decoded_salt_len = pg_b64_dec_len(strlen(encoded_salt));
637  state->salt = malloc(decoded_salt_len);
638  if (state->salt == NULL)
639  {
641  libpq_gettext("out of memory\n"));
642  return false;
643  }
644  state->saltlen = pg_b64_decode(encoded_salt,
645  strlen(encoded_salt),
646  state->salt,
647  decoded_salt_len);
648  if (state->saltlen < 0)
649  {
651  libpq_gettext("malformed SCRAM message (invalid salt)\n"));
652  return false;
653  }
654 
655  iterations_str = read_attr_value(&input, 'i', &conn->errorMessage);
656  if (iterations_str == NULL)
657  {
658  /* read_attr_value() has generated an error string */
659  return false;
660  }
661  state->iterations = strtol(iterations_str, &endptr, 10);
662  if (*endptr != '\0' || state->iterations < 1)
663  {
665  libpq_gettext("malformed SCRAM message (invalid iteration count)\n"));
666  return false;
667  }
668 
669  if (*input != '\0')
671  libpq_gettext("malformed SCRAM message (garbage at end of server-first-message)\n"));
672 
673  return true;
674 }
char * client_nonce
Definition: fe-auth-scram.c:46
void printfPQExpBuffer(PQExpBuffer str, const char *fmt,...)
Definition: pqexpbuffer.c:237
int pg_b64_dec_len(int srclen)
Definition: base64.c:239
#define malloc(a)
Definition: header.h:50
PGconn * conn
Definition: streamutil.c:54
int pg_b64_decode(const char *src, int len, char *dst, int dstlen)
Definition: base64.c:116
char * server_first_message
Definition: fe-auth-scram.c:51
PQExpBufferData errorMessage
Definition: libpq-int.h:524
static char * read_attr_value(char **input, char attr, PQExpBuffer errorMessage)
#define libpq_gettext(x)
Definition: libpq-int.h:803

◆ verify_server_signature()

static bool verify_server_signature ( fe_scram_state state)
static

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

References fe_scram_state::client_final_message_without_proof, fe_scram_state::client_first_message_bare, fe_scram_state::SaltedPassword, scram_HMAC_final(), scram_HMAC_init(), scram_HMAC_update(), SCRAM_KEY_LEN, scram_ServerKey(), fe_scram_state::server_first_message, and fe_scram_state::ServerSignature.

Referenced by pg_fe_scram_exchange().

795 {
796  uint8 expected_ServerSignature[SCRAM_KEY_LEN];
797  uint8 ServerKey[SCRAM_KEY_LEN];
798  scram_HMAC_ctx ctx;
799 
800  scram_ServerKey(state->SaltedPassword, ServerKey);
801 
802  /* calculate ServerSignature */
803  scram_HMAC_init(&ctx, ServerKey, SCRAM_KEY_LEN);
804  scram_HMAC_update(&ctx,
806  strlen(state->client_first_message_bare));
807  scram_HMAC_update(&ctx, ",", 1);
808  scram_HMAC_update(&ctx,
809  state->server_first_message,
810  strlen(state->server_first_message));
811  scram_HMAC_update(&ctx, ",", 1);
812  scram_HMAC_update(&ctx,
814  strlen(state->client_final_message_without_proof));
815  scram_HMAC_final(expected_ServerSignature, &ctx);
816 
817  if (memcmp(expected_ServerSignature, state->ServerSignature, SCRAM_KEY_LEN) != 0)
818  return false;
819 
820  return true;
821 }
char ServerSignature[SCRAM_KEY_LEN]
Definition: fe-auth-scram.c:59
unsigned char uint8
Definition: c.h:357
uint8 SaltedPassword[SCRAM_KEY_LEN]
Definition: fe-auth-scram.c:45
void scram_ServerKey(const uint8 *salted_password, uint8 *result)
Definition: scram-common.c:173
char * server_first_message
Definition: fe-auth-scram.c:51
char * client_final_message_without_proof
Definition: fe-auth-scram.c:48
char * client_first_message_bare
Definition: fe-auth-scram.c:47
void scram_HMAC_final(uint8 *result, scram_HMAC_ctx *ctx)
Definition: scram-common.c:85
void scram_HMAC_init(scram_HMAC_ctx *ctx, const uint8 *key, int keylen)
Definition: scram-common.c:35
void scram_HMAC_update(scram_HMAC_ctx *ctx, const char *str, int slen)
Definition: scram-common.c:75
#define SCRAM_KEY_LEN
Definition: scram-common.h:23