LCOV - differential code coverage report
Current view: top level - src/common - scram-common.c (source / functions) Coverage Total Hit UBC CBC
Current: Differential Code Coverage 16@8cea358b128 vs 17@8cea358b128 Lines: 66.7 % 117 78 39 78
Current Date: 2024-04-14 14:21:10 Functions: 100.0 % 5 5 5
Baseline: 16@8cea358b128 Branches: 47.4 % 76 36 40 36
Baseline Date: 2024-04-14 14:21:09 Line coverage date bins:
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed (120,180] days: 100.0 % 1 1 1
(240..) days: 66.4 % 116 77 39 77
Function coverage date bins:
(240..) days: 100.0 % 5 5 5
Branch coverage date bins:
(120,180] days: 50.0 % 2 1 1 1
(240..) days: 47.3 % 74 35 39 35

 Age         Owner                    Branch data    TLA  Line data    Source code
                                  1                 :                : /*-------------------------------------------------------------------------
                                  2                 :                :  * scram-common.c
                                  3                 :                :  *      Shared frontend/backend code for SCRAM authentication
                                  4                 :                :  *
                                  5                 :                :  * This contains the common low-level functions needed in both frontend and
                                  6                 :                :  * backend, for implement the Salted Challenge Response Authentication
                                  7                 :                :  * Mechanism (SCRAM), per IETF's RFC 5802.
                                  8                 :                :  *
                                  9                 :                :  * Portions Copyright (c) 2017-2024, PostgreSQL Global Development Group
                                 10                 :                :  *
                                 11                 :                :  * IDENTIFICATION
                                 12                 :                :  *    src/common/scram-common.c
                                 13                 :                :  *
                                 14                 :                :  *-------------------------------------------------------------------------
                                 15                 :                :  */
                                 16                 :                : #ifndef FRONTEND
                                 17                 :                : #include "postgres.h"
                                 18                 :                : #else
                                 19                 :                : #include "postgres_fe.h"
                                 20                 :                : #endif
                                 21                 :                : 
                                 22                 :                : #include "common/base64.h"
                                 23                 :                : #include "common/hmac.h"
                                 24                 :                : #include "common/scram-common.h"
                                 25                 :                : #ifndef FRONTEND
                                 26                 :                : #include "miscadmin.h"
                                 27                 :                : #endif
                                 28                 :                : #include "port/pg_bswap.h"
                                 29                 :                : 
                                 30                 :                : /*
                                 31                 :                :  * Calculate SaltedPassword.
                                 32                 :                :  *
                                 33                 :                :  * The password should already be normalized by SASLprep.  Returns 0 on
                                 34                 :                :  * success, -1 on failure with *errstr pointing to a message about the
                                 35                 :                :  * error details.
                                 36                 :                :  */
                                 37                 :                : int
 2543 heikki.linnakangas@i       38                 :CBC         109 : scram_SaltedPassword(const char *password,
                                 39                 :                :                      pg_cryptohash_type hash_type, int key_length,
                                 40                 :                :                      const char *salt, int saltlen, int iterations,
                                 41                 :                :                      uint8 *result, const char **errstr)
                                 42                 :                : {
                                 43                 :            109 :     int         password_len = strlen(password);
 2387 andres@anarazel.de         44                 :            109 :     uint32      one = pg_hton32(1);
                                 45                 :                :     int         i,
                                 46                 :                :                 j;
                                 47                 :                :     uint8       Ui[SCRAM_MAX_KEY_LEN];
                                 48                 :                :     uint8       Ui_prev[SCRAM_MAX_KEY_LEN];
  481 michael@paquier.xyz        49                 :            109 :     pg_hmac_ctx *hmac_ctx = pg_hmac_create(hash_type);
                                 50                 :                : 
 1107                            51         [ -  + ]:            109 :     if (hmac_ctx == NULL)
                                 52                 :                :     {
  822 michael@paquier.xyz        53                 :UBC           0 :         *errstr = pg_hmac_error(NULL);  /* returns OOM */
 1107                            54                 :              0 :         return -1;
                                 55                 :                :     }
                                 56                 :                : 
                                 57                 :                :     /*
                                 58                 :                :      * Iterate hash calculation of HMAC entry using given salt.  This is
                                 59                 :                :      * essentially PBKDF2 (see RFC2898) with HMAC() as the pseudorandom
                                 60                 :                :      * function.
                                 61                 :                :      */
                                 62                 :                : 
                                 63                 :                :     /* First iteration */
 1107 michael@paquier.xyz        64   [ +  -  +  - ]:CBC         218 :     if (pg_hmac_init(hmac_ctx, (uint8 *) password, password_len) < 0 ||
                                 65         [ +  - ]:            218 :         pg_hmac_update(hmac_ctx, (uint8 *) salt, saltlen) < 0 ||
                                 66         [ -  + ]:            218 :         pg_hmac_update(hmac_ctx, (uint8 *) &one, sizeof(uint32)) < 0 ||
  481                            67                 :            109 :         pg_hmac_final(hmac_ctx, Ui_prev, key_length) < 0)
                                 68                 :                :     {
  822 michael@paquier.xyz        69                 :UBC           0 :         *errstr = pg_hmac_error(hmac_ctx);
 1107                            70                 :              0 :         pg_hmac_free(hmac_ctx);
 1229                            71                 :              0 :         return -1;
                                 72                 :                :     }
                                 73                 :                : 
  481 michael@paquier.xyz        74                 :CBC         109 :     memcpy(result, Ui_prev, key_length);
                                 75                 :                : 
                                 76                 :                :     /* Subsequent iterations */
 2595 heikki.linnakangas@i       77         [ +  + ]:         403582 :     for (i = 2; i <= iterations; i++)
                                 78                 :                :     {
                                 79                 :                : #ifndef FRONTEND
                                 80                 :                :         /*
                                 81                 :                :          * Make sure that this is interruptible as scram_iterations could be
                                 82                 :                :          * set to a large value.
                                 83                 :                :          */
  138 michael@paquier.xyz        84         [ -  + ]:         231401 :         CHECK_FOR_INTERRUPTS();
                                 85                 :                : #endif
                                 86                 :                : 
 1107                            87   [ +  -  +  - ]:         806946 :         if (pg_hmac_init(hmac_ctx, (uint8 *) password, password_len) < 0 ||
  481                            88         [ -  + ]:         806946 :             pg_hmac_update(hmac_ctx, (uint8 *) Ui_prev, key_length) < 0 ||
                                 89                 :         403473 :             pg_hmac_final(hmac_ctx, Ui, key_length) < 0)
                                 90                 :                :         {
  822 michael@paquier.xyz        91                 :UBC           0 :             *errstr = pg_hmac_error(hmac_ctx);
 1107                            92                 :              0 :             pg_hmac_free(hmac_ctx);
 1229                            93                 :              0 :             return -1;
                                 94                 :                :         }
                                 95                 :                : 
  481 michael@paquier.xyz        96         [ +  + ]:CBC    13314609 :         for (j = 0; j < key_length; j++)
 2595 heikki.linnakangas@i       97                 :       12911136 :             result[j] ^= Ui[j];
  481 michael@paquier.xyz        98                 :         403473 :         memcpy(Ui_prev, Ui, key_length);
                                 99                 :                :     }
                                100                 :                : 
 1107                           101                 :            109 :     pg_hmac_free(hmac_ctx);
 1229                           102                 :            109 :     return 0;
                                103                 :                : }
                                104                 :                : 
                                105                 :                : 
                                106                 :                : /*
                                107                 :                :  * Calculate hash for a NULL-terminated string. (The NULL terminator is
                                108                 :                :  * not included in the hash).  Returns 0 on success, -1 on failure with *errstr
                                109                 :                :  * pointing to a message about the error details.
                                110                 :                :  */
                                111                 :                : int
  481                           112                 :            133 : scram_H(const uint8 *input, pg_cryptohash_type hash_type, int key_length,
                                113                 :                :         uint8 *result, const char **errstr)
                                114                 :                : {
                                115                 :                :     pg_cryptohash_ctx *ctx;
                                116                 :                : 
                                117                 :            133 :     ctx = pg_cryptohash_create(hash_type);
 1229                           118         [ -  + ]:            133 :     if (ctx == NULL)
                                119                 :                :     {
  822 michael@paquier.xyz       120                 :UBC           0 :         *errstr = pg_cryptohash_error(NULL);    /* returns OOM */
 1229                           121                 :              0 :         return -1;
                                122                 :                :     }
                                123                 :                : 
 1229 michael@paquier.xyz       124   [ +  -  +  - ]:CBC         266 :     if (pg_cryptohash_init(ctx) < 0 ||
  481                           125         [ -  + ]:            266 :         pg_cryptohash_update(ctx, input, key_length) < 0 ||
                                126                 :            133 :         pg_cryptohash_final(ctx, result, key_length) < 0)
                                127                 :                :     {
  822 michael@paquier.xyz       128                 :UBC           0 :         *errstr = pg_cryptohash_error(ctx);
 1229                           129                 :              0 :         pg_cryptohash_free(ctx);
                                130                 :              0 :         return -1;
                                131                 :                :     }
                                132                 :                : 
 1229 michael@paquier.xyz       133                 :CBC         133 :     pg_cryptohash_free(ctx);
                                134                 :            133 :     return 0;
                                135                 :                : }
                                136                 :                : 
                                137                 :                : /*
                                138                 :                :  * Calculate ClientKey.  Returns 0 on success, -1 on failure with *errstr
                                139                 :                :  * pointing to a message about the error details.
                                140                 :                :  */
                                141                 :                : int
  481                           142                 :             90 : scram_ClientKey(const uint8 *salted_password,
                                143                 :                :                 pg_cryptohash_type hash_type, int key_length,
                                144                 :                :                 uint8 *result, const char **errstr)
                                145                 :                : {
                                146                 :             90 :     pg_hmac_ctx *ctx = pg_hmac_create(hash_type);
                                147                 :                : 
 1107                           148         [ -  + ]:             90 :     if (ctx == NULL)
                                149                 :                :     {
  822 michael@paquier.xyz       150                 :UBC           0 :         *errstr = pg_hmac_error(NULL);  /* returns OOM */
 1107                           151                 :              0 :         return -1;
                                152                 :                :     }
                                153                 :                : 
  481 michael@paquier.xyz       154   [ +  -  +  - ]:CBC         180 :     if (pg_hmac_init(ctx, salted_password, key_length) < 0 ||
 1107                           155         [ -  + ]:            180 :         pg_hmac_update(ctx, (uint8 *) "Client Key", strlen("Client Key")) < 0 ||
  481                           156                 :             90 :         pg_hmac_final(ctx, result, key_length) < 0)
                                157                 :                :     {
  822 michael@paquier.xyz       158                 :UBC           0 :         *errstr = pg_hmac_error(ctx);
 1107                           159                 :              0 :         pg_hmac_free(ctx);
 1229                           160                 :              0 :         return -1;
                                161                 :                :     }
                                162                 :                : 
 1107 michael@paquier.xyz       163                 :CBC          90 :     pg_hmac_free(ctx);
 1229                           164                 :             90 :     return 0;
                                165                 :                : }
                                166                 :                : 
                                167                 :                : /*
                                168                 :                :  * Calculate ServerKey.  Returns 0 on success, -1 on failure with *errstr
                                169                 :                :  * pointing to a message about the error details.
                                170                 :                :  */
                                171                 :                : int
  481                           172                 :            103 : scram_ServerKey(const uint8 *salted_password,
                                173                 :                :                 pg_cryptohash_type hash_type, int key_length,
                                174                 :                :                 uint8 *result, const char **errstr)
                                175                 :                : {
                                176                 :            103 :     pg_hmac_ctx *ctx = pg_hmac_create(hash_type);
                                177                 :                : 
 1107                           178         [ -  + ]:            103 :     if (ctx == NULL)
                                179                 :                :     {
  822 michael@paquier.xyz       180                 :UBC           0 :         *errstr = pg_hmac_error(NULL);  /* returns OOM */
 1107                           181                 :              0 :         return -1;
                                182                 :                :     }
                                183                 :                : 
  481 michael@paquier.xyz       184   [ +  -  +  - ]:CBC         206 :     if (pg_hmac_init(ctx, salted_password, key_length) < 0 ||
 1107                           185         [ -  + ]:            206 :         pg_hmac_update(ctx, (uint8 *) "Server Key", strlen("Server Key")) < 0 ||
  481                           186                 :            103 :         pg_hmac_final(ctx, result, key_length) < 0)
                                187                 :                :     {
  822 michael@paquier.xyz       188                 :UBC           0 :         *errstr = pg_hmac_error(ctx);
 1107                           189                 :              0 :         pg_hmac_free(ctx);
 1229                           190                 :              0 :         return -1;
                                191                 :                :     }
                                192                 :                : 
 1107 michael@paquier.xyz       193                 :CBC         103 :     pg_hmac_free(ctx);
 1229                           194                 :            103 :     return 0;
                                195                 :                : }
                                196                 :                : 
                                197                 :                : 
                                198                 :                : /*
                                199                 :                :  * Construct a SCRAM secret, for storing in pg_authid.rolpassword.
                                200                 :                :  *
                                201                 :                :  * The password should already have been processed with SASLprep, if necessary!
                                202                 :                :  *
                                203                 :                :  * If iterations is 0, default number of iterations is used.  The result is
                                204                 :                :  * palloc'd or malloc'd, so caller is responsible for freeing it.
                                205                 :                :  *
                                206                 :                :  * On error, returns NULL and sets *errstr to point to a message about the
                                207                 :                :  * error details.
                                208                 :                :  */
                                209                 :                : char *
  481                           210                 :             47 : scram_build_secret(pg_cryptohash_type hash_type, int key_length,
                                211                 :                :                    const char *salt, int saltlen, int iterations,
                                212                 :                :                    const char *password, const char **errstr)
                                213                 :                : {
                                214                 :                :     uint8       salted_password[SCRAM_MAX_KEY_LEN];
                                215                 :                :     uint8       stored_key[SCRAM_MAX_KEY_LEN];
                                216                 :                :     uint8       server_key[SCRAM_MAX_KEY_LEN];
                                217                 :                :     char       *result;
                                218                 :                :     char       *p;
                                219                 :                :     int         maxlen;
                                220                 :                :     int         encoded_salt_len;
                                221                 :                :     int         encoded_stored_len;
                                222                 :                :     int         encoded_server_len;
                                223                 :                :     int         encoded_result;
                                224                 :                : 
                                225                 :                :     /* Only this hash method is supported currently */
                                226         [ -  + ]:             47 :     Assert(hash_type == PG_SHA256);
                                227                 :                : 
  384 dgustafsson@postgres      228         [ -  + ]:             47 :     Assert(iterations > 0);
                                229                 :                : 
                                230                 :                :     /* Calculate StoredKey and ServerKey */
  481 michael@paquier.xyz       231         [ +  - ]:             47 :     if (scram_SaltedPassword(password, hash_type, key_length,
                                232                 :                :                              salt, saltlen, iterations,
  822                           233         [ +  - ]:             47 :                              salted_password, errstr) < 0 ||
  481                           234                 :             47 :         scram_ClientKey(salted_password, hash_type, key_length,
                                235         [ +  - ]:             47 :                         stored_key, errstr) < 0 ||
                                236                 :             47 :         scram_H(stored_key, hash_type, key_length,
                                237         [ -  + ]:             47 :                 stored_key, errstr) < 0 ||
                                238                 :             47 :         scram_ServerKey(salted_password, hash_type, key_length,
                                239                 :                :                         server_key, errstr) < 0)
                                240                 :                :     {
                                241                 :                :         /* errstr is filled already here */
                                242                 :                : #ifdef FRONTEND
 1229 michael@paquier.xyz       243                 :UBC           0 :         return NULL;
                                244                 :                : #else
  822                           245         [ #  # ]:              0 :         elog(ERROR, "could not calculate stored key and server key: %s",
                                246                 :                :              *errstr);
                                247                 :                : #endif
                                248                 :                :     }
                                249                 :                : 
                                250                 :                :     /*----------
                                251                 :                :      * The format is:
                                252                 :                :      * SCRAM-SHA-256$<iteration count>:<salt>$<StoredKey>:<ServerKey>
                                253                 :                :      *----------
                                254                 :                :      */
 1746 michael@paquier.xyz       255                 :CBC          47 :     encoded_salt_len = pg_b64_enc_len(saltlen);
  481                           256                 :             47 :     encoded_stored_len = pg_b64_enc_len(key_length);
                                257                 :             47 :     encoded_server_len = pg_b64_enc_len(key_length);
                                258                 :                : 
 2538 heikki.linnakangas@i      259                 :             47 :     maxlen = strlen("SCRAM-SHA-256") + 1
                                260                 :                :         + 10 + 1                /* iteration count */
                                261                 :                :         + encoded_salt_len + 1  /* Base64-encoded salt */
 1746 michael@paquier.xyz       262                 :             47 :         + encoded_stored_len + 1    /* Base64-encoded StoredKey */
                                263                 :             47 :         + encoded_server_len + 1;   /* Base64-encoded ServerKey */
                                264                 :                : 
                                265                 :                : #ifdef FRONTEND
 2538 heikki.linnakangas@i      266                 :              1 :     result = malloc(maxlen);
                                267         [ -  + ]:              1 :     if (!result)
                                268                 :                :     {
  822 michael@paquier.xyz       269                 :UBC           0 :         *errstr = _("out of memory");
 2538 heikki.linnakangas@i      270                 :              0 :         return NULL;
                                271                 :                :     }
                                272                 :                : #else
 2538 heikki.linnakangas@i      273                 :CBC          46 :     result = palloc(maxlen);
                                274                 :                : #endif
                                275                 :                : 
                                276                 :             47 :     p = result + sprintf(result, "SCRAM-SHA-256$%d:", iterations);
                                277                 :                : 
                                278                 :                :     /* salt */
 1746 michael@paquier.xyz       279                 :             47 :     encoded_result = pg_b64_encode(salt, saltlen, p, encoded_salt_len);
                                280         [ -  + ]:             47 :     if (encoded_result < 0)
                                281                 :                :     {
  822 michael@paquier.xyz       282                 :UBC           0 :         *errstr = _("could not encode salt");
                                283                 :                : #ifdef FRONTEND
 1746                           284                 :              0 :         free(result);
                                285                 :              0 :         return NULL;
                                286                 :                : #else
  822                           287         [ #  # ]:              0 :         elog(ERROR, "%s", *errstr);
                                288                 :                : #endif
                                289                 :                :     }
 1746 michael@paquier.xyz       290                 :CBC          47 :     p += encoded_result;
 2538 heikki.linnakangas@i      291                 :             47 :     *(p++) = '$';
                                292                 :                : 
                                293                 :                :     /* stored key */
  481 michael@paquier.xyz       294                 :             47 :     encoded_result = pg_b64_encode((char *) stored_key, key_length, p,
                                295                 :                :                                    encoded_stored_len);
 1746                           296         [ -  + ]:             47 :     if (encoded_result < 0)
                                297                 :                :     {
  822 michael@paquier.xyz       298                 :UBC           0 :         *errstr = _("could not encode stored key");
                                299                 :                : #ifdef FRONTEND
 1746                           300                 :              0 :         free(result);
                                301                 :              0 :         return NULL;
                                302                 :                : #else
  822                           303         [ #  # ]:              0 :         elog(ERROR, "%s", *errstr);
                                304                 :                : #endif
                                305                 :                :     }
                                306                 :                : 
 1746 michael@paquier.xyz       307                 :CBC          47 :     p += encoded_result;
 2538 heikki.linnakangas@i      308                 :             47 :     *(p++) = ':';
                                309                 :                : 
                                310                 :                :     /* server key */
  481 michael@paquier.xyz       311                 :             47 :     encoded_result = pg_b64_encode((char *) server_key, key_length, p,
                                312                 :                :                                    encoded_server_len);
 1746                           313         [ -  + ]:             47 :     if (encoded_result < 0)
                                314                 :                :     {
  822 michael@paquier.xyz       315                 :UBC           0 :         *errstr = _("could not encode server key");
                                316                 :                : #ifdef FRONTEND
 1746                           317                 :              0 :         free(result);
                                318                 :              0 :         return NULL;
                                319                 :                : #else
  822                           320         [ #  # ]:              0 :         elog(ERROR, "%s", *errstr);
                                321                 :                : #endif
                                322                 :                :     }
                                323                 :                : 
 1746 michael@paquier.xyz       324                 :CBC          47 :     p += encoded_result;
 2538 heikki.linnakangas@i      325                 :             47 :     *(p++) = '\0';
                                326                 :                : 
                                327         [ -  + ]:             47 :     Assert(p - result <= maxlen);
                                328                 :                : 
                                329                 :             47 :     return result;
                                330                 :                : }
        

Generated by: LCOV version 2.1-beta2-3-g6141622