PostgreSQL Source Code  git master
scram.h File Reference
#include "lib/stringinfo.h"
#include "libpq/libpq-be.h"
Include dependency graph for scram.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Macros

#define SASL_EXCHANGE_CONTINUE   0
 
#define SASL_EXCHANGE_SUCCESS   1
 
#define SASL_EXCHANGE_FAILURE   2
 

Functions

void pg_be_scram_get_mechanisms (Port *port, StringInfo buf)
 
void * pg_be_scram_init (Port *port, const char *selected_mech, const char *shadow_pass)
 
int pg_be_scram_exchange (void *opaq, const char *input, int inputlen, char **output, int *outputlen, char **logdetail)
 
char * pg_be_scram_build_secret (const char *password)
 
bool parse_scram_secret (const char *secret, int *iterations, char **salt, uint8 *stored_key, uint8 *server_key)
 
bool scram_verify_plain_password (const char *username, const char *password, const char *secret)
 

Macro Definition Documentation

◆ SASL_EXCHANGE_CONTINUE

#define SASL_EXCHANGE_CONTINUE   0

Definition at line 20 of file scram.h.

Referenced by CheckSCRAMAuth(), and pg_be_scram_exchange().

◆ SASL_EXCHANGE_FAILURE

#define SASL_EXCHANGE_FAILURE   2

Definition at line 22 of file scram.h.

Referenced by pg_be_scram_exchange().

◆ SASL_EXCHANGE_SUCCESS

#define SASL_EXCHANGE_SUCCESS   1

Definition at line 21 of file scram.h.

Referenced by CheckSCRAMAuth(), and pg_be_scram_exchange().

Function Documentation

◆ parse_scram_secret()

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

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

References palloc(), pg_b64_dec_len(), pg_b64_decode(), pstrdup(), and SCRAM_KEY_LEN.

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

558 {
559  char *v;
560  char *p;
561  char *scheme_str;
562  char *salt_str;
563  char *iterations_str;
564  char *storedkey_str;
565  char *serverkey_str;
566  int decoded_len;
567  char *decoded_salt_buf;
568  char *decoded_stored_buf;
569  char *decoded_server_buf;
570 
571  /*
572  * The secret is of form:
573  *
574  * SCRAM-SHA-256$<iterations>:<salt>$<storedkey>:<serverkey>
575  */
576  v = pstrdup(secret);
577  if ((scheme_str = strtok(v, "$")) == NULL)
578  goto invalid_secret;
579  if ((iterations_str = strtok(NULL, ":")) == NULL)
580  goto invalid_secret;
581  if ((salt_str = strtok(NULL, "$")) == NULL)
582  goto invalid_secret;
583  if ((storedkey_str = strtok(NULL, ":")) == NULL)
584  goto invalid_secret;
585  if ((serverkey_str = strtok(NULL, "")) == NULL)
586  goto invalid_secret;
587 
588  /* Parse the fields */
589  if (strcmp(scheme_str, "SCRAM-SHA-256") != 0)
590  goto invalid_secret;
591 
592  errno = 0;
593  *iterations = strtol(iterations_str, &p, 10);
594  if (*p || errno != 0)
595  goto invalid_secret;
596 
597  /*
598  * Verify that the salt is in Base64-encoded format, by decoding it,
599  * although we return the encoded version to the caller.
600  */
601  decoded_len = pg_b64_dec_len(strlen(salt_str));
602  decoded_salt_buf = palloc(decoded_len);
603  decoded_len = pg_b64_decode(salt_str, strlen(salt_str),
604  decoded_salt_buf, decoded_len);
605  if (decoded_len < 0)
606  goto invalid_secret;
607  *salt = pstrdup(salt_str);
608 
609  /*
610  * Decode StoredKey and ServerKey.
611  */
612  decoded_len = pg_b64_dec_len(strlen(storedkey_str));
613  decoded_stored_buf = palloc(decoded_len);
614  decoded_len = pg_b64_decode(storedkey_str, strlen(storedkey_str),
615  decoded_stored_buf, decoded_len);
616  if (decoded_len != SCRAM_KEY_LEN)
617  goto invalid_secret;
618  memcpy(stored_key, decoded_stored_buf, SCRAM_KEY_LEN);
619 
620  decoded_len = pg_b64_dec_len(strlen(serverkey_str));
621  decoded_server_buf = palloc(decoded_len);
622  decoded_len = pg_b64_decode(serverkey_str, strlen(serverkey_str),
623  decoded_server_buf, decoded_len);
624  if (decoded_len != SCRAM_KEY_LEN)
625  goto invalid_secret;
626  memcpy(server_key, decoded_server_buf, SCRAM_KEY_LEN);
627 
628  return true;
629 
630 invalid_secret:
631  *salt = NULL;
632  return false;
633 }
char * pstrdup(const char *in)
Definition: mcxt.c:1186
int pg_b64_dec_len(int srclen)
Definition: base64.c:239
int pg_b64_decode(const char *src, int len, char *dst, int dstlen)
Definition: base64.c:116
void * palloc(Size size)
Definition: mcxt.c:949
#define SCRAM_KEY_LEN
Definition: scram-common.h:23

