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

Go to the source code of this file.

Data Structures

struct  fe_scram_state
 

Enumerations

enum  fe_scram_state_enum { FE_SCRAM_INIT , FE_SCRAM_NONCE_SENT , FE_SCRAM_PROOF_SENT , FE_SCRAM_FINISHED }
 

Functions

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

Variables

const pg_fe_sasl_mech pg_scram_mech
 

Enumeration Type Documentation

◆ fe_scram_state_enum

Enumerator
FE_SCRAM_INIT 
FE_SCRAM_NONCE_SENT 
FE_SCRAM_PROOF_SENT 
FE_SCRAM_FINISHED 

Definition at line 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 455 of file fe-auth-scram.c.

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

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

Referenced by scram_exchange().

◆ build_client_first_message()

static char * build_client_first_message ( fe_scram_state state)
static

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

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

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

Referenced by scram_exchange().

◆ calculate_client_proof()

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

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

769{
770 uint8 StoredKey[SCRAM_MAX_KEY_LEN];
771 uint8 ClientKey[SCRAM_MAX_KEY_LEN];
772 uint8 ClientSignature[SCRAM_MAX_KEY_LEN];
773 int i;
774 pg_hmac_ctx *ctx;
775
776 ctx = pg_hmac_create(state->hash_type);
777 if (ctx == NULL)
778 {
779 *errstr = pg_hmac_error(NULL); /* returns OOM */
780 return false;
781 }
782
783 if (state->conn->scram_client_key_binary)
784 {
785 memcpy(ClientKey, state->conn->scram_client_key_binary, SCRAM_MAX_KEY_LEN);
786 }
787 else
788 {
789 /*
790 * Calculate SaltedPassword, and store it in 'state' so that we can
791 * reuse it later in verify_server_signature.
792 */
793 if (scram_SaltedPassword(state->password, state->hash_type,
794 state->key_length, state->salt, state->saltlen,
795 state->iterations, state->SaltedPassword,
796 errstr) < 0 ||
797 scram_ClientKey(state->SaltedPassword, state->hash_type,
798 state->key_length, ClientKey, errstr) < 0)
799 {
800 /* errstr is already filled here */
801 pg_hmac_free(ctx);
802 return false;
803 }
804 }
805
806 if (scram_H(ClientKey, state->hash_type, state->key_length, StoredKey, errstr) < 0)
807 {
808 pg_hmac_free(ctx);
809 return false;
810 }
811
812 if (pg_hmac_init(ctx, StoredKey, state->key_length) < 0 ||
813 pg_hmac_update(ctx,
814 (uint8 *) state->client_first_message_bare,
815 strlen(state->client_first_message_bare)) < 0 ||
816 pg_hmac_update(ctx, (uint8 *) ",", 1) < 0 ||
817 pg_hmac_update(ctx,
818 (uint8 *) state->server_first_message,
819 strlen(state->server_first_message)) < 0 ||
820 pg_hmac_update(ctx, (uint8 *) ",", 1) < 0 ||
821 pg_hmac_update(ctx,
822 (uint8 *) client_final_message_without_proof,
823 strlen(client_final_message_without_proof)) < 0 ||
824 pg_hmac_final(ctx, ClientSignature, state->key_length) < 0)
825 {
826 *errstr = pg_hmac_error(ctx);
827 pg_hmac_free(ctx);
828 return false;
829 }
830
831 for (i = 0; i < state->key_length; i++)
832 result[i] = ClientKey[i] ^ ClientSignature[i];
833
834 pg_hmac_free(ctx);
835 return true;
836}
pg_hmac_ctx * pg_hmac_create(pg_cryptohash_type type)
Definition: hmac.c:77
void pg_hmac_free(pg_hmac_ctx *ctx)
Definition: hmac.c:289
const char * pg_hmac_error(pg_hmac_ctx *ctx)
Definition: hmac.c:306
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:72
int scram_SaltedPassword(const char *password, pg_cryptohash_type hash_type, int key_length, const char *salt, int saltlen, int iterations, uint8 *result, const char **errstr)
Definition: scram-common.c:38
int scram_ClientKey(const uint8 *salted_password, pg_cryptohash_type hash_type, int key_length, uint8 *result, const char **errstr)
Definition: scram-common.c:142
int scram_H(const uint8 *input, pg_cryptohash_type hash_type, int key_length, uint8 *result, const char **errstr)
Definition: scram-common.c:112

References i, pg_hmac_create(), pg_hmac_error(), pg_hmac_final(), pg_hmac_free(), pg_hmac_init(), pg_hmac_update(), scram_ClientKey(), scram_H(), SCRAM_MAX_KEY_LEN, and scram_SaltedPassword().

Referenced by build_client_final_message().

◆ pg_fe_scram_build_secret()

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

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

