LCOV - differential code coverage report
Current view: top level - src/common - scram-common.c (source / functions) Coverage Total Hit UBC CBC
Current: c70b6db34ffeab48beef1fb4ce61bcad3772b8dd vs 06473f5a344df8c9594ead90a609b86f6724cff8 Lines: 66.7 % 117 78 39 78
Current Date: 2025-09-06 07:49:51 +0900 Functions: 100.0 % 5 5 5
Baseline: lcov-20250906-005545-baseline Branches: 47.4 % 76 36 40 36
Baseline Date: 2025-09-05 08:21:35 +0100 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(30,360] days: 100.0 % 4 4 4
(360..) days: 65.5 % 113 74 39 74
Function coverage date bins:
(360..) days: 100.0 % 5 5 5
Branch coverage date bins:
(30,360] days: 75.0 % 4 3 1 3
(360..) days: 45.8 % 72 33 39 33

 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-2025, 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
 3053 heikki.linnakangas@i       38                 :CBC         136 : scram_SaltedPassword(const char *password,
                                 39                 :                :                      pg_cryptohash_type hash_type, int key_length,
                                 40                 :                :                      const uint8 *salt, int saltlen, int iterations,
                                 41                 :                :                      uint8 *result, const char **errstr)
                                 42                 :                : {
                                 43                 :            136 :     int         password_len = strlen(password);
 2897 andres@anarazel.de         44                 :            136 :     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];
  991 michael@paquier.xyz        49                 :            136 :     pg_hmac_ctx *hmac_ctx = pg_hmac_create(hash_type);
                                 50                 :                : 
 1617                            51         [ -  + ]:            136 :     if (hmac_ctx == NULL)
                                 52                 :                :     {
 1332 michael@paquier.xyz        53                 :UBC           0 :         *errstr = pg_hmac_error(NULL);  /* returns OOM */
 1617                            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 */
 1617 michael@paquier.xyz        64   [ +  -  +  - ]:CBC         272 :     if (pg_hmac_init(hmac_ctx, (uint8 *) password, password_len) < 0 ||
  121 heikki.linnakangas@i       65         [ +  - ]:            272 :         pg_hmac_update(hmac_ctx, salt, saltlen) < 0 ||
 1617 michael@paquier.xyz        66         [ -  + ]:            272 :         pg_hmac_update(hmac_ctx, (uint8 *) &one, sizeof(uint32)) < 0 ||
  991                            67                 :            136 :         pg_hmac_final(hmac_ctx, Ui_prev, key_length) < 0)
                                 68                 :                :     {
 1332 michael@paquier.xyz        69                 :UBC           0 :         *errstr = pg_hmac_error(hmac_ctx);
 1617                            70                 :              0 :         pg_hmac_free(hmac_ctx);
 1739                            71                 :              0 :         return -1;
                                 72                 :                :     }
                                 73                 :                : 
  991 michael@paquier.xyz        74                 :CBC         136 :     memcpy(result, Ui_prev, key_length);
                                 75                 :                : 
                                 76                 :                :     /* Subsequent iterations */
  164 rguo@postgresql.org        77         [ +  + ]:         514174 :     for (i = 1; 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                 :                :          */
  648 michael@paquier.xyz        84         [ -  + ]:         284636 :         CHECK_FOR_INTERRUPTS();
                                 85                 :                : #endif
                                 86                 :                : 
 1617                            87   [ +  -  +  - ]:        1028076 :         if (pg_hmac_init(hmac_ctx, (uint8 *) password, password_len) < 0 ||
  991                            88         [ -  + ]:        1028076 :             pg_hmac_update(hmac_ctx, (uint8 *) Ui_prev, key_length) < 0 ||
                                 89                 :         514038 :             pg_hmac_final(hmac_ctx, Ui, key_length) < 0)
                                 90                 :                :         {
 1332 michael@paquier.xyz        91                 :UBC           0 :             *errstr = pg_hmac_error(hmac_ctx);
 1617                            92                 :              0 :             pg_hmac_free(hmac_ctx);
 1739                            93                 :              0 :             return -1;
                                 94                 :                :         }
                                 95                 :                : 
  991 michael@paquier.xyz        96         [ +  + ]:CBC    16963254 :         for (j = 0; j < key_length; j++)
 3105 heikki.linnakangas@i       97                 :       16449216 :             result[j] ^= Ui[j];
  991 michael@paquier.xyz        98                 :         514038 :         memcpy(Ui_prev, Ui, key_length);
                                 99                 :                :     }
                                100                 :                : 
 1617                           101                 :            136 :     pg_hmac_free(hmac_ctx);
 1739                           102                 :            136 :     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
  991                           112                 :            177 : 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                 :            177 :     ctx = pg_cryptohash_create(hash_type);
 1739                           118         [ -  + ]:            177 :     if (ctx == NULL)
                                119                 :                :     {
 1332 michael@paquier.xyz       120                 :UBC           0 :         *errstr = pg_cryptohash_error(NULL);    /* returns OOM */
 1739                           121                 :              0 :         return -1;
                                122                 :                :     }
                                123                 :                : 
 1739 michael@paquier.xyz       124   [ +  -  +  - ]:CBC         354 :     if (pg_cryptohash_init(ctx) < 0 ||
  991                           125         [ -  + ]:            354 :         pg_cryptohash_update(ctx, input, key_length) < 0 ||
                                126                 :            177 :         pg_cryptohash_final(ctx, result, key_length) < 0)
                                127                 :                :     {
 1332 michael@paquier.xyz       128                 :UBC           0 :         *errstr = pg_cryptohash_error(ctx);
 1739                           129                 :              0 :         pg_cryptohash_free(ctx);
                                130                 :              0 :         return -1;
                                131                 :                :     }
                                132                 :                : 
 1739 michael@paquier.xyz       133                 :CBC         177 :     pg_cryptohash_free(ctx);
                                134                 :            177 :     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
  991                           142                 :            109 : scram_ClientKey(const uint8 *salted_password,
                                143                 :                :                 pg_cryptohash_type hash_type, int key_length,
                                144                 :                :                 uint8 *result, const char **errstr)
                                145                 :                : {
                                146                 :            109 :     pg_hmac_ctx *ctx = pg_hmac_create(hash_type);
                                147                 :                : 
 1617                           148         [ -  + ]:            109 :     if (ctx == NULL)
                                149                 :                :     {
 1332 michael@paquier.xyz       150                 :UBC           0 :         *errstr = pg_hmac_error(NULL);  /* returns OOM */
 1617                           151                 :              0 :         return -1;
                                152                 :                :     }
                                153                 :                : 
  991 michael@paquier.xyz       154   [ +  -  +  - ]:CBC         218 :     if (pg_hmac_init(ctx, salted_password, key_length) < 0 ||
 1617                           155         [ -  + ]:            218 :         pg_hmac_update(ctx, (uint8 *) "Client Key", strlen("Client Key")) < 0 ||
  991                           156                 :            109 :         pg_hmac_final(ctx, result, key_length) < 0)
                                157                 :                :     {
 1332 michael@paquier.xyz       158                 :UBC           0 :         *errstr = pg_hmac_error(ctx);
 1617                           159                 :              0 :         pg_hmac_free(ctx);
 1739                           160                 :              0 :         return -1;
                                161                 :                :     }
                                162                 :                : 
 1617 michael@paquier.xyz       163                 :CBC         109 :     pg_hmac_free(ctx);
 1739                           164                 :            109 :     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
  991                           172                 :            130 : scram_ServerKey(const uint8 *salted_password,
                                173                 :                :                 pg_cryptohash_type hash_type, int key_length,
                                174                 :                :                 uint8 *result, const char **errstr)
                                175                 :                : {
                                176                 :            130 :     pg_hmac_ctx *ctx = pg_hmac_create(hash_type);
                                177                 :                : 
 1617                           178         [ -  + ]:            130 :     if (ctx == NULL)
                                179                 :                :     {
 1332 michael@paquier.xyz       180                 :UBC           0 :         *errstr = pg_hmac_error(NULL);  /* returns OOM */
 1617                           181                 :              0 :         return -1;
                                182                 :                :     }
                                183                 :                : 
  991 michael@paquier.xyz       184   [ +  -  +  - ]:CBC         260 :     if (pg_hmac_init(ctx, salted_password, key_length) < 0 ||
 1617                           185         [ -  + ]:            260 :         pg_hmac_update(ctx, (uint8 *) "Server Key", strlen("Server Key")) < 0 ||
  991                           186                 :            130 :         pg_hmac_final(ctx, result, key_length) < 0)
                                187                 :                :     {
 1332 michael@paquier.xyz       188                 :UBC           0 :         *errstr = pg_hmac_error(ctx);
 1617                           189                 :              0 :         pg_hmac_free(ctx);
 1739                           190                 :              0 :         return -1;
                                191                 :                :     }
                                192                 :                : 
 1617 michael@paquier.xyz       193                 :CBC         130 :     pg_hmac_free(ctx);
 1739                           194                 :            130 :     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                 :                :  * The result is palloc'd or malloc'd, so caller is responsible for freeing it.
                                204                 :                :  *
                                205                 :                :  * On error, returns NULL and sets *errstr to point to a message about the
                                206                 :                :  * error details.
                                207                 :                :  */
                                208                 :                : char *
  991                           209                 :             52 : scram_build_secret(pg_cryptohash_type hash_type, int key_length,
                                210                 :                :                    const uint8 *salt, int saltlen, int iterations,
                                211                 :                :                    const char *password, const char **errstr)
                                212                 :                : {
                                213                 :                :     uint8       salted_password[SCRAM_MAX_KEY_LEN];
                                214                 :                :     uint8       stored_key[SCRAM_MAX_KEY_LEN];
                                215                 :                :     uint8       server_key[SCRAM_MAX_KEY_LEN];
                                216                 :                :     char       *result;
                                217                 :                :     char       *p;
                                218                 :                :     int         maxlen;
                                219                 :                :     int         encoded_salt_len;
                                220                 :                :     int         encoded_stored_len;
                                221                 :                :     int         encoded_server_len;
                                222                 :                :     int         encoded_result;
                                223                 :                : 
                                224                 :                :     /* Only this hash method is supported currently */
                                225         [ -  + ]:             52 :     Assert(hash_type == PG_SHA256);
                                226                 :                : 
  894 dgustafsson@postgres      227         [ -  + ]:             52 :     Assert(iterations > 0);
                                228                 :                : 
                                229                 :                :     /* Calculate StoredKey and ServerKey */
  991 michael@paquier.xyz       230         [ +  - ]:             52 :     if (scram_SaltedPassword(password, hash_type, key_length,
                                231                 :                :                              salt, saltlen, iterations,
 1332                           232         [ +  - ]:             52 :                              salted_password, errstr) < 0 ||
  991                           233                 :             52 :         scram_ClientKey(salted_password, hash_type, key_length,
                                234         [ +  - ]:             52 :                         stored_key, errstr) < 0 ||
                                235                 :             52 :         scram_H(stored_key, hash_type, key_length,
                                236         [ -  + ]:             52 :                 stored_key, errstr) < 0 ||
                                237                 :             52 :         scram_ServerKey(salted_password, hash_type, key_length,
                                238                 :                :                         server_key, errstr) < 0)
                                239                 :                :     {
                                240                 :                :         /* errstr is filled already here */
                                241                 :                : #ifdef FRONTEND
 1739 michael@paquier.xyz       242                 :UBC           0 :         return NULL;
                                243                 :                : #else
 1332                           244         [ #  # ]:              0 :         elog(ERROR, "could not calculate stored key and server key: %s",
                                245                 :                :              *errstr);
                                246                 :                : #endif
                                247                 :                :     }
                                248                 :                : 
                                249                 :                :     /*----------
                                250                 :                :      * The format is:
                                251                 :                :      * SCRAM-SHA-256$<iteration count>:<salt>$<StoredKey>:<ServerKey>
                                252                 :                :      *----------
                                253                 :                :      */
 2256 michael@paquier.xyz       254                 :CBC          52 :     encoded_salt_len = pg_b64_enc_len(saltlen);
  991                           255                 :             52 :     encoded_stored_len = pg_b64_enc_len(key_length);
                                256                 :             52 :     encoded_server_len = pg_b64_enc_len(key_length);
                                257                 :                : 
 3048 heikki.linnakangas@i      258                 :             52 :     maxlen = strlen("SCRAM-SHA-256") + 1
                                259                 :                :         + 10 + 1                /* iteration count */
                                260                 :                :         + encoded_salt_len + 1  /* Base64-encoded salt */
 2256 michael@paquier.xyz       261                 :             52 :         + encoded_stored_len + 1    /* Base64-encoded StoredKey */
                                262                 :             52 :         + encoded_server_len + 1;   /* Base64-encoded ServerKey */
                                263                 :                : 
                                264                 :                : #ifdef FRONTEND
 3048 heikki.linnakangas@i      265                 :              1 :     result = malloc(maxlen);
                                266         [ -  + ]:              1 :     if (!result)
                                267                 :                :     {
 1332 michael@paquier.xyz       268                 :UBC           0 :         *errstr = _("out of memory");
 3048 heikki.linnakangas@i      269                 :              0 :         return NULL;
                                270                 :                :     }
                                271                 :                : #else
 3048 heikki.linnakangas@i      272                 :CBC          51 :     result = palloc(maxlen);
                                273                 :                : #endif
                                274                 :                : 
                                275                 :             52 :     p = result + sprintf(result, "SCRAM-SHA-256$%d:", iterations);
                                276                 :                : 
                                277                 :                :     /* salt */
 2256 michael@paquier.xyz       278                 :             52 :     encoded_result = pg_b64_encode(salt, saltlen, p, encoded_salt_len);
                                279         [ -  + ]:             52 :     if (encoded_result < 0)
                                280                 :                :     {
 1332 michael@paquier.xyz       281                 :UBC           0 :         *errstr = _("could not encode salt");
                                282                 :                : #ifdef FRONTEND
 2256                           283                 :              0 :         free(result);
                                284                 :              0 :         return NULL;
                                285                 :                : #else
 1332                           286         [ #  # ]:              0 :         elog(ERROR, "%s", *errstr);
                                287                 :                : #endif
                                288                 :                :     }
 2256 michael@paquier.xyz       289                 :CBC          52 :     p += encoded_result;
 3048 heikki.linnakangas@i      290                 :             52 :     *(p++) = '$';
                                291                 :                : 
                                292                 :                :     /* stored key */
  121                           293                 :             52 :     encoded_result = pg_b64_encode(stored_key, key_length, p,
                                294                 :                :                                    encoded_stored_len);
 2256 michael@paquier.xyz       295         [ -  + ]:             52 :     if (encoded_result < 0)
                                296                 :                :     {
 1332 michael@paquier.xyz       297                 :UBC           0 :         *errstr = _("could not encode stored key");
                                298                 :                : #ifdef FRONTEND
 2256                           299                 :              0 :         free(result);
                                300                 :              0 :         return NULL;
                                301                 :                : #else
 1332                           302         [ #  # ]:              0 :         elog(ERROR, "%s", *errstr);
                                303                 :                : #endif
                                304                 :                :     }
                                305                 :                : 
 2256 michael@paquier.xyz       306                 :CBC          52 :     p += encoded_result;
 3048 heikki.linnakangas@i      307                 :             52 :     *(p++) = ':';
                                308                 :                : 
                                309                 :                :     /* server key */
  121                           310                 :             52 :     encoded_result = pg_b64_encode(server_key, key_length, p,
                                311                 :                :                                    encoded_server_len);
 2256 michael@paquier.xyz       312         [ -  + ]:             52 :     if (encoded_result < 0)
                                313                 :                :     {
 1332 michael@paquier.xyz       314                 :UBC           0 :         *errstr = _("could not encode server key");
                                315                 :                : #ifdef FRONTEND
 2256                           316                 :              0 :         free(result);
                                317                 :              0 :         return NULL;
                                318                 :                : #else
 1332                           319         [ #  # ]:              0 :         elog(ERROR, "%s", *errstr);
                                320                 :                : #endif
                                321                 :                :     }
                                322                 :                : 
 2256 michael@paquier.xyz       323                 :CBC          52 :     p += encoded_result;
 3048 heikki.linnakangas@i      324                 :             52 :     *(p++) = '\0';
                                325                 :                : 
                                326         [ -  + ]:             52 :     Assert(p - result <= maxlen);
                                327                 :                : 
                                328                 :             52 :     return result;
                                329                 :                : }
        

Generated by: LCOV version 2.4-beta