LCOV - code coverage report
Current view: top level - src/interfaces/libpq - fe-auth-scram.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 239 374 63.9 %
Date: 2019-11-13 21:06:57 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-2019, 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             :         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             :         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             :         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             :         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             : error:
     280           0 :     *done = true;
     281           0 :     *success = false;
     282           0 :     return;
     283             : }
     284             : 
     285             : /*
     286             :  * Read value for an attribute part of a SCRAM message.
     287             :  */
     288             : static char *
     289         134 : read_attr_value(char **input, char attr, PQExpBuffer errorMessage)
     290             : {
     291         134 :     char       *begin = *input;
     292             :     char       *end;
     293             : 
     294         134 :     if (*begin != attr)
     295             :     {
     296           0 :         printfPQExpBuffer(errorMessage,
     297           0 :                           libpq_gettext("malformed SCRAM message (attribute \"%c\" expected)\n"),
     298             :                           attr);
     299           0 :         return NULL;
     300             :     }
     301         134 :     begin++;
     302             : 
     303         134 :     if (*begin != '=')
     304             :     {
     305           0 :         printfPQExpBuffer(errorMessage,
     306           0 :                           libpq_gettext("malformed SCRAM message (expected character \"=\" for attribute \"%c\")\n"),
     307             :                           attr);
     308           0 :         return NULL;
     309             :     }
     310         134 :     begin++;
     311             : 
     312         134 :     end = begin;
     313        4148 :     while (*end && *end != ',')
     314        3880 :         end++;
     315             : 
     316         134 :     if (*end)
     317             :     {
     318          72 :         *end = '\0';
     319          72 :         *input = end + 1;
     320             :     }
     321             :     else
     322          62 :         *input = end;
     323             : 
     324         134 :     return begin;
     325             : }
     326             : 
     327             : /*
     328             :  * Build the first exchange message sent by the client.
     329             :  */
     330             : static char *
     331          36 : build_client_first_message(fe_scram_state *state)
     332             : {
     333          36 :     PGconn     *conn = state->conn;
     334             :     char        raw_nonce[SCRAM_RAW_NONCE_LEN + 1];
     335             :     char       *result;
     336             :     int         channel_info_len;
     337             :     int         encoded_len;
     338             :     PQExpBufferData buf;
     339             : 
     340             :     /*
     341             :      * Generate a "raw" nonce.  This is converted to ASCII-printable form by
     342             :      * base64-encoding it.
     343             :      */
     344          36 :     if (!pg_strong_random(raw_nonce, SCRAM_RAW_NONCE_LEN))
     345             :     {
     346           0 :         printfPQExpBuffer(&conn->errorMessage,
     347           0 :                           libpq_gettext("could not generate nonce\n"));
     348           0 :         return NULL;
     349             :     }
     350             : 
     351          36 :     encoded_len = pg_b64_enc_len(SCRAM_RAW_NONCE_LEN);
     352             :     /* don't forget the zero-terminator */
     353          36 :     state->client_nonce = malloc(encoded_len + 1);
     354          36 :     if (state->client_nonce == NULL)
     355             :     {
     356           0 :         printfPQExpBuffer(&conn->errorMessage,
     357           0 :                           libpq_gettext("out of memory\n"));
     358           0 :         return NULL;
     359             :     }
     360          36 :     encoded_len = pg_b64_encode(raw_nonce, SCRAM_RAW_NONCE_LEN,
     361             :                                 state->client_nonce, encoded_len);
     362          36 :     if (encoded_len < 0)
     363             :     {
     364           0 :         printfPQExpBuffer(&conn->errorMessage,
     365           0 :                           libpq_gettext("could not encode nonce\n"));
     366           0 :         return NULL;
     367             :     }
     368          36 :     state->client_nonce[encoded_len] = '\0';
     369             : 
     370             :     /*
     371             :      * Generate message.  The username is left empty as the backend uses the
     372             :      * value provided by the startup packet.  Also, as this username is not
     373             :      * prepared with SASLprep, the message parsing would fail if it includes
     374             :      * '=' or ',' characters.
     375             :      */
     376             : 
     377          36 :     initPQExpBuffer(&buf);
     378             : 
     379             :     /*
     380             :      * First build the gs2-header with channel binding information.
     381             :      */
     382          36 :     if (strcmp(state->sasl_mechanism, SCRAM_SHA_256_PLUS_NAME) == 0)
     383             :     {
     384             :         Assert(conn->ssl_in_use);
     385           4 :         appendPQExpBufferStr(&buf, "p=tls-server-end-point");
     386             :     }
     387             : #ifdef HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH
     388          62 :     else if (conn->channel_binding[0] != 'd' && /* disable */
     389          30 :              conn->ssl_in_use)
     390             :     {
     391             :         /*
     392             :          * Client supports channel binding, but thinks the server does not.
     393             :          */
     394           0 :         appendPQExpBufferChar(&buf, 'y');
     395             :     }
     396             : #endif
     397             :     else
     398             :     {
     399             :         /*
     400             :          * Client does not support channel binding, or has disabled it.
     401             :          */
     402          32 :         appendPQExpBufferChar(&buf, 'n');
     403             :     }
     404             : 
     405          36 :     if (PQExpBufferDataBroken(buf))
     406           0 :         goto oom_error;
     407             : 
     408          36 :     channel_info_len = buf.len;
     409             : 
     410          36 :     appendPQExpBuffer(&buf, ",,n=,r=%s", state->client_nonce);
     411          36 :     if (PQExpBufferDataBroken(buf))
     412           0 :         goto oom_error;
     413             : 
     414             :     /*
     415             :      * The first message content needs to be saved without channel binding
     416             :      * information.
     417             :      */
     418          36 :     state->client_first_message_bare = strdup(buf.data + channel_info_len + 2);
     419          36 :     if (!state->client_first_message_bare)
     420           0 :         goto oom_error;
     421             : 
     422          36 :     result = strdup(buf.data);
     423          36 :     if (result == NULL)
     424           0 :         goto oom_error;
     425             : 
     426          36 :     termPQExpBuffer(&buf);
     427          36 :     return result;
     428             : 
     429             : oom_error:
     430           0 :     termPQExpBuffer(&buf);
     431           0 :     printfPQExpBuffer(&conn->errorMessage,
     432           0 :                       libpq_gettext("out of memory\n"));
     433           0 :     return NULL;
     434             : }
     435             : 
     436             : /*
     437             :  * Build the final exchange message sent from the client.
     438             :  */
     439             : static char *
     440          36 : build_client_final_message(fe_scram_state *state)
     441             : {
     442             :     PQExpBufferData buf;
     443          36 :     PGconn     *conn = state->conn;
     444             :     uint8       client_proof[SCRAM_KEY_LEN];
     445             :     char       *result;
     446             :     int         encoded_len;
     447             : 
     448          36 :     initPQExpBuffer(&buf);
     449             : 
     450             :     /*
     451             :      * Construct client-final-message-without-proof.  We need to remember it
     452             :      * for verifying the server proof in the final step of authentication.
     453             :      *
     454             :      * The channel binding flag handling (p/y/n) must be consistent with
     455             :      * build_client_first_message(), because the server will check that it's
     456             :      * the same flag both times.
     457             :      */
     458          36 :     if (strcmp(state->sasl_mechanism, SCRAM_SHA_256_PLUS_NAME) == 0)
     459             :     {
     460             : #ifdef HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH
     461           4 :         char       *cbind_data = NULL;
     462           4 :         size_t      cbind_data_len = 0;
     463             :         size_t      cbind_header_len;
     464             :         char       *cbind_input;
     465             :         size_t      cbind_input_len;
     466             :         int         encoded_cbind_len;
     467             : 
     468             :         /* Fetch hash data of server's SSL certificate */
     469           4 :         cbind_data =
     470           4 :             pgtls_get_peer_certificate_hash(state->conn,
     471             :                                             &cbind_data_len);
     472           4 :         if (cbind_data == NULL)
     473             :         {
     474             :             /* error message is already set on error */
     475           0 :             termPQExpBuffer(&buf);
     476           0 :             return NULL;
     477             :         }
     478             : 
     479           4 :         appendPQExpBufferStr(&buf, "c=");
     480             : 
     481             :         /* p=type,, */
     482           4 :         cbind_header_len = strlen("p=tls-server-end-point,,");
     483           4 :         cbind_input_len = cbind_header_len + cbind_data_len;
     484           4 :         cbind_input = malloc(cbind_input_len);
     485           4 :         if (!cbind_input)
     486             :         {
     487           0 :             free(cbind_data);
     488           0 :             goto oom_error;
     489             :         }
     490           4 :         memcpy(cbind_input, "p=tls-server-end-point,,", cbind_header_len);
     491           4 :         memcpy(cbind_input + cbind_header_len, cbind_data, cbind_data_len);
     492             : 
     493           4 :         encoded_cbind_len = pg_b64_enc_len(cbind_input_len);
     494           4 :         if (!enlargePQExpBuffer(&buf, encoded_cbind_len))
     495             :         {
     496           0 :             free(cbind_data);
     497           0 :             free(cbind_input);
     498           0 :             goto oom_error;
     499             :         }
     500           4 :         encoded_cbind_len = pg_b64_encode(cbind_input, cbind_input_len,
     501           4 :                                           buf.data + buf.len,
     502             :                                           encoded_cbind_len);
     503           4 :         if (encoded_cbind_len < 0)
     504             :         {
     505           0 :             free(cbind_data);
     506           0 :             free(cbind_input);
     507           0 :             termPQExpBuffer(&buf);
     508           0 :             printfPQExpBuffer(&conn->errorMessage,
     509             :                               "could not encode cbind data for channel binding\n");
     510           0 :             return NULL;
     511             :         }
     512           4 :         buf.len += encoded_cbind_len;
     513           4 :         buf.data[buf.len] = '\0';
     514             : 
     515           4 :         free(cbind_data);
     516           4 :         free(cbind_input);
     517             : #else
     518             :         /*
     519             :          * Chose channel binding, but the SSL library doesn't support it.
     520             :          * Shouldn't happen.
     521             :          */
     522             :         termPQExpBuffer(&buf);
     523             :         printfPQExpBuffer(&conn->errorMessage,
     524             :                           "channel binding not supported by this build\n");
     525             :         return NULL;
     526             : #endif                          /* HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH */
     527             :     }
     528             : #ifdef HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH
     529          62 :     else if (conn->channel_binding[0] != 'd' && /* disable */
     530          30 :              conn->ssl_in_use)
     531           0 :         appendPQExpBufferStr(&buf, "c=eSws"); /* base64 of "y,," */
     532             : #endif
     533             :     else
     534          32 :         appendPQExpBufferStr(&buf, "c=biws"); /* base64 of "n,," */
     535             : 
     536          36 :     if (PQExpBufferDataBroken(buf))
     537           0 :         goto oom_error;
     538             : 
     539          36 :     appendPQExpBuffer(&buf, ",r=%s", state->nonce);
     540          36 :     if (PQExpBufferDataBroken(buf))
     541           0 :         goto oom_error;
     542             : 
     543          36 :     state->client_final_message_without_proof = strdup(buf.data);
     544          36 :     if (state->client_final_message_without_proof == NULL)
     545           0 :         goto oom_error;
     546             : 
     547             :     /* Append proof to it, to form client-final-message. */
     548          36 :     calculate_client_proof(state,
     549          36 :                            state->client_final_message_without_proof,
     550             :                            client_proof);
     551             : 
     552          36 :     appendPQExpBufferStr(&buf, ",p=");
     553          36 :     encoded_len = pg_b64_enc_len(SCRAM_KEY_LEN);
     554          36 :     if (!enlargePQExpBuffer(&buf, encoded_len))
     555           0 :         goto oom_error;
     556          36 :     encoded_len = pg_b64_encode((char *) client_proof,
     557             :                                 SCRAM_KEY_LEN,
     558          36 :                                 buf.data + buf.len,
     559             :                                 encoded_len);
     560          36 :     if (encoded_len < 0)
     561             :     {
     562           0 :         termPQExpBuffer(&buf);
     563           0 :         printfPQExpBuffer(&conn->errorMessage,
     564           0 :                           libpq_gettext("could not encode client proof\n"));
     565           0 :         return NULL;
     566             :     }
     567          36 :     buf.len += encoded_len;
     568          36 :     buf.data[buf.len] = '\0';
     569             : 
     570          36 :     result = strdup(buf.data);
     571          36 :     if (result == NULL)
     572           0 :         goto oom_error;
     573             : 
     574          36 :     termPQExpBuffer(&buf);
     575          36 :     return result;
     576             : 
     577             : oom_error:
     578           0 :     termPQExpBuffer(&buf);
     579           0 :     printfPQExpBuffer(&conn->errorMessage,
     580           0 :                       libpq_gettext("out of memory\n"));
     581           0 :     return NULL;
     582             : }
     583             : 
     584             : /*
     585             :  * Read the first exchange message coming from the server.
     586             :  */
     587             : static bool
     588          36 : read_server_first_message(fe_scram_state *state, char *input)
     589             : {
     590          36 :     PGconn     *conn = state->conn;
     591             :     char       *iterations_str;
     592             :     char       *endptr;
     593             :     char       *encoded_salt;
     594             :     char       *nonce;
     595             :     int         decoded_salt_len;
     596             : 
     597          36 :     state->server_first_message = strdup(input);
     598          36 :     if (state->server_first_message == NULL)
     599             :     {
     600           0 :         printfPQExpBuffer(&conn->errorMessage,
     601           0 :                           libpq_gettext("out of memory\n"));
     602           0 :         return false;
     603             :     }
     604             : 
     605             :     /* parse the message */
     606          36 :     nonce = read_attr_value(&input, 'r',
     607             :                             &conn->errorMessage);
     608          36 :     if (nonce == NULL)
     609             :     {
     610             :         /* read_attr_value() has generated an error string */
     611           0 :         return false;
     612             :     }
     613             : 
     614             :     /* Verify immediately that the server used our part of the nonce */
     615          72 :     if (strlen(nonce) < strlen(state->client_nonce) ||
     616          36 :         memcmp(nonce, state->client_nonce, strlen(state->client_nonce)) != 0)
     617             :     {
     618           0 :         printfPQExpBuffer(&conn->errorMessage,
     619           0 :                           libpq_gettext("invalid SCRAM response (nonce mismatch)\n"));
     620           0 :         return false;
     621             :     }
     622             : 
     623          36 :     state->nonce = strdup(nonce);
     624          36 :     if (state->nonce == NULL)
     625             :     {
     626           0 :         printfPQExpBuffer(&conn->errorMessage,
     627           0 :                           libpq_gettext("out of memory\n"));
     628           0 :         return false;
     629             :     }
     630             : 
     631          36 :     encoded_salt = read_attr_value(&input, 's', &conn->errorMessage);
     632          36 :     if (encoded_salt == NULL)
     633             :     {
     634             :         /* read_attr_value() has generated an error string */
     635           0 :         return false;
     636             :     }
     637          36 :     decoded_salt_len = pg_b64_dec_len(strlen(encoded_salt));
     638          36 :     state->salt = malloc(decoded_salt_len);
     639          36 :     if (state->salt == NULL)
     640             :     {
     641           0 :         printfPQExpBuffer(&conn->errorMessage,
     642           0 :                           libpq_gettext("out of memory\n"));
     643           0 :         return false;
     644             :     }
     645          72 :     state->saltlen = pg_b64_decode(encoded_salt,
     646          36 :                                    strlen(encoded_salt),
     647             :                                    state->salt,
     648             :                                    decoded_salt_len);
     649          36 :     if (state->saltlen < 0)
     650             :     {
     651           0 :         printfPQExpBuffer(&conn->errorMessage,
     652           0 :                           libpq_gettext("malformed SCRAM message (invalid salt)\n"));
     653           0 :         return false;
     654             :     }
     655             : 
     656          36 :     iterations_str = read_attr_value(&input, 'i', &conn->errorMessage);
     657          36 :     if (iterations_str == NULL)
     658             :     {
     659             :         /* read_attr_value() has generated an error string */
     660           0 :         return false;
     661             :     }
     662          36 :     state->iterations = strtol(iterations_str, &endptr, 10);
     663          36 :     if (*endptr != '\0' || state->iterations < 1)
     664             :     {
     665           0 :         printfPQExpBuffer(&conn->errorMessage,
     666           0 :                           libpq_gettext("malformed SCRAM message (invalid iteration count)\n"));
     667           0 :         return false;
     668             :     }
     669             : 
     670          36 :     if (*input != '\0')
     671           0 :         printfPQExpBuffer(&conn->errorMessage,
     672           0 :                           libpq_gettext("malformed SCRAM message (garbage at end of server-first-message)\n"));
     673             : 
     674          36 :     return true;
     675             : }
     676             : 
     677             : /*
     678             :  * Read the final exchange message coming from the server.
     679             :  */
     680             : static bool
     681          26 : read_server_final_message(fe_scram_state *state, char *input)
     682             : {
     683          26 :     PGconn     *conn = state->conn;
     684             :     char       *encoded_server_signature;
     685             :     char       *decoded_server_signature;
     686             :     int         server_signature_len;
     687             : 
     688          26 :     state->server_final_message = strdup(input);
     689          26 :     if (!state->server_final_message)
     690             :     {
     691           0 :         printfPQExpBuffer(&conn->errorMessage,
     692           0 :                           libpq_gettext("out of memory\n"));
     693           0 :         return false;
     694             :     }
     695             : 
     696             :     /* Check for error result. */
     697          26 :     if (*input == 'e')
     698             :     {
     699           0 :         char       *errmsg = read_attr_value(&input, 'e',
     700             :                                              &conn->errorMessage);
     701             : 
     702           0 :         printfPQExpBuffer(&conn->errorMessage,
     703           0 :                           libpq_gettext("error received from server in SCRAM exchange: %s\n"),
     704             :                           errmsg);
     705           0 :         return false;
     706             :     }
     707             : 
     708             :     /* Parse the message. */
     709          26 :     encoded_server_signature = read_attr_value(&input, 'v',
     710             :                                                &conn->errorMessage);
     711          26 :     if (encoded_server_signature == NULL)
     712             :     {
     713             :         /* read_attr_value() has generated an error message */
     714           0 :         return false;
     715             :     }
     716             : 
     717          26 :     if (*input != '\0')
     718           0 :         printfPQExpBuffer(&conn->errorMessage,
     719           0 :                           libpq_gettext("malformed SCRAM message (garbage at end of server-final-message)\n"));
     720             : 
     721          26 :     server_signature_len = pg_b64_dec_len(strlen(encoded_server_signature));
     722          26 :     decoded_server_signature = malloc(server_signature_len);
     723          26 :     if (!decoded_server_signature)
     724             :     {
     725           0 :         printfPQExpBuffer(&conn->errorMessage,
     726           0 :                           libpq_gettext("out of memory\n"));
     727           0 :         return false;
     728             :     }
     729             : 
     730          26 :     server_signature_len = pg_b64_decode(encoded_server_signature,
     731          26 :                                          strlen(encoded_server_signature),
     732             :                                          decoded_server_signature,
     733             :                                          server_signature_len);
     734          26 :     if (server_signature_len != SCRAM_KEY_LEN)
     735             :     {
     736           0 :         free(decoded_server_signature);
     737           0 :         printfPQExpBuffer(&conn->errorMessage,
     738           0 :                           libpq_gettext("malformed SCRAM message (invalid server signature)\n"));
     739           0 :         return false;
     740             :     }
     741          26 :     memcpy(state->ServerSignature, decoded_server_signature, SCRAM_KEY_LEN);
     742          26 :     free(decoded_server_signature);
     743             : 
     744          26 :     return true;
     745             : }
     746             : 
     747             : /*
     748             :  * Calculate the client proof, part of the final exchange message sent
     749             :  * by the client.
     750             :  */
     751             : static void
     752          36 : calculate_client_proof(fe_scram_state *state,
     753             :                        const char *client_final_message_without_proof,
     754             :                        uint8 *result)
     755             : {
     756             :     uint8       StoredKey[SCRAM_KEY_LEN];
     757             :     uint8       ClientKey[SCRAM_KEY_LEN];
     758             :     uint8       ClientSignature[SCRAM_KEY_LEN];
     759             :     int         i;
     760             :     scram_HMAC_ctx ctx;
     761             : 
     762             :     /*
     763             :      * Calculate SaltedPassword, and store it in 'state' so that we can reuse
     764             :      * it later in verify_server_signature.
     765             :      */
     766          36 :     scram_SaltedPassword(state->password, state->salt, state->saltlen,
     767          36 :                          state->iterations, state->SaltedPassword);
     768             : 
     769          36 :     scram_ClientKey(state->SaltedPassword, ClientKey);
     770          36 :     scram_H(ClientKey, SCRAM_KEY_LEN, StoredKey);
     771             : 
     772          36 :     scram_HMAC_init(&ctx, StoredKey, SCRAM_KEY_LEN);
     773          72 :     scram_HMAC_update(&ctx,
     774          36 :                       state->client_first_message_bare,
     775          36 :                       strlen(state->client_first_message_bare));
     776          36 :     scram_HMAC_update(&ctx, ",", 1);
     777          72 :     scram_HMAC_update(&ctx,
     778          36 :                       state->server_first_message,
     779          36 :                       strlen(state->server_first_message));
     780          36 :     scram_HMAC_update(&ctx, ",", 1);
     781          36 :     scram_HMAC_update(&ctx,
     782             :                       client_final_message_without_proof,
     783          36 :                       strlen(client_final_message_without_proof));
     784          36 :     scram_HMAC_final(ClientSignature, &ctx);
     785             : 
     786        1188 :     for (i = 0; i < SCRAM_KEY_LEN; i++)
     787        1152 :         result[i] = ClientKey[i] ^ ClientSignature[i];
     788          36 : }
     789             : 
     790             : /*
     791             :  * Validate the server signature, received as part of the final exchange
     792             :  * message received from the server.
     793             :  */
     794             : static bool
     795          26 : verify_server_signature(fe_scram_state *state)
     796             : {
     797             :     uint8       expected_ServerSignature[SCRAM_KEY_LEN];
     798             :     uint8       ServerKey[SCRAM_KEY_LEN];
     799             :     scram_HMAC_ctx ctx;
     800             : 
     801          26 :     scram_ServerKey(state->SaltedPassword, ServerKey);
     802             : 
     803             :     /* calculate ServerSignature */
     804          26 :     scram_HMAC_init(&ctx, ServerKey, SCRAM_KEY_LEN);
     805          52 :     scram_HMAC_update(&ctx,
     806          26 :                       state->client_first_message_bare,
     807          26 :                       strlen(state->client_first_message_bare));
     808          26 :     scram_HMAC_update(&ctx, ",", 1);
     809          52 :     scram_HMAC_update(&ctx,
     810          26 :                       state->server_first_message,
     811          26 :                       strlen(state->server_first_message));
     812          26 :     scram_HMAC_update(&ctx, ",", 1);
     813          52 :     scram_HMAC_update(&ctx,
     814          26 :                       state->client_final_message_without_proof,
     815          26 :                       strlen(state->client_final_message_without_proof));
     816          26 :     scram_HMAC_final(expected_ServerSignature, &ctx);
     817             : 
     818          26 :     if (memcmp(expected_ServerSignature, state->ServerSignature, SCRAM_KEY_LEN) != 0)
     819           0 :         return false;
     820             : 
     821          26 :     return true;
     822             : }
     823             : 
     824             : /*
     825             :  * Build a new SCRAM secret.
     826             :  */
     827             : char *
     828           0 : pg_fe_scram_build_secret(const char *password)
     829             : {
     830             :     char       *prep_password;
     831             :     pg_saslprep_rc rc;
     832             :     char        saltbuf[SCRAM_DEFAULT_SALT_LEN];
     833             :     char       *result;
     834             : 
     835             :     /*
     836             :      * Normalize the password with SASLprep.  If that doesn't work, because
     837             :      * the password isn't valid UTF-8 or contains prohibited characters, just
     838             :      * proceed with the original password.  (See comments at top of file.)
     839             :      */
     840           0 :     rc = pg_saslprep(password, &prep_password);
     841           0 :     if (rc == SASLPREP_OOM)
     842           0 :         return NULL;
     843           0 :     if (rc == SASLPREP_SUCCESS)
     844           0 :         password = (const char *) prep_password;
     845             : 
     846             :     /* Generate a random salt */
     847           0 :     if (!pg_strong_random(saltbuf, SCRAM_DEFAULT_SALT_LEN))
     848             :     {
     849           0 :         if (prep_password)
     850           0 :             free(prep_password);
     851           0 :         return NULL;
     852             :     }
     853             : 
     854           0 :     result = scram_build_secret(saltbuf, SCRAM_DEFAULT_SALT_LEN,
     855             :                                   SCRAM_DEFAULT_ITERATIONS, password);
     856             : 
     857           0 :     if (prep_password)
     858           0 :         free(prep_password);
     859             : 
     860           0 :     return result;
     861             : }

Generated by: LCOV version 1.13