LCOV - code coverage report
Current view: top level - src/interfaces/libpq - fe-auth-scram.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13beta1 Lines: 241 379 63.6 %
Date: 2020-05-31 23:07:13 Functions: 11 12 91.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * fe-auth-scram.c
       4             :  *     The front-end (client) implementation of SCRAM authentication.
       5             :  *
       6             :  * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  * IDENTIFICATION
      10             :  *    src/interfaces/libpq/fe-auth-scram.c
      11             :  *
      12             :  *-------------------------------------------------------------------------
      13             :  */
      14             : 
      15             : #include "postgres_fe.h"
      16             : 
      17             : #include "common/base64.h"
      18             : #include "common/saslprep.h"
      19             : #include "common/scram-common.h"
      20             : #include "fe-auth.h"
      21             : 
      22             : 
      23             : /*
      24             :  * Status of exchange messages used for SCRAM authentication via the
      25             :  * SASL protocol.
      26             :  */
      27             : typedef enum
      28             : {
      29             :     FE_SCRAM_INIT,
      30             :     FE_SCRAM_NONCE_SENT,
      31             :     FE_SCRAM_PROOF_SENT,
      32             :     FE_SCRAM_FINISHED
      33             : } fe_scram_state_enum;
      34             : 
      35             : typedef struct
      36             : {
      37             :     fe_scram_state_enum state;
      38             : 
      39             :     /* These are supplied by the user */
      40             :     PGconn     *conn;
      41             :     char       *password;
      42             :     char       *sasl_mechanism;
      43             : 
      44             :     /* We construct these */
      45             :     uint8       SaltedPassword[SCRAM_KEY_LEN];
      46             :     char       *client_nonce;
      47             :     char       *client_first_message_bare;
      48             :     char       *client_final_message_without_proof;
      49             : 
      50             :     /* These come from the server-first message */
      51             :     char       *server_first_message;
      52             :     char       *salt;
      53             :     int         saltlen;
      54             :     int         iterations;
      55             :     char       *nonce;
      56             : 
      57             :     /* These come from the server-final message */
      58             :     char       *server_final_message;
      59             :     char        ServerSignature[SCRAM_KEY_LEN];
      60             : } fe_scram_state;
      61             : 
      62             : static bool read_server_first_message(fe_scram_state *state, char *input);
      63             : static bool read_server_final_message(fe_scram_state *state, char *input);
      64             : static char *build_client_first_message(fe_scram_state *state);
      65             : static char *build_client_final_message(fe_scram_state *state);
      66             : static bool verify_server_signature(fe_scram_state *state);
      67             : static void calculate_client_proof(fe_scram_state *state,
      68             :                                    const char *client_final_message_without_proof,
      69             :                                    uint8 *result);
      70             : 
      71             : /*
      72             :  * Initialize SCRAM exchange status.
      73             :  */
      74             : void *
      75          36 : pg_fe_scram_init(PGconn *conn,
      76             :                  const char *password,
      77             :                  const char *sasl_mechanism)
      78             : {
      79             :     fe_scram_state *state;
      80             :     char       *prep_password;
      81             :     pg_saslprep_rc rc;
      82             : 
      83             :     Assert(sasl_mechanism != NULL);
      84             : 
      85          36 :     state = (fe_scram_state *) malloc(sizeof(fe_scram_state));
      86          36 :     if (!state)
      87           0 :         return NULL;
      88          36 :     memset(state, 0, sizeof(fe_scram_state));
      89          36 :     state->conn = conn;
      90          36 :     state->state = FE_SCRAM_INIT;
      91          36 :     state->sasl_mechanism = strdup(sasl_mechanism);
      92             : 
      93          36 :     if (!state->sasl_mechanism)
      94             :     {
      95           0 :         free(state);
      96           0 :         return NULL;
      97             :     }
      98             : 
      99             :     /* Normalize the password with SASLprep, if possible */
     100          36 :     rc = pg_saslprep(password, &prep_password);
     101          36 :     if (rc == SASLPREP_OOM)
     102             :     {
     103           0 :         free(state->sasl_mechanism);
     104           0 :         free(state);
     105           0 :         return NULL;
     106             :     }
     107          36 :     if (rc != SASLPREP_SUCCESS)
     108             :     {
     109           4 :         prep_password = strdup(password);
     110           4 :         if (!prep_password)
     111             :         {
     112           0 :             free(state->sasl_mechanism);
     113           0 :             free(state);
     114           0 :             return NULL;
     115             :         }
     116             :     }
     117          36 :     state->password = prep_password;
     118             : 
     119          36 :     return state;
     120             : }
     121             : 
     122             : /*
     123             :  * Return true if channel binding was employed and the SCRAM exchange
     124             :  * completed. This should be used after a successful exchange to determine
     125             :  * whether the server authenticated itself to the client.
     126             :  *
     127             :  * Note that the caller must also ensure that the exchange was actually
     128             :  * successful.
     129             :  */
     130             : bool
     131           4 : pg_fe_scram_channel_bound(void *opaq)
     132             : {
     133           4 :     fe_scram_state *state = (fe_scram_state *) opaq;
     134             : 
     135             :     /* no SCRAM exchange done */
     136           4 :     if (state == NULL)
     137           2 :         return false;
     138             : 
     139             :     /* SCRAM exchange not completed */
     140           2 :     if (state->state != FE_SCRAM_FINISHED)
     141           0 :         return false;
     142             : 
     143             :     /* channel binding mechanism not used */
     144           2 :     if (strcmp(state->sasl_mechanism, SCRAM_SHA_256_PLUS_NAME) != 0)
     145           0 :         return false;
     146             : 
     147             :     /* all clear! */
     148           2 :     return true;
     149             : }
     150             : 
     151             : /*
     152             :  * Free SCRAM exchange status
     153             :  */
     154             : void
     155          36 : pg_fe_scram_free(void *opaq)
     156             : {
     157          36 :     fe_scram_state *state = (fe_scram_state *) opaq;
     158             : 
     159          36 :     if (state->password)
     160          36 :         free(state->password);
     161          36 :     if (state->sasl_mechanism)
     162          36 :         free(state->sasl_mechanism);
     163             : 
     164             :     /* client messages */
     165          36 :     if (state->client_nonce)
     166          36 :         free(state->client_nonce);
     167          36 :     if (state->client_first_message_bare)
     168          36 :         free(state->client_first_message_bare);
     169          36 :     if (state->client_final_message_without_proof)
     170          36 :         free(state->client_final_message_without_proof);
     171             : 
     172             :     /* first message from server */
     173          36 :     if (state->server_first_message)
     174          36 :         free(state->server_first_message);
     175          36 :     if (state->salt)
     176          36 :         free(state->salt);
     177          36 :     if (state->nonce)
     178          36 :         free(state->nonce);
     179             : 
     180             :     /* final message from server */
     181          36 :     if (state->server_final_message)
     182          26 :         free(state->server_final_message);
     183             : 
     184          36 :     free(state);
     185          36 : }
     186             : 
     187             : /*
     188             :  * Exchange a SCRAM message with backend.
     189             :  */
     190             : void
     191          98 : pg_fe_scram_exchange(void *opaq, char *input, int inputlen,
     192             :                      char **output, int *outputlen,
     193             :                      bool *done, bool *success)
     194             : {
     195          98 :     fe_scram_state *state = (fe_scram_state *) opaq;
     196          98 :     PGconn     *conn = state->conn;
     197             : 
     198          98 :     *done = false;
     199          98 :     *success = false;
     200          98 :     *output = NULL;
     201          98 :     *outputlen = 0;
     202             : 
     203             :     /*
     204             :      * Check that the input length agrees with the string length of the input.
     205             :      * We can ignore inputlen after this.
     206             :      */
     207          98 :     if (state->state != FE_SCRAM_INIT)
     208             :     {
     209          62 :         if (inputlen == 0)
     210             :         {
     211           0 :             printfPQExpBuffer(&conn->errorMessage,
     212           0 :                               libpq_gettext("malformed SCRAM message (empty message)\n"));
     213           0 :             goto error;
     214             :         }
     215          62 :         if (inputlen != strlen(input))
     216             :         {
     217           0 :             printfPQExpBuffer(&conn->errorMessage,
     218           0 :                               libpq_gettext("malformed SCRAM message (length mismatch)\n"));
     219           0 :             goto error;
     220             :         }
     221             :     }
     222             : 
     223          98 :     switch (state->state)
     224             :     {
     225          36 :         case FE_SCRAM_INIT:
     226             :             /* Begin the SCRAM handshake, by sending client nonce */
     227          36 :             *output = build_client_first_message(state);
     228          36 :             if (*output == NULL)
     229           0 :                 goto error;
     230             : 
     231          36 :             *outputlen = strlen(*output);
     232          36 :             *done = false;
     233          36 :             state->state = FE_SCRAM_NONCE_SENT;
     234          36 :             break;
     235             : 
     236          36 :         case FE_SCRAM_NONCE_SENT:
     237             :             /* Receive salt and server nonce, send response. */
     238          36 :             if (!read_server_first_message(state, input))
     239           0 :                 goto error;
     240             : 
     241          36 :             *output = build_client_final_message(state);
     242          36 :             if (*output == NULL)
     243           0 :                 goto error;
     244             : 
     245          36 :             *outputlen = strlen(*output);
     246          36 :             *done = false;
     247          36 :             state->state = FE_SCRAM_PROOF_SENT;
     248          36 :             break;
     249             : 
     250          26 :         case FE_SCRAM_PROOF_SENT:
     251             :             /* Receive server signature */
     252          26 :             if (!read_server_final_message(state, input))
     253           0 :                 goto error;
     254             : 
     255             :             /*
     256             :              * Verify server signature, to make sure we're talking to the
     257             :              * genuine server.
     258             :              */
     259          26 :             if (verify_server_signature(state))
     260          26 :                 *success = true;
     261             :             else
     262             :             {
     263           0 :                 *success = false;
     264           0 :                 printfPQExpBuffer(&conn->errorMessage,
     265           0 :                                   libpq_gettext("incorrect server signature\n"));
     266             :             }
     267          26 :             *done = true;
     268          26 :             state->state = FE_SCRAM_FINISHED;
     269          26 :             break;
     270             : 
     271           0 :         default:
     272             :             /* shouldn't happen */
     273           0 :             printfPQExpBuffer(&conn->errorMessage,
     274           0 :                               libpq_gettext("invalid SCRAM exchange state\n"));
     275           0 :             goto error;
     276             :     }
     277          98 :     return;
     278             : 
     279           0 : error:
     280           0 :     *done = true;
     281           0 :     *success = false;
     282             : }
     283             : 
     284             : /*
     285             :  * Read value for an attribute part of a SCRAM message.
     286             :  */
     287             : static char *
     288         134 : read_attr_value(char **input, char attr, PQExpBuffer errorMessage)
     289             : {
     290         134 :     char       *begin = *input;
     291             :     char       *end;
     292             : 
     293         134 :     if (*begin != attr)
     294             :     {
     295           0 :         printfPQExpBuffer(errorMessage,
     296           0 :                           libpq_gettext("malformed SCRAM message (attribute \"%c\" expected)\n"),
     297             :                           attr);
     298           0 :         return NULL;
     299             :     }
     300         134 :     begin++;
     301             : 
     302         134 :     if (*begin != '=')
     303             :     {
     304           0 :         printfPQExpBuffer(errorMessage,
     305           0 :                           libpq_gettext("malformed SCRAM message (expected character \"=\" for attribute \"%c\")\n"),
     306             :                           attr);
     307           0 :         return NULL;
     308             :     }
     309         134 :     begin++;
     310             : 
     311         134 :     end = begin;
     312        4014 :     while (*end && *end != ',')
     313        3880 :         end++;
     314             : 
     315         134 :     if (*end)
     316             :     {
     317          72 :         *end = '\0';
     318          72 :         *input = end + 1;
     319             :     }
     320             :     else
     321          62 :         *input = end;
     322             : 
     323         134 :     return begin;
     324             : }
     325             : 
     326             : /*
     327             :  * Build the first exchange message sent by the client.
     328             :  */
     329             : static char *
     330          36 : build_client_first_message(fe_scram_state *state)
     331             : {
     332          36 :     PGconn     *conn = state->conn;
     333             :     char        raw_nonce[SCRAM_RAW_NONCE_LEN + 1];
     334             :     char       *result;
     335             :     int         channel_info_len;
     336             :     int         encoded_len;
     337             :     PQExpBufferData buf;
     338             : 
     339             :     /*
     340             :      * Generate a "raw" nonce.  This is converted to ASCII-printable form by
     341             :      * base64-encoding it.
     342             :      */
     343          36 :     if (!pg_strong_random(raw_nonce, SCRAM_RAW_NONCE_LEN))
     344             :     {
     345           0 :         printfPQExpBuffer(&conn->errorMessage,
     346           0 :                           libpq_gettext("could not generate nonce\n"));
     347           0 :         return NULL;
     348             :     }
     349             : 
     350          36 :     encoded_len = pg_b64_enc_len(SCRAM_RAW_NONCE_LEN);
     351             :     /* don't forget the zero-terminator */
     352          36 :     state->client_nonce = malloc(encoded_len + 1);
     353          36 :     if (state->client_nonce == NULL)
     354             :     {
     355           0 :         printfPQExpBuffer(&conn->errorMessage,
     356           0 :                           libpq_gettext("out of memory\n"));
     357           0 :         return NULL;
     358             :     }
     359          36 :     encoded_len = pg_b64_encode(raw_nonce, SCRAM_RAW_NONCE_LEN,
     360             :                                 state->client_nonce, encoded_len);
     361          36 :     if (encoded_len < 0)
     362             :     {
     363           0 :         printfPQExpBuffer(&conn->errorMessage,
     364           0 :                           libpq_gettext("could not encode nonce\n"));
     365           0 :         return NULL;
     366             :     }
     367          36 :     state->client_nonce[encoded_len] = '\0';
     368             : 
     369             :     /*
     370             :      * Generate message.  The username is left empty as the backend uses the
     371             :      * value provided by the startup packet.  Also, as this username is not
     372             :      * prepared with SASLprep, the message parsing would fail if it includes
     373             :      * '=' or ',' characters.
     374             :      */
     375             : 
     376          36 :     initPQExpBuffer(&buf);
     377             : 
     378             :     /*
     379             :      * First build the gs2-header with channel binding information.
     380             :      */
     381          36 :     if (strcmp(state->sasl_mechanism, SCRAM_SHA_256_PLUS_NAME) == 0)
     382             :     {
     383             :         Assert(conn->ssl_in_use);
     384           4 :         appendPQExpBufferStr(&buf, "p=tls-server-end-point");
     385             :     }
     386             : #ifdef HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH
     387          32 :     else if (conn->channel_binding[0] != 'd' && /* disable */
     388          30 :              conn->ssl_in_use)
     389             :     {
     390             :         /*
     391             :          * Client supports channel binding, but thinks the server does not.
     392             :          */
     393           0 :         appendPQExpBufferChar(&buf, 'y');
     394             :     }
     395             : #endif
     396             :     else
     397             :     {
     398             :         /*
     399             :          * Client does not support channel binding, or has disabled it.
     400             :          */
     401          32 :         appendPQExpBufferChar(&buf, 'n');
     402             :     }
     403             : 
     404          36 :     if (PQExpBufferDataBroken(buf))
     405           0 :         goto oom_error;
     406             : 
     407          36 :     channel_info_len = buf.len;
     408             : 
     409          36 :     appendPQExpBuffer(&buf, ",,n=,r=%s", state->client_nonce);
     410          36 :     if (PQExpBufferDataBroken(buf))
     411           0 :         goto oom_error;
     412             : 
     413             :     /*
     414             :      * The first message content needs to be saved without channel binding
     415             :      * information.
     416             :      */
     417          36 :     state->client_first_message_bare = strdup(buf.data + channel_info_len + 2);
     418          36 :     if (!state->client_first_message_bare)
     419           0 :         goto oom_error;
     420             : 
     421          36 :     result = strdup(buf.data);
     422          36 :     if (result == NULL)
     423           0 :         goto oom_error;
     424             : 
     425          36 :     termPQExpBuffer(&buf);
     426          36 :     return result;
     427             : 
     428           0 : oom_error:
     429           0 :     termPQExpBuffer(&buf);
     430           0 :     printfPQExpBuffer(&conn->errorMessage,
     431           0 :                       libpq_gettext("out of memory\n"));
     432           0 :     return NULL;
     433             : }
     434             : 
     435             : /*
     436             :  * Build the final exchange message sent from the client.
     437             :  */
     438             : static char *
     439          36 : build_client_final_message(fe_scram_state *state)
     440             : {
     441             :     PQExpBufferData buf;
     442          36 :     PGconn     *conn = state->conn;
     443             :     uint8       client_proof[SCRAM_KEY_LEN];
     444             :     char       *result;
     445             :     int         encoded_len;
     446             : 
     447          36 :     initPQExpBuffer(&buf);
     448             : 
     449             :     /*
     450             :      * Construct client-final-message-without-proof.  We need to remember it
     451             :      * for verifying the server proof in the final step of authentication.
     452             :      *
     453             :      * The channel binding flag handling (p/y/n) must be consistent with
     454             :      * build_client_first_message(), because the server will check that it's
     455             :      * the same flag both times.
     456             :      */
     457          36 :     if (strcmp(state->sasl_mechanism, SCRAM_SHA_256_PLUS_NAME) == 0)
     458             :     {
     459             : #ifdef HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH
     460           4 :         char       *cbind_data = NULL;
     461           4 :         size_t      cbind_data_len = 0;
     462             :         size_t      cbind_header_len;
     463             :         char       *cbind_input;
     464             :         size_t      cbind_input_len;
     465             :         int         encoded_cbind_len;
     466             : 
     467             :         /* Fetch hash data of server's SSL certificate */
     468             :         cbind_data =
     469           4 :             pgtls_get_peer_certificate_hash(state->conn,
     470             :                                             &cbind_data_len);
     471           4 :         if (cbind_data == NULL)
     472             :         {
     473             :             /* error message is already set on error */
     474           0 :             termPQExpBuffer(&buf);
     475           0 :             return NULL;
     476             :         }
     477             : 
     478           4 :         appendPQExpBufferStr(&buf, "c=");
     479             : 
     480             :         /* p=type,, */
     481           4 :         cbind_header_len = strlen("p=tls-server-end-point,,");
     482           4 :         cbind_input_len = cbind_header_len + cbind_data_len;
     483           4 :         cbind_input = malloc(cbind_input_len);
     484           4 :         if (!cbind_input)
     485             :         {
     486           0 :             free(cbind_data);
     487           0 :             goto oom_error;
     488             :         }
     489           4 :         memcpy(cbind_input, "p=tls-server-end-point,,", cbind_header_len);
     490           4 :         memcpy(cbind_input + cbind_header_len, cbind_data, cbind_data_len);
     491             : 
     492           4 :         encoded_cbind_len = pg_b64_enc_len(cbind_input_len);
     493           4 :         if (!enlargePQExpBuffer(&buf, encoded_cbind_len))
     494             :         {
     495           0 :             free(cbind_data);
     496           0 :             free(cbind_input);
     497           0 :             goto oom_error;
     498             :         }
     499           4 :         encoded_cbind_len = pg_b64_encode(cbind_input, cbind_input_len,
     500           4 :                                           buf.data + buf.len,
     501             :                                           encoded_cbind_len);
     502           4 :         if (encoded_cbind_len < 0)
     503             :         {
     504           0 :             free(cbind_data);
     505           0 :             free(cbind_input);
     506           0 :             termPQExpBuffer(&buf);
     507           0 :             printfPQExpBuffer(&conn->errorMessage,
     508             :                               "could not encode cbind data for channel binding\n");
     509           0 :             return NULL;
     510             :         }
     511           4 :         buf.len += encoded_cbind_len;
     512           4 :         buf.data[buf.len] = '\0';
     513             : 
     514           4 :         free(cbind_data);
     515           4 :         free(cbind_input);
     516             : #else
     517             :         /*
     518             :          * Chose channel binding, but the SSL library doesn't support it.
     519             :          * Shouldn't happen.
     520             :          */
     521             :         termPQExpBuffer(&buf);
     522             :         printfPQExpBuffer(&conn->errorMessage,
     523             :                           "channel binding not supported by this build\n");
     524             :         return NULL;
     525             : #endif                          /* HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH */
     526             :     }
     527             : #ifdef HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH
     528          32 :     else if (conn->channel_binding[0] != 'd' && /* disable */
     529          30 :              conn->ssl_in_use)
     530           0 :         appendPQExpBufferStr(&buf, "c=eSws"); /* base64 of "y,," */
     531             : #endif
     532             :     else
     533          32 :         appendPQExpBufferStr(&buf, "c=biws"); /* base64 of "n,," */
     534             : 
     535          36 :     if (PQExpBufferDataBroken(buf))
     536           0 :         goto oom_error;
     537             : 
     538          36 :     appendPQExpBuffer(&buf, ",r=%s", state->nonce);
     539          36 :     if (PQExpBufferDataBroken(buf))
     540           0 :         goto oom_error;
     541             : 
     542          36 :     state->client_final_message_without_proof = strdup(buf.data);
     543          36 :     if (state->client_final_message_without_proof == NULL)
     544           0 :         goto oom_error;
     545             : 
     546             :     /* Append proof to it, to form client-final-message. */
     547          36 :     calculate_client_proof(state,
     548          36 :                            state->client_final_message_without_proof,
     549             :                            client_proof);
     550             : 
     551          36 :     appendPQExpBufferStr(&buf, ",p=");
     552          36 :     encoded_len = pg_b64_enc_len(SCRAM_KEY_LEN);
     553          36 :     if (!enlargePQExpBuffer(&buf, encoded_len))
     554           0 :         goto oom_error;
     555          36 :     encoded_len = pg_b64_encode((char *) client_proof,
     556             :                                 SCRAM_KEY_LEN,
     557          36 :                                 buf.data + buf.len,
     558             :                                 encoded_len);
     559          36 :     if (encoded_len < 0)
     560             :     {
     561           0 :         termPQExpBuffer(&buf);
     562           0 :         printfPQExpBuffer(&conn->errorMessage,
     563           0 :                           libpq_gettext("could not encode client proof\n"));
     564           0 :         return NULL;
     565             :     }
     566          36 :     buf.len += encoded_len;
     567          36 :     buf.data[buf.len] = '\0';
     568             : 
     569          36 :     result = strdup(buf.data);
     570          36 :     if (result == NULL)
     571           0 :         goto oom_error;
     572             : 
     573          36 :     termPQExpBuffer(&buf);
     574          36 :     return result;
     575             : 
     576           0 : oom_error:
     577           0 :     termPQExpBuffer(&buf);
     578           0 :     printfPQExpBuffer(&conn->errorMessage,
     579           0 :                       libpq_gettext("out of memory\n"));
     580           0 :     return NULL;
     581             : }
     582             : 
     583             : /*
     584             :  * Read the first exchange message coming from the server.
     585             :  */
     586             : static bool
     587          36 : read_server_first_message(fe_scram_state *state, char *input)
     588             : {
     589          36 :     PGconn     *conn = state->conn;
     590             :     char       *iterations_str;
     591             :     char       *endptr;
     592             :     char       *encoded_salt;
     593             :     char       *nonce;
     594             :     int         decoded_salt_len;
     595             : 
     596          36 :     state->server_first_message = strdup(input);
     597          36 :     if (state->server_first_message == NULL)
     598             :     {
     599           0 :         printfPQExpBuffer(&conn->errorMessage,
     600           0 :                           libpq_gettext("out of memory\n"));
     601           0 :         return false;
     602             :     }
     603             : 
     604             :     /* parse the message */
     605          36 :     nonce = read_attr_value(&input, 'r',
     606             :                             &conn->errorMessage);
     607          36 :     if (nonce == NULL)
     608             :     {
     609             :         /* read_attr_value() has generated an error string */
     610           0 :         return false;
     611             :     }
     612             : 
     613             :     /* Verify immediately that the server used our part of the nonce */
     614          36 :     if (strlen(nonce) < strlen(state->client_nonce) ||
     615          36 :         memcmp(nonce, state->client_nonce, strlen(state->client_nonce)) != 0)
     616             :     {
     617           0 :         printfPQExpBuffer(&conn->errorMessage,
     618           0 :                           libpq_gettext("invalid SCRAM response (nonce mismatch)\n"));
     619           0 :         return false;
     620             :     }
     621             : 
     622          36 :     state->nonce = strdup(nonce);
     623          36 :     if (state->nonce == NULL)
     624             :     {
     625           0 :         printfPQExpBuffer(&conn->errorMessage,
     626           0 :                           libpq_gettext("out of memory\n"));
     627           0 :         return false;
     628             :     }
     629             : 
     630          36 :     encoded_salt = read_attr_value(&input, 's', &conn->errorMessage);
     631          36 :     if (encoded_salt == NULL)
     632             :     {
     633             :         /* read_attr_value() has generated an error string */
     634           0 :         return false;
     635             :     }
     636          36 :     decoded_salt_len = pg_b64_dec_len(strlen(encoded_salt));
     637          36 :     state->salt = malloc(decoded_salt_len);
     638          36 :     if (state->salt == NULL)
     639             :     {
     640           0 :         printfPQExpBuffer(&conn->errorMessage,
     641           0 :                           libpq_gettext("out of memory\n"));
     642           0 :         return false;
     643             :     }
     644         108 :     state->saltlen = pg_b64_decode(encoded_salt,
     645          36 :                                    strlen(encoded_salt),
     646             :                                    state->salt,
     647             :                                    decoded_salt_len);
     648          36 :     if (state->saltlen < 0)
     649             :     {
     650           0 :         printfPQExpBuffer(&conn->errorMessage,
     651           0 :                           libpq_gettext("malformed SCRAM message (invalid salt)\n"));
     652           0 :         return false;
     653             :     }
     654             : 
     655          36 :     iterations_str = read_attr_value(&input, 'i', &conn->errorMessage);
     656          36 :     if (iterations_str == NULL)
     657             :     {
     658             :         /* read_attr_value() has generated an error string */
     659           0 :         return false;
     660             :     }
     661          36 :     state->iterations = strtol(iterations_str, &endptr, 10);
     662          36 :     if (*endptr != '\0' || state->iterations < 1)
     663             :     {
     664           0 :         printfPQExpBuffer(&conn->errorMessage,
     665           0 :                           libpq_gettext("malformed SCRAM message (invalid iteration count)\n"));
     666           0 :         return false;
     667             :     }
     668             : 
     669          36 :     if (*input != '\0')
     670           0 :         printfPQExpBuffer(&conn->errorMessage,
     671           0 :                           libpq_gettext("malformed SCRAM message (garbage at end of server-first-message)\n"));
     672             : 
     673          36 :     return true;
     674             : }
     675             : 
     676             : /*
     677             :  * Read the final exchange message coming from the server.
     678             :  */
     679             : static bool
     680          26 : read_server_final_message(fe_scram_state *state, char *input)
     681             : {
     682          26 :     PGconn     *conn = state->conn;
     683             :     char       *encoded_server_signature;
     684             :     char       *decoded_server_signature;
     685             :     int         server_signature_len;
     686             : 
     687          26 :     state->server_final_message = strdup(input);
     688          26 :     if (!state->server_final_message)
     689             :     {
     690           0 :         printfPQExpBuffer(&conn->errorMessage,
     691           0 :                           libpq_gettext("out of memory\n"));
     692           0 :         return false;
     693             :     }
     694             : 
     695             :     /* Check for error result. */
     696          26 :     if (*input == 'e')
     697             :     {
     698           0 :         char       *errmsg = read_attr_value(&input, 'e',
     699             :                                              &conn->errorMessage);
     700             : 
     701           0 :         printfPQExpBuffer(&conn->errorMessage,
     702           0 :                           libpq_gettext("error received from server in SCRAM exchange: %s\n"),
     703             :                           errmsg);
     704           0 :         return false;
     705             :     }
     706             : 
     707             :     /* Parse the message. */
     708          26 :     encoded_server_signature = read_attr_value(&input, 'v',
     709             :                                                &conn->errorMessage);
     710          26 :     if (encoded_server_signature == NULL)
     711             :     {
     712             :         /* read_attr_value() has generated an error message */
     713           0 :         return false;
     714             :     }
     715             : 
     716          26 :     if (*input != '\0')
     717           0 :         printfPQExpBuffer(&conn->errorMessage,
     718           0 :                           libpq_gettext("malformed SCRAM message (garbage at end of server-final-message)\n"));
     719             : 
     720          26 :     server_signature_len = pg_b64_dec_len(strlen(encoded_server_signature));
     721          26 :     decoded_server_signature = malloc(server_signature_len);
     722          26 :     if (!decoded_server_signature)
     723             :     {
     724           0 :         printfPQExpBuffer(&conn->errorMessage,
     725           0 :                           libpq_gettext("out of memory\n"));
     726           0 :         return false;
     727             :     }
     728             : 
     729          26 :     server_signature_len = pg_b64_decode(encoded_server_signature,
     730          26 :                                          strlen(encoded_server_signature),
     731             :                                          decoded_server_signature,
     732             :                                          server_signature_len);
     733          26 :     if (server_signature_len != SCRAM_KEY_LEN)
     734             :     {
     735           0 :         free(decoded_server_signature);
     736           0 :         printfPQExpBuffer(&conn->errorMessage,
     737           0 :                           libpq_gettext("malformed SCRAM message (invalid server signature)\n"));
     738           0 :         return false;
     739             :     }
     740          26 :     memcpy(state->ServerSignature, decoded_server_signature, SCRAM_KEY_LEN);
     741          26 :     free(decoded_server_signature);
     742             : 
     743          26 :     return true;
     744             : }
     745             : 
     746             : /*
     747             :  * Calculate the client proof, part of the final exchange message sent
     748             :  * by the client.
     749             :  */
     750             : static void
     751          36 : calculate_client_proof(fe_scram_state *state,
     752             :                        const char *client_final_message_without_proof,
     753             :                        uint8 *result)
     754             : {
     755             :     uint8       StoredKey[SCRAM_KEY_LEN];
     756             :     uint8       ClientKey[SCRAM_KEY_LEN];
     757             :     uint8       ClientSignature[SCRAM_KEY_LEN];
     758             :     int         i;
     759             :     scram_HMAC_ctx ctx;
     760             : 
     761             :     /*
     762             :      * Calculate SaltedPassword, and store it in 'state' so that we can reuse
     763             :      * it later in verify_server_signature.
     764             :      */
     765          36 :     scram_SaltedPassword(state->password, state->salt, state->saltlen,
     766          36 :                          state->iterations, state->SaltedPassword);
     767             : 
     768          36 :     scram_ClientKey(state->SaltedPassword, ClientKey);
     769          36 :     scram_H(ClientKey, SCRAM_KEY_LEN, StoredKey);
     770             : 
     771          36 :     scram_HMAC_init(&ctx, StoredKey, SCRAM_KEY_LEN);
     772          72 :     scram_HMAC_update(&ctx,
     773          36 :                       state->client_first_message_bare,
     774          36 :                       strlen(state->client_first_message_bare));
     775          36 :     scram_HMAC_update(&ctx, ",", 1);
     776          72 :     scram_HMAC_update(&ctx,
     777          36 :                       state->server_first_message,
     778          36 :                       strlen(state->server_first_message));
     779          36 :     scram_HMAC_update(&ctx, ",", 1);
     780          36 :     scram_HMAC_update(&ctx,
     781             :                       client_final_message_without_proof,
     782          36 :                       strlen(client_final_message_without_proof));
     783          36 :     scram_HMAC_final(ClientSignature, &ctx);
     784             : 
     785        1188 :     for (i = 0; i < SCRAM_KEY_LEN; i++)
     786        1152 :         result[i] = ClientKey[i] ^ ClientSignature[i];
     787          36 : }
     788             : 
     789             : /*
     790             :  * Validate the server signature, received as part of the final exchange
     791             :  * message received from the server.
     792             :  */
     793             : static bool
     794          26 : verify_server_signature(fe_scram_state *state)
     795             : {
     796             :     uint8       expected_ServerSignature[SCRAM_KEY_LEN];
     797             :     uint8       ServerKey[SCRAM_KEY_LEN];
     798             :     scram_HMAC_ctx ctx;
     799             : 
     800          26 :     scram_ServerKey(state->SaltedPassword, ServerKey);
     801             : 
     802             :     /* calculate ServerSignature */
     803          26 :     scram_HMAC_init(&ctx, ServerKey, SCRAM_KEY_LEN);
     804          52 :     scram_HMAC_update(&ctx,
     805          26 :                       state->client_first_message_bare,
     806          26 :                       strlen(state->client_first_message_bare));
     807          26 :     scram_HMAC_update(&ctx, ",", 1);
     808          52 :     scram_HMAC_update(&ctx,
     809          26 :                       state->server_first_message,
     810          26 :                       strlen(state->server_first_message));
     811          26 :     scram_HMAC_update(&ctx, ",", 1);
     812          52 :     scram_HMAC_update(&ctx,
     813          26 :                       state->client_final_message_without_proof,
     814          26 :                       strlen(state->client_final_message_without_proof));
     815          26 :     scram_HMAC_final(expected_ServerSignature, &ctx);
     816             : 
     817          26 :     if (memcmp(expected_ServerSignature, state->ServerSignature, SCRAM_KEY_LEN) != 0)
     818           0 :         return false;
     819             : 
     820          26 :     return true;
     821             : }
     822             : 
     823             : /*
     824             :  * Build a new SCRAM secret.
     825             :  */
     826             : char *
     827           0 : pg_fe_scram_build_secret(const char *password)
     828             : {
     829             :     char       *prep_password;
     830             :     pg_saslprep_rc rc;
     831             :     char        saltbuf[SCRAM_DEFAULT_SALT_LEN];
     832             :     char       *result;
     833             : 
     834             :     /*
     835             :      * Normalize the password with SASLprep.  If that doesn't work, because
     836             :      * the password isn't valid UTF-8 or contains prohibited characters, just
     837             :      * proceed with the original password.  (See comments at top of file.)
     838             :      */
     839           0 :     rc = pg_saslprep(password, &prep_password);
     840           0 :     if (rc == SASLPREP_OOM)
     841           0 :         return NULL;
     842           0 :     if (rc == SASLPREP_SUCCESS)
     843           0 :         password = (const char *) prep_password;
     844             : 
     845             :     /* Generate a random salt */
     846           0 :     if (!pg_strong_random(saltbuf, SCRAM_DEFAULT_SALT_LEN))
     847             :     {
     848           0 :         if (prep_password)
     849           0 :             free(prep_password);
     850           0 :         return NULL;
     851             :     }
     852             : 
     853           0 :     result = scram_build_secret(saltbuf, SCRAM_DEFAULT_SALT_LEN,
     854             :                                 SCRAM_DEFAULT_ITERATIONS, password);
     855             : 
     856           0 :     if (prep_password)
     857           0 :         free(prep_password);
     858             : 
     859           0 :     return result;
     860             : }

Generated by: LCOV version 1.13