LCOV - differential code coverage report
Current view: top level - src/common - scram-common.c (source / functions) Coverage Total Hit LBC UIC UBC GBC GIC GNC CBC EUB ECB DUB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 66.4 % 116 77 15 17 7 7 25 32 13 21 40 4 9
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 5 5 5 2 2
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           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-2023, 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                 : #include "port/pg_bswap.h"
      26                 : 
      27                 : /*
      28                 :  * Calculate SaltedPassword.
      29                 :  *
      30                 :  * The password should already be normalized by SASLprep.  Returns 0 on
      31                 :  * success, -1 on failure with *errstr pointing to a message about the
      32                 :  * error details.
      33                 :  */
      34                 : int
      35 CBC          92 : scram_SaltedPassword(const char *password,
      36                 :                      pg_cryptohash_type hash_type, int key_length,
      37                 :                      const char *salt, int saltlen, int iterations,
      38                 :                      uint8 *result, const char **errstr)
      39                 : {
      40 GIC          92 :     int         password_len = strlen(password);
      41 CBC          92 :     uint32      one = pg_hton32(1);
      42 ECB             :     int         i,
      43                 :                 j;
      44                 :     uint8       Ui[SCRAM_MAX_KEY_LEN];
      45                 :     uint8       Ui_prev[SCRAM_MAX_KEY_LEN];
      46 GNC          92 :     pg_hmac_ctx *hmac_ctx = pg_hmac_create(hash_type);
      47 ECB             : 
      48 GIC          92 :     if (hmac_ctx == NULL)
      49 ECB             :     {
      50 UIC           0 :         *errstr = pg_hmac_error(NULL);  /* returns OOM */
      51 UBC           0 :         return -1;
      52 EUB             :     }
      53                 : 
      54                 :     /*
      55                 :      * Iterate hash calculation of HMAC entry using given salt.  This is
      56                 :      * essentially PBKDF2 (see RFC2898) with HMAC() as the pseudorandom
      57                 :      * function.
      58                 :      */
      59                 : 
      60                 :     /* First iteration */
      61 GIC         184 :     if (pg_hmac_init(hmac_ctx, (uint8 *) password, password_len) < 0 ||
      62 CBC         184 :         pg_hmac_update(hmac_ctx, (uint8 *) salt, saltlen) < 0 ||
      63             184 :         pg_hmac_update(hmac_ctx, (uint8 *) &one, sizeof(uint32)) < 0 ||
      64 GNC          92 :         pg_hmac_final(hmac_ctx, Ui_prev, key_length) < 0)
      65 ECB             :     {
      66 UIC           0 :         *errstr = pg_hmac_error(hmac_ctx);
      67 UBC           0 :         pg_hmac_free(hmac_ctx);
      68               0 :         return -1;
      69 EUB             :     }
      70                 : 
      71 GNC          92 :     memcpy(result, Ui_prev, key_length);
      72 ECB             : 
      73                 :     /* Subsequent iterations */
      74 GIC      333950 :     for (i = 2; i <= iterations; i++)
      75 ECB             :     {
      76 GIC      667716 :         if (pg_hmac_init(hmac_ctx, (uint8 *) password, password_len) < 0 ||
      77 GNC      667716 :             pg_hmac_update(hmac_ctx, (uint8 *) Ui_prev, key_length) < 0 ||
      78          333858 :             pg_hmac_final(hmac_ctx, Ui, key_length) < 0)
      79 ECB             :         {
      80 UIC           0 :             *errstr = pg_hmac_error(hmac_ctx);
      81 UBC           0 :             pg_hmac_free(hmac_ctx);
      82               0 :             return -1;
      83 EUB             :         }
      84                 : 
      85 GNC    11017314 :         for (j = 0; j < key_length; j++)
      86 CBC    10683456 :             result[j] ^= Ui[j];
      87 GNC      333858 :         memcpy(Ui_prev, Ui, key_length);
      88 ECB             :     }
      89                 : 
      90 GIC          92 :     pg_hmac_free(hmac_ctx);
      91 CBC          92 :     return 0;
      92 ECB             : }
      93                 : 
      94                 : 
      95                 : /*
      96                 :  * Calculate hash for a NULL-terminated string. (The NULL terminator is
      97                 :  * not included in the hash).  Returns 0 on success, -1 on failure with *errstr
      98                 :  * pointing to a message about the error details.
      99                 :  */
     100                 : int
     101 GNC         110 : scram_H(const uint8 *input, pg_cryptohash_type hash_type, int key_length,
     102                 :         uint8 *result, const char **errstr)
     103 ECB             : {
     104                 :     pg_cryptohash_ctx *ctx;
     105                 : 
     106 GNC         110 :     ctx = pg_cryptohash_create(hash_type);
     107 GIC         110 :     if (ctx == NULL)
     108 ECB             :     {
     109 LBC           0 :         *errstr = pg_cryptohash_error(NULL);    /* returns OOM */
     110 UIC           0 :         return -1;
     111 EUB             :     }
     112                 : 
     113 GIC         220 :     if (pg_cryptohash_init(ctx) < 0 ||
     114 GNC         220 :         pg_cryptohash_update(ctx, input, key_length) < 0 ||
     115             110 :         pg_cryptohash_final(ctx, result, key_length) < 0)
     116 ECB             :     {
     117 LBC           0 :         *errstr = pg_cryptohash_error(ctx);
     118 UIC           0 :         pg_cryptohash_free(ctx);
     119 UBC           0 :         return -1;
     120 EUB             :     }
     121                 : 
     122 GIC         110 :     pg_cryptohash_free(ctx);
     123             110 :     return 0;
     124 ECB             : }
     125                 : 
     126                 : /*
     127                 :  * Calculate ClientKey.  Returns 0 on success, -1 on failure with *errstr
     128                 :  * pointing to a message about the error details.
     129                 :  */
     130                 : int
     131 GNC          77 : scram_ClientKey(const uint8 *salted_password,
     132                 :                 pg_cryptohash_type hash_type, int key_length,
     133                 :                 uint8 *result, const char **errstr)
     134 ECB             : {
     135 GNC          77 :     pg_hmac_ctx *ctx = pg_hmac_create(hash_type);
     136                 : 
     137 GIC          77 :     if (ctx == NULL)
     138 ECB             :     {
     139 UIC           0 :         *errstr = pg_hmac_error(NULL);  /* returns OOM */
     140 LBC           0 :         return -1;
     141                 :     }
     142 EUB             : 
     143 GNC         154 :     if (pg_hmac_init(ctx, salted_password, key_length) < 0 ||
     144 GIC         154 :         pg_hmac_update(ctx, (uint8 *) "Client Key", strlen("Client Key")) < 0 ||
     145 GNC          77 :         pg_hmac_final(ctx, result, key_length) < 0)
     146 ECB             :     {
     147 LBC           0 :         *errstr = pg_hmac_error(ctx);
     148               0 :         pg_hmac_free(ctx);
     149 UIC           0 :         return -1;
     150 EUB             :     }
     151                 : 
     152 GBC          77 :     pg_hmac_free(ctx);
     153 GIC          77 :     return 0;
     154                 : }
     155 ECB             : 
     156                 : /*
     157                 :  * Calculate ServerKey.  Returns 0 on success, -1 on failure with *errstr
     158                 :  * pointing to a message about the error details.
     159                 :  */
     160                 : int
     161 GNC          86 : scram_ServerKey(const uint8 *salted_password,
     162                 :                 pg_cryptohash_type hash_type, int key_length,
     163                 :                 uint8 *result, const char **errstr)
     164                 : {
     165              86 :     pg_hmac_ctx *ctx = pg_hmac_create(hash_type);
     166                 : 
     167 GIC          86 :     if (ctx == NULL)
     168                 :     {
     169 LBC           0 :         *errstr = pg_hmac_error(NULL);  /* returns OOM */
     170 UIC           0 :         return -1;
     171 ECB             :     }
     172                 : 
     173 GNC         172 :     if (pg_hmac_init(ctx, salted_password, key_length) < 0 ||
     174 GBC         172 :         pg_hmac_update(ctx, (uint8 *) "Server Key", strlen("Server Key")) < 0 ||
     175 GNC          86 :         pg_hmac_final(ctx, result, key_length) < 0)
     176                 :     {
     177 LBC           0 :         *errstr = pg_hmac_error(ctx);
     178               0 :         pg_hmac_free(ctx);
     179               0 :         return -1;
     180                 :     }
     181 EUB             : 
     182 GBC          86 :     pg_hmac_free(ctx);
     183              86 :     return 0;
     184                 : }
     185                 : 
     186 ECB             : 
     187                 : /*
     188                 :  * Construct a SCRAM secret, for storing in pg_authid.rolpassword.
     189                 :  *
     190                 :  * The password should already have been processed with SASLprep, if necessary!
     191                 :  *
     192                 :  * If iterations is 0, default number of iterations is used.  The result is
     193                 :  * palloc'd or malloc'd, so caller is responsible for freeing it.
     194                 :  *
     195                 :  * On error, returns NULL and sets *errstr to point to a message about the
     196                 :  * error details.
     197                 :  */
     198                 : char *
     199 GNC          43 : scram_build_secret(pg_cryptohash_type hash_type, int key_length,
     200                 :                    const char *salt, int saltlen, int iterations,
     201                 :                    const char *password, const char **errstr)
     202                 : {
     203                 :     uint8       salted_password[SCRAM_MAX_KEY_LEN];
     204                 :     uint8       stored_key[SCRAM_MAX_KEY_LEN];
     205                 :     uint8       server_key[SCRAM_MAX_KEY_LEN];
     206                 :     char       *result;
     207                 :     char       *p;
     208                 :     int         maxlen;
     209                 :     int         encoded_salt_len;
     210                 :     int         encoded_stored_len;
     211                 :     int         encoded_server_len;
     212                 :     int         encoded_result;
     213                 : 
     214                 :     /* Only this hash method is supported currently */
     215              43 :     Assert(hash_type == PG_SHA256);
     216                 : 
     217              43 :     Assert(iterations > 0);
     218                 : 
     219                 :     /* Calculate StoredKey and ServerKey */
     220              43 :     if (scram_SaltedPassword(password, hash_type, key_length,
     221                 :                              salt, saltlen, iterations,
     222 GIC          43 :                              salted_password, errstr) < 0 ||
     223 GNC          43 :         scram_ClientKey(salted_password, hash_type, key_length,
     224              43 :                         stored_key, errstr) < 0 ||
     225              43 :         scram_H(stored_key, hash_type, key_length,
     226              43 :                 stored_key, errstr) < 0 ||
     227              43 :         scram_ServerKey(salted_password, hash_type, key_length,
     228                 :                         server_key, errstr) < 0)
     229                 :     {
     230                 :         /* errstr is filled already here */
     231 ECB             : #ifdef FRONTEND
     232 UIC           0 :         return NULL;
     233 ECB             : #else
     234 LBC           0 :         elog(ERROR, "could not calculate stored key and server key: %s",
     235 ECB             :              *errstr);
     236                 : #endif
     237                 :     }
     238                 : 
     239                 :     /*----------
     240                 :      * The format is:
     241                 :      * SCRAM-SHA-256$<iteration count>:<salt>$<StoredKey>:<ServerKey>
     242                 :      *----------
     243 EUB             :      */
     244 GIC          43 :     encoded_salt_len = pg_b64_enc_len(saltlen);
     245 GNC          43 :     encoded_stored_len = pg_b64_enc_len(key_length);
     246              43 :     encoded_server_len = pg_b64_enc_len(key_length);
     247                 : 
     248 GIC          43 :     maxlen = strlen("SCRAM-SHA-256") + 1
     249                 :         + 10 + 1                /* iteration count */
     250                 :         + encoded_salt_len + 1  /* Base64-encoded salt */
     251              43 :         + encoded_stored_len + 1    /* Base64-encoded StoredKey */
     252              43 :         + encoded_server_len + 1;   /* Base64-encoded ServerKey */
     253                 : 
     254                 : #ifdef FRONTEND
     255 CBC           1 :     result = malloc(maxlen);
     256               1 :     if (!result)
     257 ECB             :     {
     258 UIC           0 :         *errstr = _("out of memory");
     259 LBC           0 :         return NULL;
     260                 :     }
     261                 : #else
     262 CBC          42 :     result = palloc(maxlen);
     263 ECB             : #endif
     264                 : 
     265 GIC          43 :     p = result + sprintf(result, "SCRAM-SHA-256$%d:", iterations);
     266 ECB             : 
     267                 :     /* salt */
     268 GIC          43 :     encoded_result = pg_b64_encode(salt, saltlen, p, encoded_salt_len);
     269 GBC          43 :     if (encoded_result < 0)
     270 EUB             :     {
     271 UIC           0 :         *errstr = _("could not encode salt");
     272                 : #ifdef FRONTEND
     273 LBC           0 :         free(result);
     274 UIC           0 :         return NULL;
     275                 : #else
     276 LBC           0 :         elog(ERROR, "%s", *errstr);
     277                 : #endif
     278                 :     }
     279 CBC          43 :     p += encoded_result;
     280              43 :     *(p++) = '$';
     281                 : 
     282 EUB             :     /* stored key */
     283 GNC          43 :     encoded_result = pg_b64_encode((char *) stored_key, key_length, p,
     284 EUB             :                                    encoded_stored_len);
     285 GBC          43 :     if (encoded_result < 0)
     286                 :     {
     287 UBC           0 :         *errstr = _("could not encode stored key");
     288                 : #ifdef FRONTEND
     289 UIC           0 :         free(result);
     290 LBC           0 :         return NULL;
     291 ECB             : #else
     292 UIC           0 :         elog(ERROR, "%s", *errstr);
     293                 : #endif
     294 ECB             :     }
     295                 : 
     296 CBC          43 :     p += encoded_result;
     297 GIC          43 :     *(p++) = ':';
     298 EUB             : 
     299                 :     /* server key */
     300 GNC          43 :     encoded_result = pg_b64_encode((char *) server_key, key_length, p,
     301 EUB             :                                    encoded_server_len);
     302 GIC          43 :     if (encoded_result < 0)
     303 EUB             :     {
     304 UIC           0 :         *errstr = _("could not encode server key");
     305                 : #ifdef FRONTEND
     306               0 :         free(result);
     307 LBC           0 :         return NULL;
     308 ECB             : #else
     309 UIC           0 :         elog(ERROR, "%s", *errstr);
     310                 : #endif
     311 ECB             :     }
     312                 : 
     313 CBC          43 :     p += encoded_result;
     314 GIC          43 :     *(p++) = '\0';
     315 EUB             : 
     316 GIC          43 :     Assert(p - result <= maxlen);
     317 EUB             : 
     318 GBC          43 :     return result;
     319                 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a