◆ pg_be_scram_build_secret()

char* pg_be_scram_build_secret ( const char *  password)

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

References ereport, errcode(), errmsg(), ERROR, pfree(), pg_saslprep(), pg_strong_random(), SASLPREP_SUCCESS, scram_build_secret(), SCRAM_DEFAULT_ITERATIONS, and SCRAM_DEFAULT_SALT_LEN.

Referenced by encrypt_password().

452 {
453  char *prep_password;
454  pg_saslprep_rc rc;
455  char saltbuf[SCRAM_DEFAULT_SALT_LEN];
456  char *result;
457 
458  /*
459  * Normalize the password with SASLprep. If that doesn't work, because
460  * the password isn't valid UTF-8 or contains prohibited characters, just
461  * proceed with the original password. (See comments at top of file.)
462  */
463  rc = pg_saslprep(password, &prep_password);
464  if (rc == SASLPREP_SUCCESS)
465  password = (const char *) prep_password;
466 
467  /* Generate random salt */
469  ereport(ERROR,
470  (errcode(ERRCODE_INTERNAL_ERROR),
471  errmsg("could not generate random salt")));
472 
473  result = scram_build_secret(saltbuf, SCRAM_DEFAULT_SALT_LEN,
475 
476  if (prep_password)
477  pfree(prep_password);
478 
479  return result;
480 }
static char password[100]
Definition: streamutil.c:55
int errcode(int sqlerrcode)
Definition: elog.c:570
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:1071
pg_saslprep_rc
Definition: saslprep.h:20
void pfree(void *pointer)
Definition: mcxt.c:1056
#define ERROR
Definition: elog.h:43
#define ereport(elevel, rest)
Definition: elog.h:141
#define SCRAM_DEFAULT_SALT_LEN
Definition: scram-common.h:40
bool pg_strong_random(void *buf, size_t len)
#define SCRAM_DEFAULT_ITERATIONS
Definition: scram-common.h:46
int errmsg(const char *fmt,...)
Definition: elog.c:784

◆ pg_be_scram_exchange()

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

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

References Assert, build_server_final_message(), build_server_first_message(), scram_state::doomed, elog, ereport, errcode(), errdetail(), errmsg(), ERROR, scram_state::logdetail, pstrdup(), read_client_final_message(), read_client_first_message(), SASL_EXCHANGE_CONTINUE, SASL_EXCHANGE_FAILURE, SASL_EXCHANGE_SUCCESS, SCRAM_AUTH_FINISHED, SCRAM_AUTH_INIT, SCRAM_AUTH_SALT_SENT, scram_state::state, verify_client_proof(), and verify_final_nonce().

Referenced by CheckSCRAMAuth().

330 {
331  scram_state *state = (scram_state *) opaq;
332  int result;
333 
334  *output = NULL;
335 
336  /*
337  * If the client didn't include an "Initial Client Response" in the
338  * SASLInitialResponse message, send an empty challenge, to which the
339  * client will respond with the same data that usually comes in the
340  * Initial Client Response.
341  */
342  if (input == NULL)
343  {
344  Assert(state->state == SCRAM_AUTH_INIT);
345 
346  *output = pstrdup("");
347  *outputlen = 0;
348  return SASL_EXCHANGE_CONTINUE;
349  }
350 
351  /*
352  * Check that the input length agrees with the string length of the input.
353  * We can ignore inputlen after this.
354  */
355  if (inputlen == 0)
356  ereport(ERROR,
357  (errcode(ERRCODE_PROTOCOL_VIOLATION),
358  errmsg("malformed SCRAM message"),
359  errdetail("The message is empty.")));
360  if (inputlen != strlen(input))
361  ereport(ERROR,
362  (errcode(ERRCODE_PROTOCOL_VIOLATION),
363  errmsg("malformed SCRAM message"),
364  errdetail("Message length does not match input length.")));
365 
366  switch (state->state)
367  {
368  case SCRAM_AUTH_INIT:
369 
370  /*
371  * Initialization phase. Receive the first message from client
372  * and be sure that it parsed correctly. Then send the challenge
373  * to the client.
374  */
375  read_client_first_message(state, input);
376 
377  /* prepare message to send challenge */
379 
380  state->state = SCRAM_AUTH_SALT_SENT;
381  result = SASL_EXCHANGE_CONTINUE;
382  break;
383 
385 
386  /*
387  * Final phase for the server. Receive the response to the
388  * challenge previously sent, verify, and let the client know that
389  * everything went well (or not).
390  */
391  read_client_final_message(state, input);
392 
393  if (!verify_final_nonce(state))
394  ereport(ERROR,
395  (errcode(ERRCODE_PROTOCOL_VIOLATION),
396  errmsg("invalid SCRAM response"),
397  errdetail("Nonce does not match.")));
398 
399  /*
400  * Now check the final nonce and the client proof.
401  *
402  * If we performed a "mock" authentication that we knew would fail
403  * from the get go, this is where we fail.
404  *
405  * The SCRAM specification includes an error code,
406  * "invalid-proof", for authentication failure, but it also allows
407  * erroring out in an application-specific way. We choose to do
408  * the latter, so that the error message for invalid password is
409  * the same for all authentication methods. The caller will call
410  * ereport(), when we return SASL_EXCHANGE_FAILURE with no output.
411  *
412  * NB: the order of these checks is intentional. We calculate the
413  * client proof even in a mock authentication, even though it's
414  * bound to fail, to thwart timing attacks to determine if a role
415  * with the given name exists or not.
416  */
417  if (!verify_client_proof(state) || state->doomed)
418  {
419  result = SASL_EXCHANGE_FAILURE;
420  break;
421  }
422 
423  /* Build final message for client */
425 
426  /* Success! */
427  result = SASL_EXCHANGE_SUCCESS;
428  state->state = SCRAM_AUTH_FINISHED;
429  break;
430 
431  default:
432  elog(ERROR, "invalid SCRAM exchange state");
433  result = SASL_EXCHANGE_FAILURE;
434  }
435 
436  if (result == SASL_EXCHANGE_FAILURE && state->logdetail && logdetail)
437  *logdetail = state->logdetail;
438 
439  if (*output)
440  *outputlen = strlen(*output);
441 
442  return result;
443 }
static void output(uint64 loop_count)
char * pstrdup(const char *in)
Definition: mcxt.c:1186
#define SASL_EXCHANGE_FAILURE
Definition: scram.h:22
#define SASL_EXCHANGE_SUCCESS
Definition: scram.h:21
int errcode(int sqlerrcode)
Definition: elog.c:570
#define ERROR
Definition: elog.h:43
static void read_client_first_message(scram_state *state, const char *input)
Definition: auth-scram.c:855
static bool verify_client_proof(scram_state *state)
Definition: auth-scram.c:1090
int errdetail(const char *fmt,...)
Definition: elog.c:860
#define ereport(elevel, rest)
Definition: elog.h:141
scram_state_enum state
Definition: auth-scram.c:121
#define Assert(condition)
Definition: c.h:732
Definition: regguts.h:298
static char * build_server_final_message(scram_state *state)
Definition: auth-scram.c:1341
static void read_client_final_message(scram_state *state, const char *input)
Definition: auth-scram.c:1195
int errmsg(const char *fmt,...)
Definition: elog.c:784
#define elog(elevel,...)
Definition: elog.h:226
static bool verify_final_nonce(scram_state *state)
Definition: auth-scram.c:1069
char * logdetail
Definition: auth-scram.c:155
static char * build_server_first_message(scram_state *state)
Definition: auth-scram.c:1131
#define SASL_EXCHANGE_CONTINUE
Definition: scram.h:20

◆ pg_be_scram_get_mechanisms()

void pg_be_scram_get_mechanisms ( Port port,
StringInfo  buf 
)

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

References appendStringInfoChar(), appendStringInfoString(), SCRAM_SHA_256_NAME, SCRAM_SHA_256_PLUS_NAME, and Port::ssl_in_use.

Referenced by CheckSCRAMAuth().

182 {
183  /*
184  * Advertise the mechanisms in decreasing order of importance. So the
185  * channel-binding variants go first, if they are supported. Channel
186  * binding is only supported with SSL, and only if the SSL implementation
187  * has a function to get the certificate's hash.
188  */
189 #ifdef HAVE_BE_TLS_GET_CERTIFICATE_HASH
190  if (port->ssl_in_use)
191  {
193  appendStringInfoChar(buf, '\0');
194  }
195 #endif
197  appendStringInfoChar(buf, '\0');
198 }
#define SCRAM_SHA_256_NAME
Definition: scram-common.h:19
bool ssl_in_use
Definition: libpq-be.h:190
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:163
#define SCRAM_SHA_256_PLUS_NAME
Definition: scram-common.h:20
void appendStringInfoChar(StringInfo str, char ch)
Definition: stringinfo.c:175

◆ pg_be_scram_init()

void* pg_be_scram_init ( Port port,
const char *  selected_mech,
const char *  shadow_pass 
)

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

References _, scram_state::channel_binding_in_use, scram_state::doomed, ereport, errcode(), errmsg(), ERROR, get_password_type(), scram_state::iterations, LOG, scram_state::logdetail, mock_scram_secret(), palloc0(), parse_scram_secret(), PASSWORD_TYPE_SCRAM_SHA_256, port, scram_state::port, psprintf(), scram_state::salt, SCRAM_AUTH_INIT, SCRAM_SHA_256_NAME, SCRAM_SHA_256_PLUS_NAME, scram_state::ServerKey, Port::ssl_in_use, scram_state::state, scram_state::StoredKey, and Port::user_name.

Referenced by CheckSCRAMAuth().

221 {
223  bool got_secret;
224 
225  state = (scram_state *) palloc0(sizeof(scram_state));
226  state->port = port;
227  state->state = SCRAM_AUTH_INIT;
228 
229  /*
230  * Parse the selected mechanism.
231  *
232  * Note that if we don't support channel binding, either because the SSL
233  * implementation doesn't support it or we're not using SSL at all, we
234  * would not have advertised the PLUS variant in the first place. If the
235  * client nevertheless tries to select it, it's a protocol violation like
236  * selecting any other SASL mechanism we don't support.
237  */
238 #ifdef HAVE_BE_TLS_GET_CERTIFICATE_HASH
239  if (strcmp(selected_mech, SCRAM_SHA_256_PLUS_NAME) == 0 && port->ssl_in_use)
240  state->channel_binding_in_use = true;
241  else
242 #endif
243  if (strcmp(selected_mech, SCRAM_SHA_256_NAME) == 0)
244  state->channel_binding_in_use = false;
245  else
246  ereport(ERROR,
247  (errcode(ERRCODE_PROTOCOL_VIOLATION),
248  errmsg("client selected an invalid SASL authentication mechanism")));
249 
250  /*
251  * Parse the stored secret.
252  */
253  if (shadow_pass)
254  {
255  int password_type = get_password_type(shadow_pass);
256 
257  if (password_type == PASSWORD_TYPE_SCRAM_SHA_256)
258  {
259  if (parse_scram_secret(shadow_pass, &state->iterations, &state->salt,
260  state->StoredKey, state->ServerKey))
261  got_secret = true;
262  else
263  {
264  /*
265  * The password looked like a SCRAM secret, but could not be
266  * parsed.
267  */
268  ereport(LOG,
269  (errmsg("invalid SCRAM secret for user \"%s\"",
270  state->port->user_name)));
271  got_secret = false;
272  }
273  }
274  else
275  {
276  /*
277  * The user doesn't have SCRAM secret. (You cannot do SCRAM
278  * authentication with an MD5 hash.)
279  */
280  state->logdetail = psprintf(_("User \"%s\" does not have a valid SCRAM secret."),
281  state->port->user_name);
282  got_secret = false;
283  }
284  }
285  else
286  {
287  /*
288  * The caller requested us to perform a dummy authentication. This is
289  * considered normal, since the caller requested it, so don't set log
290  * detail.
291  */
292  got_secret = false;
293  }
294 
295  /*
296  * If the user did not have a valid SCRAM secret, we still go through
297  * the motions with a mock one, and fail as if the client supplied an
298  * incorrect password. This is to avoid revealing information to an
299  * attacker.
300  */
301  if (!got_secret)
302  {
303  mock_scram_secret(state->port->user_name, &state->iterations,
304  &state->salt, state->StoredKey, state->ServerKey);
305  state->doomed = true;
306  }
307 
308  return state;
309 }
Port * port
Definition: auth-scram.c:125
static void mock_scram_secret(const char *username, int *iterations, char **salt, uint8 *stored_key, uint8 *server_key)
Definition: auth-scram.c:647
#define SCRAM_SHA_256_NAME
Definition: scram-common.h:19
uint8 StoredKey[SCRAM_KEY_LEN]
Definition: auth-scram.c:130
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
int errcode(int sqlerrcode)
Definition: elog.c:570
bool ssl_in_use
Definition: libpq-be.h:190
#define LOG
Definition: elog.h:26
#define ERROR
Definition: elog.h:43
int iterations
Definition: auth-scram.c:128
char * salt
Definition: auth-scram.c:129
#define SCRAM_SHA_256_PLUS_NAME
Definition: scram-common.h:20
char * user_name
Definition: libpq-be.h:141
#define ereport(elevel, rest)
Definition: elog.h:141
static int port
Definition: pg_regress.c:92
scram_state_enum state
Definition: auth-scram.c:121
void * palloc0(Size size)
Definition: mcxt.c:980
Definition: regguts.h:298
bool parse_scram_secret(const char *secret, int *iterations, char **salt, uint8 *stored_key, uint8 *server_key)
Definition: auth-scram.c:556
int errmsg(const char *fmt,...)
Definition: elog.c:784
PasswordType get_password_type(const char *shadow_pass)
Definition: crypt.c:89
char * logdetail
Definition: auth-scram.c:155
bool channel_binding_in_use
Definition: auth-scram.c:126
uint8 ServerKey[SCRAM_KEY_LEN]
Definition: auth-scram.c:131
#define _(x)
Definition: elog.c:84

◆ scram_verify_plain_password()

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

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

References ereport, errmsg(), LOG, palloc(), parse_scram_secret(), pfree(), pg_b64_dec_len(), pg_b64_decode(), pg_saslprep(), SASLPREP_SUCCESS, SCRAM_KEY_LEN, scram_SaltedPassword(), and scram_ServerKey().

Referenced by plain_crypt_verify().

490 {
491  char *encoded_salt;
492  char *salt;
493  int saltlen;
494  int iterations;
495  uint8 salted_password[SCRAM_KEY_LEN];
496  uint8 stored_key[SCRAM_KEY_LEN];
497  uint8 server_key[SCRAM_KEY_LEN];
498  uint8 computed_key[SCRAM_KEY_LEN];
499  char *prep_password;
500  pg_saslprep_rc rc;
501 
502  if (!parse_scram_secret(secret, &iterations, &encoded_salt,
503  stored_key, server_key))
504  {
505  /*
506  * The password looked like a SCRAM secret, but could not be parsed.
507  */
508  ereport(LOG,
509  (errmsg("invalid SCRAM secret for user \"%s\"", username)));
510  return false;
511  }
512 
513  saltlen = pg_b64_dec_len(strlen(encoded_salt));
514  salt = palloc(saltlen);
515  saltlen = pg_b64_decode(encoded_salt, strlen(encoded_salt), salt,
516  saltlen);
517  if (saltlen < 0)
518  {
519  ereport(LOG,
520  (errmsg("invalid SCRAM secret for user \"%s\"", username)));
521  return false;
522  }
523 
524  /* Normalize the password */
525  rc = pg_saslprep(password, &prep_password);
526  if (rc == SASLPREP_SUCCESS)
527  password = prep_password;
528 
529  /* Compute Server Key based on the user-supplied plaintext password */
530  scram_SaltedPassword(password, salt, saltlen, iterations, salted_password);
531  scram_ServerKey(salted_password, computed_key);
532 
533  if (prep_password)
534  pfree(prep_password);
535 
536  /*
537  * Compare the secret's Server Key with the one computed from the
538  * user-supplied password.
539  */
540  return memcmp(computed_key, server_key, SCRAM_KEY_LEN) == 0;
541 }
static char password[100]
Definition: streamutil.c:55
unsigned char uint8
Definition: c.h:356
void scram_SaltedPassword(const char *password, const char *salt, int saltlen, int iterations, uint8 *result)
Definition: scram-common.c:104
pg_saslprep_rc pg_saslprep(const char *input, char **output)
Definition: saslprep.c:1071
#define LOG
Definition: elog.h:26
int pg_b64_dec_len(int srclen)
Definition: base64.c:239
void scram_ServerKey(const uint8 *salted_password, uint8 *result)
Definition: scram-common.c:173
pg_saslprep_rc
Definition: saslprep.h:20
void pfree(void *pointer)
Definition: mcxt.c:1056
#define ereport(elevel, rest)
Definition: elog.h:141
int pg_b64_decode(const char *src, int len, char *dst, int dstlen)
Definition: base64.c:116
static char * username
Definition: initdb.c:133
bool parse_scram_secret(const char *secret, int *iterations, char **salt, uint8 *stored_key, uint8 *server_key)
Definition: auth-scram.c:556
void * palloc(Size size)
Definition: mcxt.c:949
int errmsg(const char *fmt,...)
Definition: elog.c:784
#define SCRAM_KEY_LEN
Definition: scram-common.h:23