Age Owner 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
2172 heikki.linnakangas 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 : {
2172 heikki.linnakangas 40 GIC 92 : int password_len = strlen(password);
2016 andres 41 CBC 92 : uint32 one = pg_hton32(1);
2224 heikki.linnakangas 42 ECB : int i,
43 : j;
44 : uint8 Ui[SCRAM_MAX_KEY_LEN];
45 : uint8 Ui_prev[SCRAM_MAX_KEY_LEN];
110 michael 46 GNC 92 : pg_hmac_ctx *hmac_ctx = pg_hmac_create(hash_type);
736 michael 47 ECB :
736 michael 48 GIC 92 : if (hmac_ctx == NULL)
451 michael 49 ECB : {
451 michael 50 UIC 0 : *errstr = pg_hmac_error(NULL); /* returns OOM */
736 michael 51 UBC 0 : return -1;
451 michael 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 */
736 michael 61 GIC 184 : if (pg_hmac_init(hmac_ctx, (uint8 *) password, password_len) < 0 ||
736 michael 62 CBC 184 : pg_hmac_update(hmac_ctx, (uint8 *) salt, saltlen) < 0 ||
63 184 : pg_hmac_update(hmac_ctx, (uint8 *) &one, sizeof(uint32)) < 0 ||
110 michael 64 GNC 92 : pg_hmac_final(hmac_ctx, Ui_prev, key_length) < 0)
858 michael 65 ECB : {
451 michael 66 UIC 0 : *errstr = pg_hmac_error(hmac_ctx);
736 michael 67 UBC 0 : pg_hmac_free(hmac_ctx);
858 68 0 : return -1;
858 michael 69 EUB : }
70 :
110 michael 71 GNC 92 : memcpy(result, Ui_prev, key_length);
2224 heikki.linnakangas 72 ECB :
73 : /* Subsequent iterations */
2224 heikki.linnakangas 74 GIC 333950 : for (i = 2; i <= iterations; i++)
2224 heikki.linnakangas 75 ECB : {
736 michael 76 GIC 667716 : if (pg_hmac_init(hmac_ctx, (uint8 *) password, password_len) < 0 ||
110 michael 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)
858 michael 79 ECB : {
451 michael 80 UIC 0 : *errstr = pg_hmac_error(hmac_ctx);
736 michael 81 UBC 0 : pg_hmac_free(hmac_ctx);
858 82 0 : return -1;
858 michael 83 EUB : }
84 :
110 michael 85 GNC 11017314 : for (j = 0; j < key_length; j++)
2224 heikki.linnakangas 86 CBC 10683456 : result[j] ^= Ui[j];
110 michael 87 GNC 333858 : memcpy(Ui_prev, Ui, key_length);
2224 heikki.linnakangas 88 ECB : }
89 :
736 michael 90 GIC 92 : pg_hmac_free(hmac_ctx);
858 michael 91 CBC 92 : return 0;
2224 heikki.linnakangas 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
110 michael 101 GNC 110 : scram_H(const uint8 *input, pg_cryptohash_type hash_type, int key_length,
102 : uint8 *result, const char **errstr)
2224 heikki.linnakangas 103 ECB : {
104 : pg_cryptohash_ctx *ctx;
105 :
110 michael 106 GNC 110 : ctx = pg_cryptohash_create(hash_type);
858 michael 107 GIC 110 : if (ctx == NULL)
451 michael 108 ECB : {
451 michael 109 LBC 0 : *errstr = pg_cryptohash_error(NULL); /* returns OOM */
858 michael 110 UIC 0 : return -1;
451 michael 111 EUB : }
858 112 :
858 michael 113 GIC 220 : if (pg_cryptohash_init(ctx) < 0 ||
110 michael 114 GNC 220 : pg_cryptohash_update(ctx, input, key_length) < 0 ||
115 110 : pg_cryptohash_final(ctx, result, key_length) < 0)
858 michael 116 ECB : {
451 michael 117 LBC 0 : *errstr = pg_cryptohash_error(ctx);
858 michael 118 UIC 0 : pg_cryptohash_free(ctx);
858 michael 119 UBC 0 : return -1;
858 michael 120 EUB : }
121 :
858 michael 122 GIC 110 : pg_cryptohash_free(ctx);
123 110 : return 0;
2224 heikki.linnakangas 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
110 michael 131 GNC 77 : scram_ClientKey(const uint8 *salted_password,
132 : pg_cryptohash_type hash_type, int key_length,
133 : uint8 *result, const char **errstr)
2172 heikki.linnakangas 134 ECB : {
110 michael 135 GNC 77 : pg_hmac_ctx *ctx = pg_hmac_create(hash_type);
136 :
736 michael 137 GIC 77 : if (ctx == NULL)
451 michael 138 ECB : {
451 michael 139 UIC 0 : *errstr = pg_hmac_error(NULL); /* returns OOM */
736 michael 140 LBC 0 : return -1;
141 : }
2172 heikki.linnakangas 142 EUB :
110 michael 143 GNC 154 : if (pg_hmac_init(ctx, salted_password, key_length) < 0 ||
736 michael 144 GIC 154 : pg_hmac_update(ctx, (uint8 *) "Client Key", strlen("Client Key")) < 0 ||
110 michael 145 GNC 77 : pg_hmac_final(ctx, result, key_length) < 0)
858 michael 146 ECB : {
451 michael 147 LBC 0 : *errstr = pg_hmac_error(ctx);
736 148 0 : pg_hmac_free(ctx);
858 michael 149 UIC 0 : return -1;
858 michael 150 EUB : }
151 :
736 michael 152 GBC 77 : pg_hmac_free(ctx);
858 michael 153 GIC 77 : return 0;
154 : }
2172 heikki.linnakangas 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
110 michael 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 :
736 michael 167 GIC 86 : if (ctx == NULL)
168 : {
451 michael 169 LBC 0 : *errstr = pg_hmac_error(NULL); /* returns OOM */
736 michael 170 UIC 0 : return -1;
451 michael 171 ECB : }
172 :
110 michael 173 GNC 172 : if (pg_hmac_init(ctx, salted_password, key_length) < 0 ||
736 michael 174 GBC 172 : pg_hmac_update(ctx, (uint8 *) "Server Key", strlen("Server Key")) < 0 ||
110 michael 175 GNC 86 : pg_hmac_final(ctx, result, key_length) < 0)
176 : {
451 michael 177 LBC 0 : *errstr = pg_hmac_error(ctx);
736 178 0 : pg_hmac_free(ctx);
858 179 0 : return -1;
180 : }
858 michael 181 EUB :
736 michael 182 GBC 86 : pg_hmac_free(ctx);
858 183 86 : return 0;
184 : }
185 :
2167 heikki.linnakangas 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 *
110 michael 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 :
13 dgustafsson 217 43 : Assert(iterations > 0);
218 :
219 : /* Calculate StoredKey and ServerKey */
110 michael 220 43 : if (scram_SaltedPassword(password, hash_type, key_length,
221 : salt, saltlen, iterations,
451 michael 222 GIC 43 : salted_password, errstr) < 0 ||
110 michael 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 */
858 michael 231 ECB : #ifdef FRONTEND
858 michael 232 UIC 0 : return NULL;
858 michael 233 ECB : #else
451 michael 234 LBC 0 : elog(ERROR, "could not calculate stored key and server key: %s",
451 michael 235 ECB : *errstr);
858 236 : #endif
237 : }
2167 heikki.linnakangas 238 :
239 : /*----------
240 : * The format is:
241 : * SCRAM-SHA-256$<iteration count>:<salt>$<StoredKey>:<ServerKey>
242 : *----------
2167 heikki.linnakangas 243 EUB : */
1375 michael 244 GIC 43 : encoded_salt_len = pg_b64_enc_len(saltlen);
110 michael 245 GNC 43 : encoded_stored_len = pg_b64_enc_len(key_length);
246 43 : encoded_server_len = pg_b64_enc_len(key_length);
247 :
2167 heikki.linnakangas 248 GIC 43 : maxlen = strlen("SCRAM-SHA-256") + 1
249 : + 10 + 1 /* iteration count */
250 : + encoded_salt_len + 1 /* Base64-encoded salt */
1375 michael 251 43 : + encoded_stored_len + 1 /* Base64-encoded StoredKey */
252 43 : + encoded_server_len + 1; /* Base64-encoded ServerKey */
253 :
254 : #ifdef FRONTEND
2167 heikki.linnakangas 255 CBC 1 : result = malloc(maxlen);
256 1 : if (!result)
451 michael 257 ECB : {
451 michael 258 UIC 0 : *errstr = _("out of memory");
2167 heikki.linnakangas 259 LBC 0 : return NULL;
260 : }
261 : #else
2167 heikki.linnakangas 262 CBC 42 : result = palloc(maxlen);
2167 heikki.linnakangas 263 ECB : #endif
264 :
2167 heikki.linnakangas 265 GIC 43 : p = result + sprintf(result, "SCRAM-SHA-256$%d:", iterations);
2167 heikki.linnakangas 266 ECB :
1375 michael 267 : /* salt */
1375 michael 268 GIC 43 : encoded_result = pg_b64_encode(salt, saltlen, p, encoded_salt_len);
1375 michael 269 GBC 43 : if (encoded_result < 0)
1375 michael 270 EUB : {
451 michael 271 UIC 0 : *errstr = _("could not encode salt");
272 : #ifdef FRONTEND
1375 michael 273 LBC 0 : free(result);
1375 michael 274 UIC 0 : return NULL;
275 : #else
451 michael 276 LBC 0 : elog(ERROR, "%s", *errstr);
277 : #endif
278 : }
1375 michael 279 CBC 43 : p += encoded_result;
2167 heikki.linnakangas 280 43 : *(p++) = '$';
281 :
1375 michael 282 EUB : /* stored key */
110 michael 283 GNC 43 : encoded_result = pg_b64_encode((char *) stored_key, key_length, p,
1375 michael 284 EUB : encoded_stored_len);
1375 michael 285 GBC 43 : if (encoded_result < 0)
286 : {
451 michael 287 UBC 0 : *errstr = _("could not encode stored key");
288 : #ifdef FRONTEND
1375 michael 289 UIC 0 : free(result);
1375 michael 290 LBC 0 : return NULL;
1375 michael 291 ECB : #else
451 michael 292 UIC 0 : elog(ERROR, "%s", *errstr);
293 : #endif
1375 michael 294 ECB : }
295 :
1375 michael 296 CBC 43 : p += encoded_result;
2167 heikki.linnakangas 297 GIC 43 : *(p++) = ':';
1375 michael 298 EUB :
299 : /* server key */
110 michael 300 GNC 43 : encoded_result = pg_b64_encode((char *) server_key, key_length, p,
1375 michael 301 EUB : encoded_server_len);
1375 michael 302 GIC 43 : if (encoded_result < 0)
1375 michael 303 EUB : {
451 michael 304 UIC 0 : *errstr = _("could not encode server key");
305 : #ifdef FRONTEND
1375 306 0 : free(result);
1375 michael 307 LBC 0 : return NULL;
1375 michael 308 ECB : #else
451 michael 309 UIC 0 : elog(ERROR, "%s", *errstr);
310 : #endif
1375 michael 311 ECB : }
312 :
1375 michael 313 CBC 43 : p += encoded_result;
2167 heikki.linnakangas 314 GIC 43 : *(p++) = '\0';
2167 heikki.linnakangas 315 EUB :
2167 heikki.linnakangas 316 GIC 43 : Assert(p - result <= maxlen);
2167 heikki.linnakangas 317 EUB :
2167 heikki.linnakangas 318 GBC 43 : return result;
319 : }
|