LCOV - code coverage report
Current view: top level - src/backend/libpq - auth-scram.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 46 287 16.0 %
Date: 2017-09-29 13:40:31 Functions: 3 17 17.6 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * auth-scram.c
       4             :  *    Server-side implementation of the SASL SCRAM-SHA-256 mechanism.
       5             :  *
       6             :  * See the following RFCs for more details:
       7             :  * - RFC 5802: https://tools.ietf.org/html/rfc5802
       8             :  * - RFC 5803: https://tools.ietf.org/html/rfc5803
       9             :  * - RFC 7677: https://tools.ietf.org/html/rfc7677
      10             :  *
      11             :  * Here are some differences:
      12             :  *
      13             :  * - Username from the authentication exchange is not used. The client
      14             :  *   should send an empty string as the username.
      15             :  *
      16             :  * - If the password isn't valid UTF-8, or contains characters prohibited
      17             :  *   by the SASLprep profile, we skip the SASLprep pre-processing and use
      18             :  *   the raw bytes in calculating the hash.
      19             :  *
      20             :  * - Channel binding is not supported yet.
      21             :  *
      22             :  *
      23             :  * The password stored in pg_authid consists of the iteration count, salt,
      24             :  * StoredKey and ServerKey.
      25             :  *
      26             :  * SASLprep usage
      27             :  * --------------
      28             :  *
      29             :  * One notable difference to the SCRAM specification is that while the
      30             :  * specification dictates that the password is in UTF-8, and prohibits
      31             :  * certain characters, we are more lenient.  If the password isn't a valid
      32             :  * UTF-8 string, or contains prohibited characters, the raw bytes are used
      33             :  * to calculate the hash instead, without SASLprep processing.  This is
      34             :  * because PostgreSQL supports other encodings too, and the encoding being
      35             :  * used during authentication is undefined (client_encoding isn't set until
      36             :  * after authentication).  In effect, we try to interpret the password as
      37             :  * UTF-8 and apply SASLprep processing, but if it looks invalid, we assume
      38             :  * that it's in some other encoding.
      39             :  *
      40             :  * In the worst case, we misinterpret a password that's in a different
      41             :  * encoding as being Unicode, because it happens to consists entirely of
      42             :  * valid UTF-8 bytes, and we apply Unicode normalization to it.  As long
      43             :  * as we do that consistently, that will not lead to failed logins.
      44             :  * Fortunately, the UTF-8 byte sequences that are ignored by SASLprep
      45             :  * don't correspond to any commonly used characters in any of the other
      46             :  * supported encodings, so it should not lead to any significant loss in
      47             :  * entropy, even if the normalization is incorrectly applied to a
      48             :  * non-UTF-8 password.
      49             :  *
      50             :  * Error handling
      51             :  * --------------
      52             :  *
      53             :  * Don't reveal user information to an unauthenticated client.  We don't
      54             :  * want an attacker to be able to probe whether a particular username is
      55             :  * valid.  In SCRAM, the server has to read the salt and iteration count
      56             :  * from the user's password verifier, and send it to the client.  To avoid
      57             :  * revealing whether a user exists, when the client tries to authenticate
      58             :  * with a username that doesn't exist, or doesn't have a valid SCRAM
      59             :  * verifier in pg_authid, we create a fake salt and iteration count
      60             :  * on-the-fly, and proceed with the authentication with that.  In the end,
      61             :  * we'll reject the attempt, as if an incorrect password was given.  When
      62             :  * we are performing a "mock" authentication, the 'doomed' flag in
      63             :  * scram_state is set.
      64             :  *
      65             :  * In the error messages, avoid printing strings from the client, unless
      66             :  * you check that they are pure ASCII.  We don't want an unauthenticated
      67             :  * attacker to be able to spam the logs with characters that are not valid
      68             :  * to the encoding being used, whatever that is.  We cannot avoid that in
      69             :  * general, after logging in, but let's do what we can here.
      70             :  *
      71             :  *
      72             :  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
      73             :  * Portions Copyright (c) 1994, Regents of the University of California
      74             :  *
      75             :  * src/backend/libpq/auth-scram.c
      76             :  *
      77             :  *-------------------------------------------------------------------------
      78             :  */
      79             : #include "postgres.h"
      80             : 
      81             : #include <unistd.h>
      82             : 
      83             : #include "access/xlog.h"
      84             : #include "catalog/pg_authid.h"
      85             : #include "catalog/pg_control.h"
      86             : #include "common/base64.h"
      87             : #include "common/saslprep.h"
      88             : #include "common/scram-common.h"
      89             : #include "common/sha2.h"
      90             : #include "libpq/auth.h"
      91             : #include "libpq/crypt.h"
      92             : #include "libpq/scram.h"
      93             : #include "miscadmin.h"
      94             : #include "utils/backend_random.h"
      95             : #include "utils/builtins.h"
      96             : #include "utils/timestamp.h"
      97             : 
      98             : /*
      99             :  * Status data for a SCRAM authentication exchange.  This should be kept
     100             :  * internal to this file.
     101             :  */
     102             : typedef enum
     103             : {
     104             :     SCRAM_AUTH_INIT,
     105             :     SCRAM_AUTH_SALT_SENT,
     106             :     SCRAM_AUTH_FINISHED
     107             : } scram_state_enum;
     108             : 
     109             : typedef struct
     110             : {
     111             :     scram_state_enum state;
     112             : 
     113             :     const char *username;       /* username from startup packet */
     114             : 
     115             :     int         iterations;
     116             :     char       *salt;           /* base64-encoded */
     117             :     uint8       StoredKey[SCRAM_KEY_LEN];
     118             :     uint8       ServerKey[SCRAM_KEY_LEN];
     119             : 
     120             :     /* Fields of the first message from client */
     121             :     char       *client_first_message_bare;
     122             :     char       *client_username;
     123             :     char       *client_nonce;
     124             : 
     125             :     /* Fields from the last message from client */
     126             :     char       *client_final_message_without_proof;
     127             :     char       *client_final_nonce;
     128             :     char        ClientProof[SCRAM_KEY_LEN];
     129             : 
     130             :     /* Fields generated in the server */
     131             :     char       *server_first_message;
     132             :     char       *server_nonce;
     133             : 
     134             :     /*
     135             :      * If something goes wrong during the authentication, or we are performing
     136             :      * a "mock" authentication (see comments at top of file), the 'doomed'
     137             :      * flag is set.  A reason for the failure, for the server log, is put in
     138             :      * 'logdetail'.
     139             :      */
     140             :     bool        doomed;
     141             :     char       *logdetail;
     142             : } scram_state;
     143             : 
     144             : static void read_client_first_message(scram_state *state, char *input);
     145             : static void read_client_final_message(scram_state *state, char *input);
     146             : static char *build_server_first_message(scram_state *state);
     147             : static char *build_server_final_message(scram_state *state);
     148             : static bool verify_client_proof(scram_state *state);
     149             : static bool verify_final_nonce(scram_state *state);
     150             : static bool parse_scram_verifier(const char *verifier, int *iterations,
     151             :                      char **salt, uint8 *stored_key, uint8 *server_key);
     152             : static void mock_scram_verifier(const char *username, int *iterations,
     153             :                     char **salt, uint8 *stored_key, uint8 *server_key);
     154             : static bool is_scram_printable(char *p);
     155             : static char *sanitize_char(char c);
     156             : static char *scram_mock_salt(const char *username);
     157             : 
     158             : /*
     159             :  * pg_be_scram_init
     160             :  *
     161             :  * Initialize a new SCRAM authentication exchange status tracker.  This
     162             :  * needs to be called before doing any exchange.  It will be filled later
     163             :  * after the beginning of the exchange with verifier data.
     164             :  *
     165             :  * 'username' is the username provided by the client in the startup message.
     166             :  * 'shadow_pass' is the role's password verifier, from pg_authid.rolpassword.
     167             :  * If 'shadow_pass' is NULL, we still perform an authentication exchange, but
     168             :  * it will fail, as if an incorrect password was given.
     169             :  */
     170             : void *
     171           0 : pg_be_scram_init(const char *username, const char *shadow_pass)
     172             : {
     173             :     scram_state *state;
     174             :     bool        got_verifier;
     175             : 
     176           0 :     state = (scram_state *) palloc0(sizeof(scram_state));
     177           0 :     state->state = SCRAM_AUTH_INIT;
     178           0 :     state->username = username;
     179             : 
     180             :     /*
     181             :      * Parse the stored password verifier.
     182             :      */
     183           0 :     if (shadow_pass)
     184             :     {
     185           0 :         int         password_type = get_password_type(shadow_pass);
     186             : 
     187           0 :         if (password_type == PASSWORD_TYPE_SCRAM_SHA_256)
     188             :         {
     189           0 :             if (parse_scram_verifier(shadow_pass, &state->iterations, &state->salt,
     190           0 :                                      state->StoredKey, state->ServerKey))
     191           0 :                 got_verifier = true;
     192             :             else
     193             :             {
     194             :                 /*
     195             :                  * The password looked like a SCRAM verifier, but could not be
     196             :                  * parsed.
     197             :                  */
     198           0 :                 ereport(LOG,
     199             :                         (errmsg("invalid SCRAM verifier for user \"%s\"",
     200             :                                 username)));
     201           0 :                 got_verifier = false;
     202             :             }
     203             :         }
     204             :         else
     205             :         {
     206             :             /*
     207             :              * The user doesn't have SCRAM verifier. (You cannot do SCRAM
     208             :              * authentication with an MD5 hash.)
     209             :              */
     210           0 :             state->logdetail = psprintf(_("User \"%s\" does not have a valid SCRAM verifier."),
     211             :                                         state->username);
     212           0 :             got_verifier = false;
     213             :         }
     214             :     }
     215             :     else
     216             :     {
     217             :         /*
     218             :          * The caller requested us to perform a dummy authentication.  This is
     219             :          * considered normal, since the caller requested it, so don't set log
     220             :          * detail.
     221             :          */
     222           0 :         got_verifier = false;
     223             :     }
     224             : 
     225             :     /*
     226             :      * If the user did not have a valid SCRAM verifier, we still go through
     227             :      * the motions with a mock one, and fail as if the client supplied an
     228             :      * incorrect password.  This is to avoid revealing information to an
     229             :      * attacker.
     230             :      */
     231           0 :     if (!got_verifier)
     232             :     {
     233           0 :         mock_scram_verifier(username, &state->iterations, &state->salt,
     234           0 :                             state->StoredKey, state->ServerKey);
     235           0 :         state->doomed = true;
     236             :     }
     237             : 
     238           0 :     return state;
     239             : }
     240             : 
     241             : /*
     242             :  * Continue a SCRAM authentication exchange.
     243             :  *
     244             :  * 'input' is the SCRAM payload sent by the client.  On the first call,
     245             :  * 'input' contains the "Initial Client Response" that the client sent as
     246             :  * part of the SASLInitialResponse message, or NULL if no Initial Client
     247             :  * Response was given.  (The SASL specification distinguishes between an
     248             :  * empty response and non-existing one.)  On subsequent calls, 'input'
     249             :  * cannot be NULL.  For convenience in this function, the caller must
     250             :  * ensure that there is a null terminator at input[inputlen].
     251             :  *
     252             :  * The next message to send to client is saved in 'output', for a length
     253             :  * of 'outputlen'.  In the case of an error, optionally store a palloc'd
     254             :  * string at *logdetail that will be sent to the postmaster log (but not
     255             :  * the client).
     256             :  */
     257             : int
     258           0 : pg_be_scram_exchange(void *opaq, char *input, int inputlen,
     259             :                      char **output, int *outputlen, char **logdetail)
     260             : {
     261           0 :     scram_state *state = (scram_state *) opaq;
     262             :     int         result;
     263             : 
     264           0 :     *output = NULL;
     265             : 
     266             :     /*
     267             :      * If the client didn't include an "Initial Client Response" in the
     268             :      * SASLInitialResponse message, send an empty challenge, to which the
     269             :      * client will respond with the same data that usually comes in the
     270             :      * Initial Client Response.
     271             :      */
     272           0 :     if (input == NULL)
     273             :     {
     274           0 :         Assert(state->state == SCRAM_AUTH_INIT);
     275             : 
     276           0 :         *output = pstrdup("");
     277           0 :         *outputlen = 0;
     278           0 :         return SASL_EXCHANGE_CONTINUE;
     279             :     }
     280             : 
     281             :     /*
     282             :      * Check that the input length agrees with the string length of the input.
     283             :      * We can ignore inputlen after this.
     284             :      */
     285           0 :     if (inputlen == 0)
     286           0 :         ereport(ERROR,
     287             :                 (errcode(ERRCODE_PROTOCOL_VIOLATION),
     288             :                  errmsg("malformed SCRAM message"),
     289             :                  errdetail("The message is empty.")));
     290           0 :     if (inputlen != strlen(input))
     291           0 :         ereport(ERROR,
     292             :                 (errcode(ERRCODE_PROTOCOL_VIOLATION),
     293             :                  errmsg("malformed SCRAM message"),
     294             :                  errdetail("Message length does not match input length.")));
     295             : 
     296           0 :     switch (state->state)
     297             :     {
     298             :         case SCRAM_AUTH_INIT:
     299             : 
     300             :             /*
     301             :              * Initialization phase.  Receive the first message from client
     302             :              * and be sure that it parsed correctly.  Then send the challenge
     303             :              * to the client.
     304             :              */
     305           0 :             read_client_first_message(state, input);
     306             : 
     307             :             /* prepare message to send challenge */
     308           0 :             *output = build_server_first_message(state);
     309             : 
     310           0 :             state->state = SCRAM_AUTH_SALT_SENT;
     311           0 :             result = SASL_EXCHANGE_CONTINUE;
     312           0 :             break;
     313             : 
     314             :         case SCRAM_AUTH_SALT_SENT:
     315             : 
     316             :             /*
     317             :              * Final phase for the server.  Receive the response to the
     318             :              * challenge previously sent, verify, and let the client know that
     319             :              * everything went well (or not).
     320             :              */
     321           0 :             read_client_final_message(state, input);
     322             : 
     323           0 :             if (!verify_final_nonce(state))
     324           0 :                 ereport(ERROR,
     325             :                         (errcode(ERRCODE_PROTOCOL_VIOLATION),
     326             :                          errmsg("invalid SCRAM response"),
     327             :                          errdetail("Nonce does not match.")));
     328             : 
     329             :             /*
     330             :              * Now check the final nonce and the client proof.
     331             :              *
     332             :              * If we performed a "mock" authentication that we knew would fail
     333             :              * from the get go, this is where we fail.
     334             :              *
     335             :              * The SCRAM specification includes an error code,
     336             :              * "invalid-proof", for authentication failure, but it also allows
     337             :              * erroring out in an application-specific way.  We choose to do
     338             :              * the latter, so that the error message for invalid password is
     339             :              * the same for all authentication methods.  The caller will call
     340             :              * ereport(), when we return SASL_EXCHANGE_FAILURE with no output.
     341             :              *
     342             :              * NB: the order of these checks is intentional.  We calculate the
     343             :              * client proof even in a mock authentication, even though it's
     344             :              * bound to fail, to thwart timing attacks to determine if a role
     345             :              * with the given name exists or not.
     346             :              */
     347           0 :             if (!verify_client_proof(state) || state->doomed)
     348             :             {
     349           0 :                 result = SASL_EXCHANGE_FAILURE;
     350           0 :                 break;
     351             :             }
     352             : 
     353             :             /* Build final message for client */
     354           0 :             *output = build_server_final_message(state);
     355             : 
     356             :             /* Success! */
     357           0 :             result = SASL_EXCHANGE_SUCCESS;
     358           0 :             state->state = SCRAM_AUTH_FINISHED;
     359           0 :             break;
     360             : 
     361             :         default:
     362           0 :             elog(ERROR, "invalid SCRAM exchange state");
     363             :             result = SASL_EXCHANGE_FAILURE;
     364             :     }
     365             : 
     366           0 :     if (result == SASL_EXCHANGE_FAILURE && state->logdetail && logdetail)
     367           0 :         *logdetail = state->logdetail;
     368             : 
     369           0 :     if (*output)
     370           0 :         *outputlen = strlen(*output);
     371             : 
     372           0 :     return result;
     373             : }
     374             : 
     375             : /*
     376             :  * Construct a verifier string for SCRAM, stored in pg_authid.rolpassword.
     377             :  *
     378             :  * The result is palloc'd, so caller is responsible for freeing it.
     379             :  */
     380             : char *
     381           2 : pg_be_scram_build_verifier(const char *password)
     382             : {
     383           2 :     char       *prep_password = NULL;
     384             :     pg_saslprep_rc rc;
     385             :     char        saltbuf[SCRAM_DEFAULT_SALT_LEN];
     386             :     char       *result;
     387             : 
     388             :     /*
     389             :      * Normalize the password with SASLprep.  If that doesn't work, because
     390             :      * the password isn't valid UTF-8 or contains prohibited characters, just
     391             :      * proceed with the original password.  (See comments at top of file.)
     392             :      */
     393           2 :     rc = pg_saslprep(password, &prep_password);
     394           2 :     if (rc == SASLPREP_SUCCESS)
     395           2 :         password = (const char *) prep_password;
     396             : 
     397             :     /* Generate random salt */
     398           2 :     if (!pg_backend_random(saltbuf, SCRAM_DEFAULT_SALT_LEN))
     399           0 :         ereport(ERROR,
     400             :                 (errcode(ERRCODE_INTERNAL_ERROR),
     401             :                  errmsg("could not generate random salt")));
     402             : 
     403           2 :     result = scram_build_verifier(saltbuf, SCRAM_DEFAULT_SALT_LEN,
     404             :                                   SCRAM_DEFAULT_ITERATIONS, password);
     405             : 
     406           2 :     if (prep_password)
     407           2 :         pfree(prep_password);
     408             : 
     409           2 :     return result;
     410             : }
     411             : 
     412             : /*
     413             :  * Verify a plaintext password against a SCRAM verifier.  This is used when
     414             :  * performing plaintext password authentication for a user that has a SCRAM
     415             :  * verifier stored in pg_authid.
     416             :  */
     417             : bool
     418           2 : scram_verify_plain_password(const char *username, const char *password,
     419             :                             const char *verifier)
     420             : {
     421             :     char       *encoded_salt;
     422             :     char       *salt;
     423             :     int         saltlen;
     424             :     int         iterations;
     425             :     uint8       salted_password[SCRAM_KEY_LEN];
     426             :     uint8       stored_key[SCRAM_KEY_LEN];
     427             :     uint8       server_key[SCRAM_KEY_LEN];
     428             :     uint8       computed_key[SCRAM_KEY_LEN];
     429           2 :     char       *prep_password = NULL;
     430             :     pg_saslprep_rc rc;
     431             : 
     432           2 :     if (!parse_scram_verifier(verifier, &iterations, &encoded_salt,
     433             :                               stored_key, server_key))
     434             :     {
     435             :         /*
     436             :          * The password looked like a SCRAM verifier, but could not be parsed.
     437             :          */
     438           0 :         ereport(LOG,
     439             :                 (errmsg("invalid SCRAM verifier for user \"%s\"", username)));
     440           0 :         return false;
     441             :     }
     442             : 
     443           2 :     salt = palloc(pg_b64_dec_len(strlen(encoded_salt)));
     444           2 :     saltlen = pg_b64_decode(encoded_salt, strlen(encoded_salt), salt);
     445           2 :     if (saltlen == -1)
     446             :     {
     447           0 :         ereport(LOG,
     448             :                 (errmsg("invalid SCRAM verifier for user \"%s\"", username)));
     449           0 :         return false;
     450             :     }
     451             : 
     452             :     /* Normalize the password */
     453           2 :     rc = pg_saslprep(password, &prep_password);
     454           2 :     if (rc == SASLPREP_SUCCESS)
     455           2 :         password = prep_password;
     456             : 
     457             :     /* Compute Server Key based on the user-supplied plaintext password */
     458           2 :     scram_SaltedPassword(password, salt, saltlen, iterations, salted_password);
     459           2 :     scram_ServerKey(salted_password, computed_key);
     460             : 
     461           2 :     if (prep_password)
     462           2 :         pfree(prep_password);
     463             : 
     464             :     /*
     465             :      * Compare the verifier's Server Key with the one computed from the
     466             :      * user-supplied password.
     467             :      */
     468           2 :     return memcmp(computed_key, server_key, SCRAM_KEY_LEN) == 0;
     469             : }
     470             : 
     471             : 
     472             : /*
     473             :  * Parse and validate format of given SCRAM verifier.
     474             :  *
     475             :  * Returns true if the SCRAM verifier has been parsed, and false otherwise.
     476             :  */
     477             : static bool
     478           2 : parse_scram_verifier(const char *verifier, int *iterations, char **salt,
     479             :                      uint8 *stored_key, uint8 *server_key)
     480             : {
     481             :     char       *v;
     482             :     char       *p;
     483             :     char       *scheme_str;
     484             :     char       *salt_str;
     485             :     char       *iterations_str;
     486             :     char       *storedkey_str;
     487             :     char       *serverkey_str;
     488             :     int         decoded_len;
     489             :     char       *decoded_salt_buf;
     490             : 
     491             :     /*
     492             :      * The verifier is of form:
     493             :      *
     494             :      * SCRAM-SHA-256$<iterations>:<salt>$<storedkey>:<serverkey>
     495             :      */
     496           2 :     v = pstrdup(verifier);
     497           2 :     if ((scheme_str = strtok(v, "$")) == NULL)
     498           0 :         goto invalid_verifier;
     499           2 :     if ((iterations_str = strtok(NULL, ":")) == NULL)
     500           0 :         goto invalid_verifier;
     501           2 :     if ((salt_str = strtok(NULL, "$")) == NULL)
     502           0 :         goto invalid_verifier;
     503           2 :     if ((storedkey_str = strtok(NULL, ":")) == NULL)
     504           0 :         goto invalid_verifier;
     505           2 :     if ((serverkey_str = strtok(NULL, "")) == NULL)
     506           0 :         goto invalid_verifier;
     507             : 
     508             :     /* Parse the fields */
     509           2 :     if (strcmp(scheme_str, "SCRAM-SHA-256") != 0)
     510           0 :         goto invalid_verifier;
     511             : 
     512           2 :     errno = 0;
     513           2 :     *iterations = strtol(iterations_str, &p, 10);
     514           2 :     if (*p || errno != 0)
     515             :         goto invalid_verifier;
     516             : 
     517             :     /*
     518             :      * Verify that the salt is in Base64-encoded format, by decoding it,
     519             :      * although we return the encoded version to the caller.
     520             :      */
     521           2 :     decoded_salt_buf = palloc(pg_b64_dec_len(strlen(salt_str)));
     522           2 :     decoded_len = pg_b64_decode(salt_str, strlen(salt_str), decoded_salt_buf);
     523           2 :     if (decoded_len < 0)
     524           0 :         goto invalid_verifier;
     525           2 :     *salt = pstrdup(salt_str);
     526             : 
     527             :     /*
     528             :      * Decode StoredKey and ServerKey.
     529             :      */
     530           2 :     if (pg_b64_dec_len(strlen(storedkey_str) != SCRAM_KEY_LEN))
     531           0 :         goto invalid_verifier;
     532           2 :     decoded_len = pg_b64_decode(storedkey_str, strlen(storedkey_str),
     533             :                                 (char *) stored_key);
     534           2 :     if (decoded_len != SCRAM_KEY_LEN)
     535           0 :         goto invalid_verifier;
     536             : 
     537           2 :     if (pg_b64_dec_len(strlen(serverkey_str) != SCRAM_KEY_LEN))
     538           0 :         goto invalid_verifier;
     539           2 :     decoded_len = pg_b64_decode(serverkey_str, strlen(serverkey_str),
     540             :                                 (char *) server_key);
     541           2 :     if (decoded_len != SCRAM_KEY_LEN)
     542           0 :         goto invalid_verifier;
     543             : 
     544           2 :     return true;
     545             : 
     546             : invalid_verifier:
     547           0 :     pfree(v);
     548           0 :     *salt = NULL;
     549           0 :     return false;
     550             : }
     551             : 
     552             : static void
     553           0 : mock_scram_verifier(const char *username, int *iterations, char **salt,
     554             :                     uint8 *stored_key, uint8 *server_key)
     555             : {
     556             :     char       *raw_salt;
     557             :     char       *encoded_salt;
     558             :     int         encoded_len;
     559             : 
     560             :     /* Generate deterministic salt */
     561           0 :     raw_salt = scram_mock_salt(username);
     562             : 
     563           0 :     encoded_salt = (char *) palloc(pg_b64_enc_len(SCRAM_DEFAULT_SALT_LEN) + 1);
     564           0 :     encoded_len = pg_b64_encode(raw_salt, SCRAM_DEFAULT_SALT_LEN, encoded_salt);
     565           0 :     encoded_salt[encoded_len] = '\0';
     566             : 
     567           0 :     *salt = encoded_salt;
     568           0 :     *iterations = SCRAM_DEFAULT_ITERATIONS;
     569             : 
     570             :     /* StoredKey and ServerKey are not used in a doomed authentication */
     571           0 :     memset(stored_key, 0, SCRAM_KEY_LEN);
     572           0 :     memset(server_key, 0, SCRAM_KEY_LEN);
     573           0 : }
     574             : 
     575             : /*
     576             :  * Read the value in a given SCRAM exchange message for given attribute.
     577             :  */
     578             : static char *
     579           0 : read_attr_value(char **input, char attr)
     580             : {
     581           0 :     char       *begin = *input;
     582             :     char       *end;
     583             : 
     584           0 :     if (*begin != attr)
     585           0 :         ereport(ERROR,
     586             :                 (errcode(ERRCODE_PROTOCOL_VIOLATION),
     587             :                  errmsg("malformed SCRAM message"),
     588             :                  errdetail("Expected attribute \"%c\" but found \"%s\".",
     589             :                            attr, sanitize_char(*begin))));
     590           0 :     begin++;
     591             : 
     592           0 :     if (*begin != '=')
     593           0 :         ereport(ERROR,
     594             :                 (errcode(ERRCODE_PROTOCOL_VIOLATION),
     595             :                  errmsg("malformed SCRAM message"),
     596             :                  errdetail("Expected character \"=\" for attribute \"%c\".", attr)));
     597           0 :     begin++;
     598             : 
     599           0 :     end = begin;
     600           0 :     while (*end && *end != ',')
     601           0 :         end++;
     602             : 
     603           0 :     if (*end)
     604             :     {
     605           0 :         *end = '\0';
     606           0 :         *input = end + 1;
     607             :     }
     608             :     else
     609           0 :         *input = end;
     610             : 
     611           0 :     return begin;
     612             : }
     613             : 
     614             : static bool
     615           0 : is_scram_printable(char *p)
     616             : {
     617             :     /*------
     618             :      * Printable characters, as defined by SCRAM spec: (RFC 5802)
     619             :      *
     620             :      *  printable       = %x21-2B / %x2D-7E
     621             :      *                    ;; Printable ASCII except ",".
     622             :      *                    ;; Note that any "printable" is also
     623             :      *                    ;; a valid "value".
     624             :      *------
     625             :      */
     626           0 :     for (; *p; p++)
     627             :     {
     628           0 :         if (*p < 0x21 || *p > 0x7E || *p == 0x2C /* comma */ )
     629           0 :             return false;
     630             :     }
     631           0 :     return true;
     632             : }
     633             : 
     634             : /*
     635             :  * Convert an arbitrary byte to printable form.  For error messages.
     636             :  *
     637             :  * If it's a printable ASCII character, print it as a single character.
     638             :  * otherwise, print it in hex.
     639             :  *
     640             :  * The returned pointer points to a static buffer.
     641             :  */
     642             : static char *
     643           0 : sanitize_char(char c)
     644             : {
     645             :     static char buf[5];
     646             : 
     647           0 :     if (c >= 0x21 && c <= 0x7E)
     648           0 :         snprintf(buf, sizeof(buf), "'%c'", c);
     649             :     else
     650           0 :         snprintf(buf, sizeof(buf), "0x%02x", (unsigned char) c);
     651           0 :     return buf;
     652             : }
     653             : 
     654             : /*
     655             :  * Read the next attribute and value in a SCRAM exchange message.
     656             :  *
     657             :  * Returns NULL if there is attribute.
     658             :  */
     659             : static char *
     660           0 : read_any_attr(char **input, char *attr_p)
     661             : {
     662           0 :     char       *begin = *input;
     663             :     char       *end;
     664           0 :     char        attr = *begin;
     665             : 
     666             :     /*------
     667             :      * attr-val        = ALPHA "=" value
     668             :      *                   ;; Generic syntax of any attribute sent
     669             :      *                   ;; by server or client
     670             :      *------
     671             :      */
     672           0 :     if (!((attr >= 'A' && attr <= 'Z') ||
     673           0 :           (attr >= 'a' && attr <= 'z')))
     674           0 :         ereport(ERROR,
     675             :                 (errcode(ERRCODE_PROTOCOL_VIOLATION),
     676             :                  errmsg("malformed SCRAM message"),
     677             :                  errdetail("Attribute expected, but found invalid character \"%s\".",
     678             :                            sanitize_char(attr))));
     679           0 :     if (attr_p)
     680           0 :         *attr_p = attr;
     681           0 :     begin++;
     682             : 
     683           0 :     if (*begin != '=')
     684           0 :         ereport(ERROR,
     685             :                 (errcode(ERRCODE_PROTOCOL_VIOLATION),
     686             :                  errmsg("malformed SCRAM message"),
     687             :                  errdetail("Expected character \"=\" for attribute \"%c\".", attr)));
     688           0 :     begin++;
     689             : 
     690           0 :     end = begin;
     691           0 :     while (*end && *end != ',')
     692           0 :         end++;
     693             : 
     694           0 :     if (*end)
     695             :     {
     696           0 :         *end = '\0';
     697           0 :         *input = end + 1;
     698             :     }
     699             :     else
     700           0 :         *input = end;
     701             : 
     702           0 :     return begin;
     703             : }
     704             : 
     705             : /*
     706             :  * Read and parse the first message from client in the context of a SCRAM
     707             :  * authentication exchange message.
     708             :  *
     709             :  * At this stage, any errors will be reported directly with ereport(ERROR).
     710             :  */
     711             : static void
     712           0 : read_client_first_message(scram_state *state, char *input)
     713             : {
     714           0 :     input = pstrdup(input);
     715             : 
     716             :     /*------
     717             :      * The syntax for the client-first-message is: (RFC 5802)
     718             :      *
     719             :      * saslname        = 1*(value-safe-char / "=2C" / "=3D")
     720             :      *                   ;; Conforms to <value>.
     721             :      *
     722             :      * authzid         = "a=" saslname
     723             :      *                   ;; Protocol specific.
     724             :      *
     725             :      * cb-name         = 1*(ALPHA / DIGIT / "." / "-")
     726             :      *                    ;; See RFC 5056, Section 7.
     727             :      *                    ;; E.g., "tls-server-end-point" or
     728             :      *                    ;; "tls-unique".
     729             :      *
     730             :      * gs2-cbind-flag  = ("p=" cb-name) / "n" / "y"
     731             :      *                   ;; "n" -> client doesn't support channel binding.
     732             :      *                   ;; "y" -> client does support channel binding
     733             :      *                   ;;        but thinks the server does not.
     734             :      *                   ;; "p" -> client requires channel binding.
     735             :      *                   ;; The selected channel binding follows "p=".
     736             :      *
     737             :      * gs2-header      = gs2-cbind-flag "," [ authzid ] ","
     738             :      *                   ;; GS2 header for SCRAM
     739             :      *                   ;; (the actual GS2 header includes an optional
     740             :      *                   ;; flag to indicate that the GSS mechanism is not
     741             :      *                   ;; "standard", but since SCRAM is "standard", we
     742             :      *                   ;; don't include that flag).
     743             :      *
     744             :      * username        = "n=" saslname
     745             :      *                   ;; Usernames are prepared using SASLprep.
     746             :      *
     747             :      * reserved-mext  = "m=" 1*(value-char)
     748             :      *                   ;; Reserved for signaling mandatory extensions.
     749             :      *                   ;; The exact syntax will be defined in
     750             :      *                   ;; the future.
     751             :      *
     752             :      * nonce           = "r=" c-nonce [s-nonce]
     753             :      *                   ;; Second part provided by server.
     754             :      *
     755             :      * c-nonce         = printable
     756             :      *
     757             :      * client-first-message-bare =
     758             :      *                   [reserved-mext ","]
     759             :      *                   username "," nonce ["," extensions]
     760             :      *
     761             :      * client-first-message =
     762             :      *                   gs2-header client-first-message-bare
     763             :      *
     764             :      * For example:
     765             :      * n,,n=user,r=fyko+d2lbbFgONRv9qkxdawL
     766             :      *
     767             :      * The "n,," in the beginning means that the client doesn't support
     768             :      * channel binding, and no authzid is given.  "n=user" is the username.
     769             :      * However, in PostgreSQL the username is sent in the startup packet, and
     770             :      * the username in the SCRAM exchange is ignored.  libpq always sends it
     771             :      * as an empty string.  The last part, "r=fyko+d2lbbFgONRv9qkxdawL" is
     772             :      * the client nonce.
     773             :      *------
     774             :      */
     775             : 
     776             :     /* read gs2-cbind-flag */
     777           0 :     switch (*input)
     778             :     {
     779             :         case 'n':
     780             :             /* Client does not support channel binding */
     781           0 :             input++;
     782           0 :             break;
     783             :         case 'y':
     784             :             /* Client supports channel binding, but we're not doing it today */
     785           0 :             input++;
     786           0 :             break;
     787             :         case 'p':
     788             : 
     789             :             /*
     790             :              * Client requires channel binding.  We don't support it.
     791             :              *
     792             :              * RFC 5802 specifies a particular error code,
     793             :              * e=server-does-support-channel-binding, for this.  But it can
     794             :              * only be sent in the server-final message, and we don't want to
     795             :              * go through the motions of the authentication, knowing it will
     796             :              * fail, just to send that error message.
     797             :              */
     798           0 :             ereport(ERROR,
     799             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     800             :                      errmsg("client requires SCRAM channel binding, but it is not supported")));
     801             :         default:
     802           0 :             ereport(ERROR,
     803             :                     (errcode(ERRCODE_PROTOCOL_VIOLATION),
     804             :                      errmsg("malformed SCRAM message"),
     805             :                      errdetail("Unexpected channel-binding flag \"%s\".",
     806             :                                sanitize_char(*input))));
     807             :     }
     808           0 :     if (*input != ',')
     809           0 :         ereport(ERROR,
     810             :                 (errcode(ERRCODE_PROTOCOL_VIOLATION),
     811             :                  errmsg("malformed SCRAM message"),
     812             :                  errdetail("Comma expected, but found character \"%s\".",
     813             :                            sanitize_char(*input))));
     814           0 :     input++;
     815             : 
     816             :     /*
     817             :      * Forbid optional authzid (authorization identity).  We don't support it.
     818             :      */
     819           0 :     if (*input == 'a')
     820           0 :         ereport(ERROR,
     821             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     822             :                  errmsg("client uses authorization identity, but it is not supported")));
     823           0 :     if (*input != ',')
     824           0 :         ereport(ERROR,
     825             :                 (errcode(ERRCODE_PROTOCOL_VIOLATION),
     826             :                  errmsg("malformed SCRAM message"),
     827             :                  errdetail("Unexpected attribute \"%s\" in client-first-message.",
     828             :                            sanitize_char(*input))));
     829           0 :     input++;
     830             : 
     831           0 :     state->client_first_message_bare = pstrdup(input);
     832             : 
     833             :     /*
     834             :      * Any mandatory extensions would go here.  We don't support any.
     835             :      *
     836             :      * RFC 5802 specifies error code "e=extensions-not-supported" for this,
     837             :      * but it can only be sent in the server-final message.  We prefer to fail
     838             :      * immediately (which the RFC also allows).
     839             :      */
     840           0 :     if (*input == 'm')
     841           0 :         ereport(ERROR,
     842             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     843             :                  errmsg("client requires an unsupported SCRAM extension")));
     844             : 
     845             :     /*
     846             :      * Read username.  Note: this is ignored.  We use the username from the
     847             :      * startup message instead, still it is kept around if provided as it
     848             :      * proves to be useful for debugging purposes.
     849             :      */
     850           0 :     state->client_username = read_attr_value(&input, 'n');
     851             : 
     852             :     /* read nonce and check that it is made of only printable characters */
     853           0 :     state->client_nonce = read_attr_value(&input, 'r');
     854           0 :     if (!is_scram_printable(state->client_nonce))
     855           0 :         ereport(ERROR,
     856             :                 (errcode(ERRCODE_PROTOCOL_VIOLATION),
     857             :                  errmsg("non-printable characters in SCRAM nonce")));
     858             : 
     859             :     /*
     860             :      * There can be any number of optional extensions after this.  We don't
     861             :      * support any extensions, so ignore them.
     862             :      */
     863           0 :     while (*input != '\0')
     864           0 :         read_any_attr(&input, NULL);
     865             : 
     866             :     /* success! */
     867           0 : }
     868             : 
     869             : /*
     870             :  * Verify the final nonce contained in the last message received from
     871             :  * client in an exchange.
     872             :  */
     873             : static bool
     874           0 : verify_final_nonce(scram_state *state)
     875             : {
     876           0 :     int         client_nonce_len = strlen(state->client_nonce);
     877           0 :     int         server_nonce_len = strlen(state->server_nonce);
     878           0 :     int         final_nonce_len = strlen(state->client_final_nonce);
     879             : 
     880           0 :     if (final_nonce_len != client_nonce_len + server_nonce_len)
     881           0 :         return false;
     882           0 :     if (memcmp(state->client_final_nonce, state->client_nonce, client_nonce_len) != 0)
     883           0 :         return false;
     884           0 :     if (memcmp(state->client_final_nonce + client_nonce_len, state->server_nonce, server_nonce_len) != 0)
     885           0 :         return false;
     886             : 
     887           0 :     return true;
     888             : }
     889             : 
     890             : /*
     891             :  * Verify the client proof contained in the last message received from
     892             :  * client in an exchange.
     893             :  */
     894             : static bool
     895           0 : verify_client_proof(scram_state *state)
     896             : {
     897             :     uint8       ClientSignature[SCRAM_KEY_LEN];
     898             :     uint8       ClientKey[SCRAM_KEY_LEN];
     899             :     uint8       client_StoredKey[SCRAM_KEY_LEN];
     900             :     scram_HMAC_ctx ctx;
     901             :     int         i;
     902             : 
     903             :     /* calculate ClientSignature */
     904           0 :     scram_HMAC_init(&ctx, state->StoredKey, SCRAM_KEY_LEN);
     905           0 :     scram_HMAC_update(&ctx,
     906           0 :                       state->client_first_message_bare,
     907           0 :                       strlen(state->client_first_message_bare));
     908           0 :     scram_HMAC_update(&ctx, ",", 1);
     909           0 :     scram_HMAC_update(&ctx,
     910           0 :                       state->server_first_message,
     911           0 :                       strlen(state->server_first_message));
     912           0 :     scram_HMAC_update(&ctx, ",", 1);
     913           0 :     scram_HMAC_update(&ctx,
     914           0 :                       state->client_final_message_without_proof,
     915           0 :                       strlen(state->client_final_message_without_proof));
     916           0 :     scram_HMAC_final(ClientSignature, &ctx);
     917             : 
     918             :     /* Extract the ClientKey that the client calculated from the proof */
     919           0 :     for (i = 0; i < SCRAM_KEY_LEN; i++)
     920           0 :         ClientKey[i] = state->ClientProof[i] ^ ClientSignature[i];
     921             : 
     922             :     /* Hash it one more time, and compare with StoredKey */
     923           0 :     scram_H(ClientKey, SCRAM_KEY_LEN, client_StoredKey);
     924             : 
     925           0 :     if (memcmp(client_StoredKey, state->StoredKey, SCRAM_KEY_LEN) != 0)
     926           0 :         return false;
     927             : 
     928           0 :     return true;
     929             : }
     930             : 
     931             : /*
     932             :  * Build the first server-side message sent to the client in a SCRAM
     933             :  * communication exchange.
     934             :  */
     935             : static char *
     936           0 : build_server_first_message(scram_state *state)
     937             : {
     938             :     /*------
     939             :      * The syntax for the server-first-message is: (RFC 5802)
     940             :      *
     941             :      * server-first-message =
     942             :      *                   [reserved-mext ","] nonce "," salt ","
     943             :      *                   iteration-count ["," extensions]
     944             :      *
     945             :      * nonce           = "r=" c-nonce [s-nonce]
     946             :      *                   ;; Second part provided by server.
     947             :      *
     948             :      * c-nonce         = printable
     949             :      *
     950             :      * s-nonce         = printable
     951             :      *
     952             :      * salt            = "s=" base64
     953             :      *
     954             :      * iteration-count = "i=" posit-number
     955             :      *                   ;; A positive number.
     956             :      *
     957             :      * Example:
     958             :      *
     959             :      * r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,s=QSXCR+Q6sek8bf92,i=4096
     960             :      *------
     961             :      */
     962             : 
     963             :     /*
     964             :      * Per the spec, the nonce may consist of any printable ASCII characters.
     965             :      * For convenience, however, we don't use the whole range available,
     966             :      * rather, we generate some random bytes, and base64 encode them.
     967             :      */
     968             :     char        raw_nonce[SCRAM_RAW_NONCE_LEN];
     969             :     int         encoded_len;
     970             : 
     971           0 :     if (!pg_backend_random(raw_nonce, SCRAM_RAW_NONCE_LEN))
     972           0 :         ereport(ERROR,
     973             :                 (errcode(ERRCODE_INTERNAL_ERROR),
     974             :                  errmsg("could not generate random nonce")));
     975             : 
     976           0 :     state->server_nonce = palloc(pg_b64_enc_len(SCRAM_RAW_NONCE_LEN) + 1);
     977           0 :     encoded_len = pg_b64_encode(raw_nonce, SCRAM_RAW_NONCE_LEN, state->server_nonce);
     978           0 :     state->server_nonce[encoded_len] = '\0';
     979             : 
     980           0 :     state->server_first_message =
     981           0 :         psprintf("r=%s%s,s=%s,i=%u",
     982             :                  state->client_nonce, state->server_nonce,
     983             :                  state->salt, state->iterations);
     984             : 
     985           0 :     return pstrdup(state->server_first_message);
     986             : }
     987             : 
     988             : 
     989             : /*
     990             :  * Read and parse the final message received from client.
     991             :  */
     992             : static void
     993           0 : read_client_final_message(scram_state *state, char *input)
     994             : {
     995             :     char        attr;
     996             :     char       *channel_binding;
     997             :     char       *value;
     998             :     char       *begin,
     999             :                *proof;
    1000             :     char       *p;
    1001             :     char       *client_proof;
    1002             : 
    1003           0 :     begin = p = pstrdup(input);
    1004             : 
    1005             :     /*------
    1006             :      * The syntax for the server-first-message is: (RFC 5802)
    1007             :      *
    1008             :      * gs2-header      = gs2-cbind-flag "," [ authzid ] ","
    1009             :      *                   ;; GS2 header for SCRAM
    1010             :      *                   ;; (the actual GS2 header includes an optional
    1011             :      *                   ;; flag to indicate that the GSS mechanism is not
    1012             :      *                   ;; "standard", but since SCRAM is "standard", we
    1013             :      *                   ;; don't include that flag).
    1014             :      *
    1015             :      * cbind-input   = gs2-header [ cbind-data ]
    1016             :      *                   ;; cbind-data MUST be present for
    1017             :      *                   ;; gs2-cbind-flag of "p" and MUST be absent
    1018             :      *                   ;; for "y" or "n".
    1019             :      *
    1020             :      * channel-binding = "c=" base64
    1021             :      *                   ;; base64 encoding of cbind-input.
    1022             :      *
    1023             :      * proof           = "p=" base64
    1024             :      *
    1025             :      * client-final-message-without-proof =
    1026             :      *                   channel-binding "," nonce [","
    1027             :      *                   extensions]
    1028             :      *
    1029             :      * client-final-message =
    1030             :      *                   client-final-message-without-proof "," proof
    1031             :      *------
    1032             :      */
    1033             : 
    1034             :     /*
    1035             :      * Read channel-binding.  We don't support channel binding, so it's
    1036             :      * expected to always be "biws", which is "n,,", base64-encoded.
    1037             :      */
    1038           0 :     channel_binding = read_attr_value(&p, 'c');
    1039           0 :     if (strcmp(channel_binding, "biws") != 0)
    1040           0 :         ereport(ERROR,
    1041             :                 (errcode(ERRCODE_PROTOCOL_VIOLATION),
    1042             :                  (errmsg("unexpected SCRAM channel-binding attribute in client-final-message"))));
    1043           0 :     state->client_final_nonce = read_attr_value(&p, 'r');
    1044             : 
    1045             :     /* ignore optional extensions */
    1046             :     do
    1047             :     {
    1048           0 :         proof = p - 1;
    1049           0 :         value = read_any_attr(&p, &attr);
    1050           0 :     } while (attr != 'p');
    1051             : 
    1052           0 :     client_proof = palloc(pg_b64_dec_len(strlen(value)));
    1053           0 :     if (pg_b64_decode(value, strlen(value), client_proof) != SCRAM_KEY_LEN)
    1054           0 :         ereport(ERROR,
    1055             :                 (errcode(ERRCODE_PROTOCOL_VIOLATION),
    1056             :                  errmsg("malformed SCRAM message"),
    1057             :                  errdetail("Malformed proof in client-final-message.")));
    1058           0 :     memcpy(state->ClientProof, client_proof, SCRAM_KEY_LEN);
    1059           0 :     pfree(client_proof);
    1060             : 
    1061           0 :     if (*p != '\0')
    1062           0 :         ereport(ERROR,
    1063             :                 (errcode(ERRCODE_PROTOCOL_VIOLATION),
    1064             :                  errmsg("malformed SCRAM message"),
    1065             :                  errdetail("Garbage found at the end of client-final-message.")));
    1066             : 
    1067           0 :     state->client_final_message_without_proof = palloc(proof - begin + 1);
    1068           0 :     memcpy(state->client_final_message_without_proof, input, proof - begin);
    1069           0 :     state->client_final_message_without_proof[proof - begin] = '\0';
    1070           0 : }
    1071             : 
    1072             : /*
    1073             :  * Build the final server-side message of an exchange.
    1074             :  */
    1075             : static char *
    1076           0 : build_server_final_message(scram_state *state)
    1077             : {
    1078             :     uint8       ServerSignature[SCRAM_KEY_LEN];
    1079             :     char       *server_signature_base64;
    1080             :     int         siglen;
    1081             :     scram_HMAC_ctx ctx;
    1082             : 
    1083             :     /* calculate ServerSignature */
    1084           0 :     scram_HMAC_init(&ctx, state->ServerKey, SCRAM_KEY_LEN);
    1085           0 :     scram_HMAC_update(&ctx,
    1086           0 :                       state->client_first_message_bare,
    1087           0 :                       strlen(state->client_first_message_bare));
    1088           0 :     scram_HMAC_update(&ctx, ",", 1);
    1089           0 :     scram_HMAC_update(&ctx,
    1090           0 :                       state->server_first_message,
    1091           0 :                       strlen(state->server_first_message));
    1092           0 :     scram_HMAC_update(&ctx, ",", 1);
    1093           0 :     scram_HMAC_update(&ctx,
    1094           0 :                       state->client_final_message_without_proof,
    1095           0 :                       strlen(state->client_final_message_without_proof));
    1096           0 :     scram_HMAC_final(ServerSignature, &ctx);
    1097             : 
    1098           0 :     server_signature_base64 = palloc(pg_b64_enc_len(SCRAM_KEY_LEN) + 1);
    1099           0 :     siglen = pg_b64_encode((const char *) ServerSignature,
    1100             :                            SCRAM_KEY_LEN, server_signature_base64);
    1101           0 :     server_signature_base64[siglen] = '\0';
    1102             : 
    1103             :     /*------
    1104             :      * The syntax for the server-final-message is: (RFC 5802)
    1105             :      *
    1106             :      * verifier        = "v=" base64
    1107             :      *                   ;; base-64 encoded ServerSignature.
    1108             :      *
    1109             :      * server-final-message = (server-error / verifier)
    1110             :      *                   ["," extensions]
    1111             :      *
    1112             :      *------
    1113             :      */
    1114           0 :     return psprintf("v=%s", server_signature_base64);
    1115             : }
    1116             : 
    1117             : 
    1118             : /*
    1119             :  * Determinisitcally generate salt for mock authentication, using a SHA256
    1120             :  * hash based on the username and a cluster-level secret key.  Returns a
    1121             :  * pointer to a static buffer of size SCRAM_DEFAULT_SALT_LEN.
    1122             :  */
    1123             : static char *
    1124           0 : scram_mock_salt(const char *username)
    1125             : {
    1126             :     pg_sha256_ctx ctx;
    1127             :     static uint8 sha_digest[PG_SHA256_DIGEST_LENGTH];
    1128           0 :     char       *mock_auth_nonce = GetMockAuthenticationNonce();
    1129             : 
    1130             :     /*
    1131             :      * Generate salt using a SHA256 hash of the username and the cluster's
    1132             :      * mock authentication nonce.  (This works as long as the salt length is
    1133             :      * not larger the SHA256 digest length. If the salt is smaller, the caller
    1134             :      * will just ignore the extra data.)
    1135             :      */
    1136             :     StaticAssertStmt(PG_SHA256_DIGEST_LENGTH >= SCRAM_DEFAULT_SALT_LEN,
    1137             :                      "salt length greater than SHA256 digest length");
    1138             : 
    1139           0 :     pg_sha256_init(&ctx);
    1140           0 :     pg_sha256_update(&ctx, (uint8 *) username, strlen(username));
    1141           0 :     pg_sha256_update(&ctx, (uint8 *) mock_auth_nonce, MOCK_AUTH_NONCE_LEN);
    1142           0 :     pg_sha256_final(&ctx, sha_digest);
    1143             : 
    1144           0 :     return (char *) sha_digest;
    1145             : }

Generated by: LCOV version 1.11