916{
917 char *prep_password;
919 char saltbuf[SCRAM_DEFAULT_SALT_LEN];
920 char *result;
921
922 /*
923 * Normalize the password with SASLprep. If that doesn't work, because
924 * the password isn't valid UTF-8 or contains prohibited characters, just
925 * proceed with the original password. (See comments at the top of
926 * auth-scram.c.)
927 */
928 rc = pg_saslprep(password, &prep_password);
929 if (rc == SASLPREP_OOM)
930 {
931 *errstr = libpq_gettext("out of memory");
932 return NULL;
933 }
934 if (rc == SASLPREP_SUCCESS)
935 password = (const char *) prep_password;
936
937 /* Generate a random salt */
939 {
940 *errstr = libpq_gettext("could not generate random salt");
941 free(prep_password);
942 return NULL;
943 }
944
948 errstr);
949
950 free(prep_password);
951
952 return result;
953}
@ PG_SHA256
Definition: cryptohash.h:24
#define libpq_gettext(x)
Definition: libpq-int.h:923
pg_saslprep_rc pg_saslprep(const char *input, char **output)
Definition: saslprep.c:1047
pg_saslprep_rc
Definition: saslprep.h:21
@ SASLPREP_OOM
Definition: saslprep.h:23
@ SASLPREP_SUCCESS
Definition: saslprep.h:22
char * scram_build_secret(pg_cryptohash_type hash_type, int key_length, const char *salt, int saltlen, int iterations, const char *password, const char **errstr)
Definition: scram-common.c:209
#define SCRAM_DEFAULT_SALT_LEN
Definition: scram-common.h:44
#define SCRAM_SHA_256_KEY_LEN
Definition: scram-common.h:24
static char * password
Definition: streamutil.c:52
int iterations
Definition: thread-thread.c:39

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

Referenced by PQencryptPasswordConn().

◆ read_attr_value()

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

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

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

References input, and libpq_append_error().

Referenced by read_server_final_message(), and read_server_first_message().

◆ read_server_final_message()

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

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

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

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

Referenced by scram_exchange().

◆ read_server_first_message()

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

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

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

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

Referenced by scram_exchange().

◆ scram_channel_bound()

static bool scram_channel_bound ( void *  opaq)
static

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

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

References FE_SCRAM_FINISHED, and SCRAM_SHA_256_PLUS_NAME.

◆ scram_exchange()

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

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

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

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

◆ scram_free()

static void scram_free ( void *  opaq)
static

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

183{
185
186 free(state->password);
187 free(state->sasl_mechanism);
188
189 /* client messages */
190 free(state->client_nonce);
191 free(state->client_first_message_bare);
192 free(state->client_final_message_without_proof);
193
194 /* first message from server */
195 free(state->server_first_message);
196 free(state->salt);
197 free(state->nonce);
198
199 /* final message from server */
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 97 of file fe-auth-scram.c.

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

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

◆ verify_server_signature()

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

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

848{
849 uint8 expected_ServerSignature[SCRAM_MAX_KEY_LEN];
850 uint8 ServerKey[SCRAM_MAX_KEY_LEN];
851 pg_hmac_ctx *ctx;
852
853 ctx = pg_hmac_create(state->hash_type);
854 if (ctx == NULL)
855 {
856 *errstr = pg_hmac_error(NULL); /* returns OOM */
857 return false;
858 }
859
860 if (state->conn->scram_server_key_binary)
861 {
862 memcpy(ServerKey, state->conn->scram_server_key_binary, SCRAM_MAX_KEY_LEN);
863 }
864 else
865 {
866 if (scram_ServerKey(state->SaltedPassword, state->hash_type,
867 state->key_length, ServerKey, errstr) < 0)
868 {
869 /* errstr is filled already */
870 pg_hmac_free(ctx);
871 return false;
872 }
873 }
874
875 /* calculate ServerSignature */
876 if (pg_hmac_init(ctx, ServerKey, state->key_length) < 0 ||
877 pg_hmac_update(ctx,
878 (uint8 *) state->client_first_message_bare,
879 strlen(state->client_first_message_bare)) < 0 ||
880 pg_hmac_update(ctx, (uint8 *) ",", 1) < 0 ||
881 pg_hmac_update(ctx,
882 (uint8 *) state->server_first_message,
883 strlen(state->server_first_message)) < 0 ||
884 pg_hmac_update(ctx, (uint8 *) ",", 1) < 0 ||
885 pg_hmac_update(ctx,
886 (uint8 *) state->client_final_message_without_proof,
887 strlen(state->client_final_message_without_proof)) < 0 ||
888 pg_hmac_final(ctx, expected_ServerSignature,
889 state->key_length) < 0)
890 {
891 *errstr = pg_hmac_error(ctx);
892 pg_hmac_free(ctx);
893 return false;
894 }
895
896 pg_hmac_free(ctx);
897
898 /* signature processed, so now check after it */
899 if (memcmp(expected_ServerSignature, state->ServerSignature,
900 state->key_length) != 0)
901 *match = false;
902 else
903 *match = true;
904
905 return true;
906}
int scram_ServerKey(const uint8 *salted_password, pg_cryptohash_type hash_type, int key_length, uint8 *result, const char **errstr)
Definition: scram-common.c:172

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

Referenced by scram_exchange().

Variable Documentation

◆ pg_scram_mech

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

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

Referenced by pg_SASL_init(), and pqConnectOptions2().