/src/postgres/src/backend/libpq/auth-scram.c
Line | Count | Source |
1 | | /*------------------------------------------------------------------------- |
2 | | * |
3 | | * auth-scram.c |
4 | | * Server-side implementation of the SASL SCRAM-SHA-256 mechanism. |
5 | | * |
6 | | * See the following RFCs for more details: |
7 | | * - RFC 5802: https://tools.ietf.org/html/rfc5802 |
8 | | * - RFC 5803: https://tools.ietf.org/html/rfc5803 |
9 | | * - RFC 7677: https://tools.ietf.org/html/rfc7677 |
10 | | * |
11 | | * Here are some differences: |
12 | | * |
13 | | * - Username from the authentication exchange is not used. The client |
14 | | * should send an empty string as the username. |
15 | | * |
16 | | * - If the password isn't valid UTF-8, or contains characters prohibited |
17 | | * by the SASLprep profile, we skip the SASLprep pre-processing and use |
18 | | * the raw bytes in calculating the hash. |
19 | | * |
20 | | * - If channel binding is used, the channel binding type is always |
21 | | * "tls-server-end-point". The spec says the default is "tls-unique" |
22 | | * (RFC 5802, section 6.1. Default Channel Binding), but there are some |
23 | | * problems with that. Firstly, not all SSL libraries provide an API to |
24 | | * get the TLS Finished message, required to use "tls-unique". Secondly, |
25 | | * "tls-unique" is not specified for TLS v1.3, and as of this writing, |
26 | | * it's not clear if there will be a replacement. We could support both |
27 | | * "tls-server-end-point" and "tls-unique", but for our use case, |
28 | | * "tls-unique" doesn't really have any advantages. The main advantage |
29 | | * of "tls-unique" would be that it works even if the server doesn't |
30 | | * have a certificate, but PostgreSQL requires a server certificate |
31 | | * whenever SSL is used, anyway. |
32 | | * |
33 | | * |
34 | | * The password stored in pg_authid consists of the iteration count, salt, |
35 | | * StoredKey and ServerKey. |
36 | | * |
37 | | * SASLprep usage |
38 | | * -------------- |
39 | | * |
40 | | * One notable difference to the SCRAM specification is that while the |
41 | | * specification dictates that the password is in UTF-8, and prohibits |
42 | | * certain characters, we are more lenient. If the password isn't a valid |
43 | | * UTF-8 string, or contains prohibited characters, the raw bytes are used |
44 | | * to calculate the hash instead, without SASLprep processing. This is |
45 | | * because PostgreSQL supports other encodings too, and the encoding being |
46 | | * used during authentication is undefined (client_encoding isn't set until |
47 | | * after authentication). In effect, we try to interpret the password as |
48 | | * UTF-8 and apply SASLprep processing, but if it looks invalid, we assume |
49 | | * that it's in some other encoding. |
50 | | * |
51 | | * In the worst case, we misinterpret a password that's in a different |
52 | | * encoding as being Unicode, because it happens to consists entirely of |
53 | | * valid UTF-8 bytes, and we apply Unicode normalization to it. As long |
54 | | * as we do that consistently, that will not lead to failed logins. |
55 | | * Fortunately, the UTF-8 byte sequences that are ignored by SASLprep |
56 | | * don't correspond to any commonly used characters in any of the other |
57 | | * supported encodings, so it should not lead to any significant loss in |
58 | | * entropy, even if the normalization is incorrectly applied to a |
59 | | * non-UTF-8 password. |
60 | | * |
61 | | * Error handling |
62 | | * -------------- |
63 | | * |
64 | | * Don't reveal user information to an unauthenticated client. We don't |
65 | | * want an attacker to be able to probe whether a particular username is |
66 | | * valid. In SCRAM, the server has to read the salt and iteration count |
67 | | * from the user's stored secret, and send it to the client. To avoid |
68 | | * revealing whether a user exists, when the client tries to authenticate |
69 | | * with a username that doesn't exist, or doesn't have a valid SCRAM |
70 | | * secret in pg_authid, we create a fake salt and iteration count |
71 | | * on-the-fly, and proceed with the authentication with that. In the end, |
72 | | * we'll reject the attempt, as if an incorrect password was given. When |
73 | | * we are performing a "mock" authentication, the 'doomed' flag in |
74 | | * scram_state is set. |
75 | | * |
76 | | * In the error messages, avoid printing strings from the client, unless |
77 | | * you check that they are pure ASCII. We don't want an unauthenticated |
78 | | * attacker to be able to spam the logs with characters that are not valid |
79 | | * to the encoding being used, whatever that is. We cannot avoid that in |
80 | | * general, after logging in, but let's do what we can here. |
81 | | * |
82 | | * |
83 | | * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group |
84 | | * Portions Copyright (c) 1994, Regents of the University of California |
85 | | * |
86 | | * src/backend/libpq/auth-scram.c |
87 | | * |
88 | | *------------------------------------------------------------------------- |
89 | | */ |
90 | | #include "postgres.h" |
91 | | |
92 | | #include <unistd.h> |
93 | | |
94 | | #include "access/xlog.h" |
95 | | #include "catalog/pg_control.h" |
96 | | #include "common/base64.h" |
97 | | #include "common/hmac.h" |
98 | | #include "common/saslprep.h" |
99 | | #include "common/scram-common.h" |
100 | | #include "common/sha2.h" |
101 | | #include "libpq/crypt.h" |
102 | | #include "libpq/sasl.h" |
103 | | #include "libpq/scram.h" |
104 | | #include "miscadmin.h" |
105 | | |
106 | | static void scram_get_mechanisms(Port *port, StringInfo buf); |
107 | | static void *scram_init(Port *port, const char *selected_mech, |
108 | | const char *shadow_pass); |
109 | | static int scram_exchange(void *opaq, const char *input, int inputlen, |
110 | | char **output, int *outputlen, |
111 | | const char **logdetail); |
112 | | |
113 | | /* Mechanism declaration */ |
114 | | const pg_be_sasl_mech pg_be_scram_mech = { |
115 | | scram_get_mechanisms, |
116 | | scram_init, |
117 | | scram_exchange, |
118 | | |
119 | | PG_MAX_SASL_MESSAGE_LENGTH |
120 | | }; |
121 | | |
122 | | /* |
123 | | * Status data for a SCRAM authentication exchange. This should be kept |
124 | | * internal to this file. |
125 | | */ |
126 | | typedef enum |
127 | | { |
128 | | SCRAM_AUTH_INIT, |
129 | | SCRAM_AUTH_SALT_SENT, |
130 | | SCRAM_AUTH_FINISHED, |
131 | | } scram_state_enum; |
132 | | |
133 | | typedef struct |
134 | | { |
135 | | scram_state_enum state; |
136 | | |
137 | | Port *port; |
138 | | bool channel_binding_in_use; |
139 | | |
140 | | /* State data depending on the hash type */ |
141 | | pg_cryptohash_type hash_type; |
142 | | int key_length; |
143 | | |
144 | | int iterations; |
145 | | char *salt; /* base64-encoded */ |
146 | | uint8 ClientKey[SCRAM_MAX_KEY_LEN]; |
147 | | uint8 StoredKey[SCRAM_MAX_KEY_LEN]; |
148 | | uint8 ServerKey[SCRAM_MAX_KEY_LEN]; |
149 | | |
150 | | /* Fields of the first message from client */ |
151 | | char cbind_flag; |
152 | | char *client_first_message_bare; |
153 | | char *client_username; |
154 | | char *client_nonce; |
155 | | |
156 | | /* Fields from the last message from client */ |
157 | | char *client_final_message_without_proof; |
158 | | char *client_final_nonce; |
159 | | uint8 ClientProof[SCRAM_MAX_KEY_LEN]; |
160 | | |
161 | | /* Fields generated in the server */ |
162 | | char *server_first_message; |
163 | | char *server_nonce; |
164 | | |
165 | | /* |
166 | | * If something goes wrong during the authentication, or we are performing |
167 | | * a "mock" authentication (see comments at top of file), the 'doomed' |
168 | | * flag is set. A reason for the failure, for the server log, is put in |
169 | | * 'logdetail'. |
170 | | */ |
171 | | bool doomed; |
172 | | char *logdetail; |
173 | | } scram_state; |
174 | | |
175 | | static void read_client_first_message(scram_state *state, const char *input); |
176 | | static void read_client_final_message(scram_state *state, const char *input); |
177 | | static char *build_server_first_message(scram_state *state); |
178 | | static char *build_server_final_message(scram_state *state); |
179 | | static bool verify_client_proof(scram_state *state); |
180 | | static bool verify_final_nonce(scram_state *state); |
181 | | static void mock_scram_secret(const char *username, pg_cryptohash_type *hash_type, |
182 | | int *iterations, int *key_length, char **salt, |
183 | | uint8 *stored_key, uint8 *server_key); |
184 | | static bool is_scram_printable(char *p); |
185 | | static char *sanitize_char(char c); |
186 | | static char *sanitize_str(const char *s); |
187 | | static uint8 *scram_mock_salt(const char *username, |
188 | | pg_cryptohash_type hash_type, |
189 | | int key_length); |
190 | | |
191 | | /* |
192 | | * The number of iterations to use when generating new secrets. |
193 | | */ |
194 | | int scram_sha_256_iterations = SCRAM_SHA_256_DEFAULT_ITERATIONS; |
195 | | |
196 | | /* |
197 | | * Get a list of SASL mechanisms that this module supports. |
198 | | * |
199 | | * For the convenience of building the FE/BE packet that lists the |
200 | | * mechanisms, the names are appended to the given StringInfo buffer, |
201 | | * separated by '\0' bytes. |
202 | | */ |
203 | | static void |
204 | | scram_get_mechanisms(Port *port, StringInfo buf) |
205 | 0 | { |
206 | | /* |
207 | | * Advertise the mechanisms in decreasing order of importance. So the |
208 | | * channel-binding variants go first, if they are supported. Channel |
209 | | * binding is only supported with SSL. |
210 | | */ |
211 | | #ifdef USE_SSL |
212 | | if (port->ssl_in_use) |
213 | | { |
214 | | appendStringInfoString(buf, SCRAM_SHA_256_PLUS_NAME); |
215 | | appendStringInfoChar(buf, '\0'); |
216 | | } |
217 | | #endif |
218 | 0 | appendStringInfoString(buf, SCRAM_SHA_256_NAME); |
219 | 0 | appendStringInfoChar(buf, '\0'); |
220 | 0 | } |
221 | | |
222 | | /* |
223 | | * Initialize a new SCRAM authentication exchange status tracker. This |
224 | | * needs to be called before doing any exchange. It will be filled later |
225 | | * after the beginning of the exchange with authentication information. |
226 | | * |
227 | | * 'selected_mech' identifies the SASL mechanism that the client selected. |
228 | | * It should be one of the mechanisms that we support, as returned by |
229 | | * scram_get_mechanisms(). |
230 | | * |
231 | | * 'shadow_pass' is the role's stored secret, from pg_authid.rolpassword. |
232 | | * The username was provided by the client in the startup message, and is |
233 | | * available in port->user_name. If 'shadow_pass' is NULL, we still perform |
234 | | * an authentication exchange, but it will fail, as if an incorrect password |
235 | | * was given. |
236 | | */ |
237 | | static void * |
238 | | scram_init(Port *port, const char *selected_mech, const char *shadow_pass) |
239 | 0 | { |
240 | 0 | scram_state *state; |
241 | 0 | bool got_secret; |
242 | |
|
243 | 0 | state = palloc0_object(scram_state); |
244 | 0 | state->port = port; |
245 | 0 | state->state = SCRAM_AUTH_INIT; |
246 | | |
247 | | /* |
248 | | * Parse the selected mechanism. |
249 | | * |
250 | | * Note that if we don't support channel binding, or if we're not using |
251 | | * SSL at all, we would not have advertised the PLUS variant in the first |
252 | | * place. If the client nevertheless tries to select it, it's a protocol |
253 | | * violation like selecting any other SASL mechanism we don't support. |
254 | | */ |
255 | | #ifdef USE_SSL |
256 | | if (strcmp(selected_mech, SCRAM_SHA_256_PLUS_NAME) == 0 && port->ssl_in_use) |
257 | | state->channel_binding_in_use = true; |
258 | | else |
259 | | #endif |
260 | 0 | if (strcmp(selected_mech, SCRAM_SHA_256_NAME) == 0) |
261 | 0 | state->channel_binding_in_use = false; |
262 | 0 | else |
263 | 0 | ereport(ERROR, |
264 | 0 | (errcode(ERRCODE_PROTOCOL_VIOLATION), |
265 | 0 | errmsg("client selected an invalid SASL authentication mechanism"))); |
266 | | |
267 | | /* |
268 | | * Parse the stored secret. |
269 | | */ |
270 | 0 | if (shadow_pass) |
271 | 0 | { |
272 | 0 | int password_type = get_password_type(shadow_pass); |
273 | |
|
274 | 0 | if (password_type == PASSWORD_TYPE_SCRAM_SHA_256) |
275 | 0 | { |
276 | 0 | if (parse_scram_secret(shadow_pass, &state->iterations, |
277 | 0 | &state->hash_type, &state->key_length, |
278 | 0 | &state->salt, |
279 | 0 | state->StoredKey, |
280 | 0 | state->ServerKey)) |
281 | 0 | got_secret = true; |
282 | 0 | else |
283 | 0 | { |
284 | | /* |
285 | | * The password looked like a SCRAM secret, but could not be |
286 | | * parsed. |
287 | | */ |
288 | 0 | ereport(LOG, |
289 | 0 | (errmsg("invalid SCRAM secret for user \"%s\"", |
290 | 0 | state->port->user_name))); |
291 | 0 | got_secret = false; |
292 | 0 | } |
293 | 0 | } |
294 | 0 | else |
295 | 0 | { |
296 | | /* |
297 | | * The user doesn't have SCRAM secret. (You cannot do SCRAM |
298 | | * authentication with an MD5 hash.) |
299 | | */ |
300 | 0 | state->logdetail = psprintf(_("User \"%s\" does not have a valid SCRAM secret."), |
301 | 0 | state->port->user_name); |
302 | 0 | got_secret = false; |
303 | 0 | } |
304 | 0 | } |
305 | 0 | else |
306 | 0 | { |
307 | | /* |
308 | | * The caller requested us to perform a dummy authentication. This is |
309 | | * considered normal, since the caller requested it, so don't set log |
310 | | * detail. |
311 | | */ |
312 | 0 | got_secret = false; |
313 | 0 | } |
314 | | |
315 | | /* |
316 | | * If the user did not have a valid SCRAM secret, we still go through the |
317 | | * motions with a mock one, and fail as if the client supplied an |
318 | | * incorrect password. This is to avoid revealing information to an |
319 | | * attacker. |
320 | | */ |
321 | 0 | if (!got_secret) |
322 | 0 | { |
323 | 0 | mock_scram_secret(state->port->user_name, &state->hash_type, |
324 | 0 | &state->iterations, &state->key_length, |
325 | 0 | &state->salt, |
326 | 0 | state->StoredKey, state->ServerKey); |
327 | 0 | state->doomed = true; |
328 | 0 | } |
329 | |
|
330 | 0 | return state; |
331 | 0 | } |
332 | | |
333 | | /* |
334 | | * Continue a SCRAM authentication exchange. |
335 | | * |
336 | | * 'input' is the SCRAM payload sent by the client. On the first call, |
337 | | * 'input' contains the "Initial Client Response" that the client sent as |
338 | | * part of the SASLInitialResponse message, or NULL if no Initial Client |
339 | | * Response was given. (The SASL specification distinguishes between an |
340 | | * empty response and non-existing one.) On subsequent calls, 'input' |
341 | | * cannot be NULL. For convenience in this function, the caller must |
342 | | * ensure that there is a null terminator at input[inputlen]. |
343 | | * |
344 | | * The next message to send to client is saved in 'output', for a length |
345 | | * of 'outputlen'. In the case of an error, optionally store a palloc'd |
346 | | * string at *logdetail that will be sent to the postmaster log (but not |
347 | | * the client). |
348 | | */ |
349 | | static int |
350 | | scram_exchange(void *opaq, const char *input, int inputlen, |
351 | | char **output, int *outputlen, const char **logdetail) |
352 | 0 | { |
353 | 0 | scram_state *state = (scram_state *) opaq; |
354 | 0 | int result; |
355 | |
|
356 | 0 | *output = NULL; |
357 | | |
358 | | /* |
359 | | * If the client didn't include an "Initial Client Response" in the |
360 | | * SASLInitialResponse message, send an empty challenge, to which the |
361 | | * client will respond with the same data that usually comes in the |
362 | | * Initial Client Response. |
363 | | */ |
364 | 0 | if (input == NULL) |
365 | 0 | { |
366 | 0 | Assert(state->state == SCRAM_AUTH_INIT); |
367 | |
|
368 | 0 | *output = pstrdup(""); |
369 | 0 | *outputlen = 0; |
370 | 0 | return PG_SASL_EXCHANGE_CONTINUE; |
371 | 0 | } |
372 | | |
373 | | /* |
374 | | * Check that the input length agrees with the string length of the input. |
375 | | * We can ignore inputlen after this. |
376 | | */ |
377 | 0 | if (inputlen == 0) |
378 | 0 | ereport(ERROR, |
379 | 0 | (errcode(ERRCODE_PROTOCOL_VIOLATION), |
380 | 0 | errmsg("malformed SCRAM message"), |
381 | 0 | errdetail("The message is empty."))); |
382 | 0 | if (inputlen != strlen(input)) |
383 | 0 | ereport(ERROR, |
384 | 0 | (errcode(ERRCODE_PROTOCOL_VIOLATION), |
385 | 0 | errmsg("malformed SCRAM message"), |
386 | 0 | errdetail("Message length does not match input length."))); |
387 | | |
388 | 0 | switch (state->state) |
389 | 0 | { |
390 | 0 | case SCRAM_AUTH_INIT: |
391 | | |
392 | | /* |
393 | | * Initialization phase. Receive the first message from client |
394 | | * and be sure that it parsed correctly. Then send the challenge |
395 | | * to the client. |
396 | | */ |
397 | 0 | read_client_first_message(state, input); |
398 | | |
399 | | /* prepare message to send challenge */ |
400 | 0 | *output = build_server_first_message(state); |
401 | |
|
402 | 0 | state->state = SCRAM_AUTH_SALT_SENT; |
403 | 0 | result = PG_SASL_EXCHANGE_CONTINUE; |
404 | 0 | break; |
405 | | |
406 | 0 | case SCRAM_AUTH_SALT_SENT: |
407 | | |
408 | | /* |
409 | | * Final phase for the server. Receive the response to the |
410 | | * challenge previously sent, verify, and let the client know that |
411 | | * everything went well (or not). |
412 | | */ |
413 | 0 | read_client_final_message(state, input); |
414 | |
|
415 | 0 | if (!verify_final_nonce(state)) |
416 | 0 | ereport(ERROR, |
417 | 0 | (errcode(ERRCODE_PROTOCOL_VIOLATION), |
418 | 0 | errmsg("invalid SCRAM response"), |
419 | 0 | errdetail("Nonce does not match."))); |
420 | | |
421 | | /* |
422 | | * Now check the final nonce and the client proof. |
423 | | * |
424 | | * If we performed a "mock" authentication that we knew would fail |
425 | | * from the get go, this is where we fail. |
426 | | * |
427 | | * The SCRAM specification includes an error code, |
428 | | * "invalid-proof", for authentication failure, but it also allows |
429 | | * erroring out in an application-specific way. We choose to do |
430 | | * the latter, so that the error message for invalid password is |
431 | | * the same for all authentication methods. The caller will call |
432 | | * ereport(), when we return PG_SASL_EXCHANGE_FAILURE with no |
433 | | * output. |
434 | | * |
435 | | * NB: the order of these checks is intentional. We calculate the |
436 | | * client proof even in a mock authentication, even though it's |
437 | | * bound to fail, to thwart timing attacks to determine if a role |
438 | | * with the given name exists or not. |
439 | | */ |
440 | 0 | if (!verify_client_proof(state) || state->doomed) |
441 | 0 | { |
442 | 0 | result = PG_SASL_EXCHANGE_FAILURE; |
443 | 0 | break; |
444 | 0 | } |
445 | | |
446 | | /* Build final message for client */ |
447 | 0 | *output = build_server_final_message(state); |
448 | | |
449 | | /* Success! */ |
450 | 0 | result = PG_SASL_EXCHANGE_SUCCESS; |
451 | 0 | state->state = SCRAM_AUTH_FINISHED; |
452 | 0 | break; |
453 | | |
454 | 0 | default: |
455 | 0 | elog(ERROR, "invalid SCRAM exchange state"); |
456 | 0 | result = PG_SASL_EXCHANGE_FAILURE; |
457 | 0 | } |
458 | | |
459 | 0 | if (result == PG_SASL_EXCHANGE_FAILURE && state->logdetail && logdetail) |
460 | 0 | *logdetail = state->logdetail; |
461 | |
|
462 | 0 | if (*output) |
463 | 0 | *outputlen = strlen(*output); |
464 | |
|
465 | 0 | if (result == PG_SASL_EXCHANGE_SUCCESS && state->state == SCRAM_AUTH_FINISHED) |
466 | 0 | { |
467 | 0 | memcpy(MyProcPort->scram_ClientKey, state->ClientKey, sizeof(MyProcPort->scram_ClientKey)); |
468 | 0 | memcpy(MyProcPort->scram_ServerKey, state->ServerKey, sizeof(MyProcPort->scram_ServerKey)); |
469 | 0 | MyProcPort->has_scram_keys = true; |
470 | 0 | } |
471 | |
|
472 | 0 | return result; |
473 | 0 | } |
474 | | |
475 | | /* |
476 | | * Construct a SCRAM secret, for storing in pg_authid.rolpassword. |
477 | | * |
478 | | * The result is palloc'd, so caller is responsible for freeing it. |
479 | | */ |
480 | | char * |
481 | | pg_be_scram_build_secret(const char *password) |
482 | 0 | { |
483 | 0 | char *prep_password; |
484 | 0 | pg_saslprep_rc rc; |
485 | 0 | uint8 saltbuf[SCRAM_DEFAULT_SALT_LEN]; |
486 | 0 | char *result; |
487 | 0 | const char *errstr = NULL; |
488 | | |
489 | | /* |
490 | | * Normalize the password with SASLprep. If that doesn't work, because |
491 | | * the password isn't valid UTF-8 or contains prohibited characters, just |
492 | | * proceed with the original password. (See comments at top of file.) |
493 | | */ |
494 | 0 | rc = pg_saslprep(password, &prep_password); |
495 | 0 | if (rc == SASLPREP_SUCCESS) |
496 | 0 | password = (const char *) prep_password; |
497 | | |
498 | | /* Generate random salt */ |
499 | 0 | if (!pg_strong_random(saltbuf, SCRAM_DEFAULT_SALT_LEN)) |
500 | 0 | ereport(ERROR, |
501 | 0 | (errcode(ERRCODE_INTERNAL_ERROR), |
502 | 0 | errmsg("could not generate random salt"))); |
503 | | |
504 | 0 | result = scram_build_secret(PG_SHA256, SCRAM_SHA_256_KEY_LEN, |
505 | 0 | saltbuf, SCRAM_DEFAULT_SALT_LEN, |
506 | 0 | scram_sha_256_iterations, password, |
507 | 0 | &errstr); |
508 | |
|
509 | 0 | if (prep_password) |
510 | 0 | pfree(prep_password); |
511 | |
|
512 | 0 | return result; |
513 | 0 | } |
514 | | |
515 | | /* |
516 | | * Verify a plaintext password against a SCRAM secret. This is used when |
517 | | * performing plaintext password authentication for a user that has a SCRAM |
518 | | * secret stored in pg_authid. |
519 | | */ |
520 | | bool |
521 | | scram_verify_plain_password(const char *username, const char *password, |
522 | | const char *secret) |
523 | 0 | { |
524 | 0 | char *encoded_salt; |
525 | 0 | uint8 *salt; |
526 | 0 | int saltlen; |
527 | 0 | int iterations; |
528 | 0 | int key_length = 0; |
529 | 0 | pg_cryptohash_type hash_type; |
530 | 0 | uint8 salted_password[SCRAM_MAX_KEY_LEN]; |
531 | 0 | uint8 stored_key[SCRAM_MAX_KEY_LEN]; |
532 | 0 | uint8 server_key[SCRAM_MAX_KEY_LEN]; |
533 | 0 | uint8 computed_key[SCRAM_MAX_KEY_LEN]; |
534 | 0 | char *prep_password; |
535 | 0 | pg_saslprep_rc rc; |
536 | 0 | const char *errstr = NULL; |
537 | |
|
538 | 0 | if (!parse_scram_secret(secret, &iterations, &hash_type, &key_length, |
539 | 0 | &encoded_salt, stored_key, server_key)) |
540 | 0 | { |
541 | | /* |
542 | | * The password looked like a SCRAM secret, but could not be parsed. |
543 | | */ |
544 | 0 | ereport(LOG, |
545 | 0 | (errmsg("invalid SCRAM secret for user \"%s\"", username))); |
546 | 0 | return false; |
547 | 0 | } |
548 | | |
549 | 0 | saltlen = pg_b64_dec_len(strlen(encoded_salt)); |
550 | 0 | salt = palloc(saltlen); |
551 | 0 | saltlen = pg_b64_decode(encoded_salt, strlen(encoded_salt), salt, |
552 | 0 | saltlen); |
553 | 0 | if (saltlen < 0) |
554 | 0 | { |
555 | 0 | ereport(LOG, |
556 | 0 | (errmsg("invalid SCRAM secret for user \"%s\"", username))); |
557 | 0 | return false; |
558 | 0 | } |
559 | | |
560 | | /* Normalize the password */ |
561 | 0 | rc = pg_saslprep(password, &prep_password); |
562 | 0 | if (rc == SASLPREP_SUCCESS) |
563 | 0 | password = prep_password; |
564 | | |
565 | | /* Compute Server Key based on the user-supplied plaintext password */ |
566 | 0 | if (scram_SaltedPassword(password, hash_type, key_length, |
567 | 0 | salt, saltlen, iterations, |
568 | 0 | salted_password, &errstr) < 0 || |
569 | 0 | scram_ServerKey(salted_password, hash_type, key_length, |
570 | 0 | computed_key, &errstr) < 0) |
571 | 0 | { |
572 | 0 | elog(ERROR, "could not compute server key: %s", errstr); |
573 | 0 | } |
574 | | |
575 | 0 | if (prep_password) |
576 | 0 | pfree(prep_password); |
577 | | |
578 | | /* |
579 | | * Compare the secret's Server Key with the one computed from the |
580 | | * user-supplied password. |
581 | | */ |
582 | 0 | return timingsafe_bcmp(computed_key, server_key, key_length) == 0; |
583 | 0 | } |
584 | | |
585 | | |
586 | | /* |
587 | | * Parse and validate format of given SCRAM secret. |
588 | | * |
589 | | * On success, the iteration count, salt, stored key, and server key are |
590 | | * extracted from the secret, and returned to the caller. For 'stored_key' |
591 | | * and 'server_key', the caller must pass pre-allocated buffers of size |
592 | | * SCRAM_MAX_KEY_LEN. Salt is returned as a base64-encoded, null-terminated |
593 | | * string. The buffer for the salt is palloc'd by this function. |
594 | | * |
595 | | * Returns true if the SCRAM secret has been parsed, and false otherwise. |
596 | | */ |
597 | | bool |
598 | | parse_scram_secret(const char *secret, int *iterations, |
599 | | pg_cryptohash_type *hash_type, int *key_length, |
600 | | char **salt, uint8 *stored_key, uint8 *server_key) |
601 | 0 | { |
602 | 0 | char *v; |
603 | 0 | char *p; |
604 | 0 | char *scheme_str; |
605 | 0 | char *salt_str; |
606 | 0 | char *iterations_str; |
607 | 0 | char *storedkey_str; |
608 | 0 | char *serverkey_str; |
609 | 0 | int decoded_len; |
610 | 0 | uint8 *decoded_salt_buf; |
611 | 0 | uint8 *decoded_stored_buf; |
612 | 0 | uint8 *decoded_server_buf; |
613 | | |
614 | | /* |
615 | | * The secret is of form: |
616 | | * |
617 | | * SCRAM-SHA-256$<iterations>:<salt>$<storedkey>:<serverkey> |
618 | | */ |
619 | 0 | v = pstrdup(secret); |
620 | 0 | scheme_str = strsep(&v, "$"); |
621 | 0 | if (v == NULL) |
622 | 0 | goto invalid_secret; |
623 | 0 | iterations_str = strsep(&v, ":"); |
624 | 0 | if (v == NULL) |
625 | 0 | goto invalid_secret; |
626 | 0 | salt_str = strsep(&v, "$"); |
627 | 0 | if (v == NULL) |
628 | 0 | goto invalid_secret; |
629 | 0 | storedkey_str = strsep(&v, ":"); |
630 | 0 | if (v == NULL) |
631 | 0 | goto invalid_secret; |
632 | 0 | serverkey_str = v; |
633 | | |
634 | | /* Parse the fields */ |
635 | 0 | if (strcmp(scheme_str, "SCRAM-SHA-256") != 0) |
636 | 0 | goto invalid_secret; |
637 | 0 | *hash_type = PG_SHA256; |
638 | 0 | *key_length = SCRAM_SHA_256_KEY_LEN; |
639 | |
|
640 | 0 | errno = 0; |
641 | 0 | *iterations = strtol(iterations_str, &p, 10); |
642 | 0 | if (*p || errno != 0) |
643 | 0 | goto invalid_secret; |
644 | | |
645 | | /* |
646 | | * Verify that the salt is in Base64-encoded format, by decoding it, |
647 | | * although we return the encoded version to the caller. |
648 | | */ |
649 | 0 | decoded_len = pg_b64_dec_len(strlen(salt_str)); |
650 | 0 | decoded_salt_buf = palloc(decoded_len); |
651 | 0 | decoded_len = pg_b64_decode(salt_str, strlen(salt_str), |
652 | 0 | decoded_salt_buf, decoded_len); |
653 | 0 | if (decoded_len < 0) |
654 | 0 | goto invalid_secret; |
655 | 0 | *salt = pstrdup(salt_str); |
656 | | |
657 | | /* |
658 | | * Decode StoredKey and ServerKey. |
659 | | */ |
660 | 0 | decoded_len = pg_b64_dec_len(strlen(storedkey_str)); |
661 | 0 | decoded_stored_buf = palloc(decoded_len); |
662 | 0 | decoded_len = pg_b64_decode(storedkey_str, strlen(storedkey_str), |
663 | 0 | decoded_stored_buf, decoded_len); |
664 | 0 | if (decoded_len != *key_length) |
665 | 0 | goto invalid_secret; |
666 | 0 | memcpy(stored_key, decoded_stored_buf, *key_length); |
667 | |
|
668 | 0 | decoded_len = pg_b64_dec_len(strlen(serverkey_str)); |
669 | 0 | decoded_server_buf = palloc(decoded_len); |
670 | 0 | decoded_len = pg_b64_decode(serverkey_str, strlen(serverkey_str), |
671 | 0 | decoded_server_buf, decoded_len); |
672 | 0 | if (decoded_len != *key_length) |
673 | 0 | goto invalid_secret; |
674 | 0 | memcpy(server_key, decoded_server_buf, *key_length); |
675 | |
|
676 | 0 | return true; |
677 | | |
678 | 0 | invalid_secret: |
679 | 0 | *salt = NULL; |
680 | 0 | return false; |
681 | 0 | } |
682 | | |
683 | | /* |
684 | | * Generate plausible SCRAM secret parameters for mock authentication. |
685 | | * |
686 | | * In a normal authentication, these are extracted from the secret |
687 | | * stored in the server. This function generates values that look |
688 | | * realistic, for when there is no stored secret, using SCRAM-SHA-256. |
689 | | * |
690 | | * Like in parse_scram_secret(), for 'stored_key' and 'server_key', the |
691 | | * caller must pass pre-allocated buffers of size SCRAM_MAX_KEY_LEN, and |
692 | | * the buffer for the salt is palloc'd by this function. |
693 | | */ |
694 | | static void |
695 | | mock_scram_secret(const char *username, pg_cryptohash_type *hash_type, |
696 | | int *iterations, int *key_length, char **salt, |
697 | | uint8 *stored_key, uint8 *server_key) |
698 | 0 | { |
699 | 0 | uint8 *raw_salt; |
700 | 0 | char *encoded_salt; |
701 | 0 | int encoded_len; |
702 | | |
703 | | /* Enforce the use of SHA-256, which would be realistic enough */ |
704 | 0 | *hash_type = PG_SHA256; |
705 | 0 | *key_length = SCRAM_SHA_256_KEY_LEN; |
706 | | |
707 | | /* |
708 | | * Generate deterministic salt. |
709 | | * |
710 | | * Note that we cannot reveal any information to an attacker here so the |
711 | | * error messages need to remain generic. This should never fail anyway |
712 | | * as the salt generated for mock authentication uses the cluster's nonce |
713 | | * value. |
714 | | */ |
715 | 0 | raw_salt = scram_mock_salt(username, *hash_type, *key_length); |
716 | 0 | if (raw_salt == NULL) |
717 | 0 | elog(ERROR, "could not encode salt"); |
718 | | |
719 | 0 | encoded_len = pg_b64_enc_len(SCRAM_DEFAULT_SALT_LEN); |
720 | | /* don't forget the zero-terminator */ |
721 | 0 | encoded_salt = (char *) palloc(encoded_len + 1); |
722 | 0 | encoded_len = pg_b64_encode(raw_salt, SCRAM_DEFAULT_SALT_LEN, encoded_salt, |
723 | 0 | encoded_len); |
724 | |
|
725 | 0 | if (encoded_len < 0) |
726 | 0 | elog(ERROR, "could not encode salt"); |
727 | 0 | encoded_salt[encoded_len] = '\0'; |
728 | |
|
729 | 0 | *salt = encoded_salt; |
730 | 0 | *iterations = scram_sha_256_iterations; |
731 | | |
732 | | /* StoredKey and ServerKey are not used in a doomed authentication */ |
733 | 0 | memset(stored_key, 0, SCRAM_MAX_KEY_LEN); |
734 | 0 | memset(server_key, 0, SCRAM_MAX_KEY_LEN); |
735 | 0 | } |
736 | | |
737 | | /* |
738 | | * Read the value in a given SCRAM exchange message for given attribute. |
739 | | */ |
740 | | static char * |
741 | | read_attr_value(char **input, char attr) |
742 | 0 | { |
743 | 0 | char *begin = *input; |
744 | 0 | char *end; |
745 | |
|
746 | 0 | if (*begin != attr) |
747 | 0 | ereport(ERROR, |
748 | 0 | (errcode(ERRCODE_PROTOCOL_VIOLATION), |
749 | 0 | errmsg("malformed SCRAM message"), |
750 | 0 | errdetail("Expected attribute \"%c\" but found \"%s\".", |
751 | 0 | attr, sanitize_char(*begin)))); |
752 | 0 | begin++; |
753 | |
|
754 | 0 | if (*begin != '=') |
755 | 0 | ereport(ERROR, |
756 | 0 | (errcode(ERRCODE_PROTOCOL_VIOLATION), |
757 | 0 | errmsg("malformed SCRAM message"), |
758 | 0 | errdetail("Expected character \"=\" for attribute \"%c\".", attr))); |
759 | 0 | begin++; |
760 | |
|
761 | 0 | end = begin; |
762 | 0 | while (*end && *end != ',') |
763 | 0 | end++; |
764 | |
|
765 | 0 | if (*end) |
766 | 0 | { |
767 | 0 | *end = '\0'; |
768 | 0 | *input = end + 1; |
769 | 0 | } |
770 | 0 | else |
771 | 0 | *input = end; |
772 | |
|
773 | 0 | return begin; |
774 | 0 | } |
775 | | |
776 | | static bool |
777 | | is_scram_printable(char *p) |
778 | 0 | { |
779 | | /*------ |
780 | | * Printable characters, as defined by SCRAM spec: (RFC 5802) |
781 | | * |
782 | | * printable = %x21-2B / %x2D-7E |
783 | | * ;; Printable ASCII except ",". |
784 | | * ;; Note that any "printable" is also |
785 | | * ;; a valid "value". |
786 | | *------ |
787 | | */ |
788 | 0 | for (; *p; p++) |
789 | 0 | { |
790 | 0 | if (*p < 0x21 || *p > 0x7E || *p == 0x2C /* comma */ ) |
791 | 0 | return false; |
792 | 0 | } |
793 | 0 | return true; |
794 | 0 | } |
795 | | |
796 | | /* |
797 | | * Convert an arbitrary byte to printable form. For error messages. |
798 | | * |
799 | | * If it's a printable ASCII character, print it as a single character. |
800 | | * otherwise, print it in hex. |
801 | | * |
802 | | * The returned pointer points to a static buffer. |
803 | | */ |
804 | | static char * |
805 | | sanitize_char(char c) |
806 | 0 | { |
807 | 0 | static char buf[5]; |
808 | |
|
809 | 0 | if (c >= 0x21 && c <= 0x7E) |
810 | 0 | snprintf(buf, sizeof(buf), "'%c'", c); |
811 | 0 | else |
812 | 0 | snprintf(buf, sizeof(buf), "0x%02x", (unsigned char) c); |
813 | 0 | return buf; |
814 | 0 | } |
815 | | |
816 | | /* |
817 | | * Convert an arbitrary string to printable form, for error messages. |
818 | | * |
819 | | * Anything that's not a printable ASCII character is replaced with |
820 | | * '?', and the string is truncated at 30 characters. |
821 | | * |
822 | | * The returned pointer points to a static buffer. |
823 | | */ |
824 | | static char * |
825 | | sanitize_str(const char *s) |
826 | 0 | { |
827 | 0 | static char buf[30 + 1]; |
828 | 0 | int i; |
829 | |
|
830 | 0 | for (i = 0; i < sizeof(buf) - 1; i++) |
831 | 0 | { |
832 | 0 | char c = s[i]; |
833 | |
|
834 | 0 | if (c == '\0') |
835 | 0 | break; |
836 | | |
837 | 0 | if (c >= 0x21 && c <= 0x7E) |
838 | 0 | buf[i] = c; |
839 | 0 | else |
840 | 0 | buf[i] = '?'; |
841 | 0 | } |
842 | 0 | buf[i] = '\0'; |
843 | 0 | return buf; |
844 | 0 | } |
845 | | |
846 | | /* |
847 | | * Read the next attribute and value in a SCRAM exchange message. |
848 | | * |
849 | | * The attribute character is set in *attr_p, the attribute value is the |
850 | | * return value. |
851 | | */ |
852 | | static char * |
853 | | read_any_attr(char **input, char *attr_p) |
854 | 0 | { |
855 | 0 | char *begin = *input; |
856 | 0 | char *end; |
857 | 0 | char attr = *begin; |
858 | |
|
859 | 0 | if (attr == '\0') |
860 | 0 | ereport(ERROR, |
861 | 0 | (errcode(ERRCODE_PROTOCOL_VIOLATION), |
862 | 0 | errmsg("malformed SCRAM message"), |
863 | 0 | errdetail("Attribute expected, but found end of string."))); |
864 | | |
865 | | /*------ |
866 | | * attr-val = ALPHA "=" value |
867 | | * ;; Generic syntax of any attribute sent |
868 | | * ;; by server or client |
869 | | *------ |
870 | | */ |
871 | 0 | if (!((attr >= 'A' && attr <= 'Z') || |
872 | 0 | (attr >= 'a' && attr <= 'z'))) |
873 | 0 | ereport(ERROR, |
874 | 0 | (errcode(ERRCODE_PROTOCOL_VIOLATION), |
875 | 0 | errmsg("malformed SCRAM message"), |
876 | 0 | errdetail("Attribute expected, but found invalid character \"%s\".", |
877 | 0 | sanitize_char(attr)))); |
878 | 0 | if (attr_p) |
879 | 0 | *attr_p = attr; |
880 | 0 | begin++; |
881 | |
|
882 | 0 | if (*begin != '=') |
883 | 0 | ereport(ERROR, |
884 | 0 | (errcode(ERRCODE_PROTOCOL_VIOLATION), |
885 | 0 | errmsg("malformed SCRAM message"), |
886 | 0 | errdetail("Expected character \"=\" for attribute \"%c\".", attr))); |
887 | 0 | begin++; |
888 | |
|
889 | 0 | end = begin; |
890 | 0 | while (*end && *end != ',') |
891 | 0 | end++; |
892 | |
|
893 | 0 | if (*end) |
894 | 0 | { |
895 | 0 | *end = '\0'; |
896 | 0 | *input = end + 1; |
897 | 0 | } |
898 | 0 | else |
899 | 0 | *input = end; |
900 | |
|
901 | 0 | return begin; |
902 | 0 | } |
903 | | |
904 | | /* |
905 | | * Read and parse the first message from client in the context of a SCRAM |
906 | | * authentication exchange message. |
907 | | * |
908 | | * At this stage, any errors will be reported directly with ereport(ERROR). |
909 | | */ |
910 | | static void |
911 | | read_client_first_message(scram_state *state, const char *input) |
912 | 0 | { |
913 | 0 | char *p = pstrdup(input); |
914 | 0 | char *channel_binding_type; |
915 | | |
916 | | |
917 | | /*------ |
918 | | * The syntax for the client-first-message is: (RFC 5802) |
919 | | * |
920 | | * saslname = 1*(value-safe-char / "=2C" / "=3D") |
921 | | * ;; Conforms to <value>. |
922 | | * |
923 | | * authzid = "a=" saslname |
924 | | * ;; Protocol specific. |
925 | | * |
926 | | * cb-name = 1*(ALPHA / DIGIT / "." / "-") |
927 | | * ;; See RFC 5056, Section 7. |
928 | | * ;; E.g., "tls-server-end-point" or |
929 | | * ;; "tls-unique". |
930 | | * |
931 | | * gs2-cbind-flag = ("p=" cb-name) / "n" / "y" |
932 | | * ;; "n" -> client doesn't support channel binding. |
933 | | * ;; "y" -> client does support channel binding |
934 | | * ;; but thinks the server does not. |
935 | | * ;; "p" -> client requires channel binding. |
936 | | * ;; The selected channel binding follows "p=". |
937 | | * |
938 | | * gs2-header = gs2-cbind-flag "," [ authzid ] "," |
939 | | * ;; GS2 header for SCRAM |
940 | | * ;; (the actual GS2 header includes an optional |
941 | | * ;; flag to indicate that the GSS mechanism is not |
942 | | * ;; "standard", but since SCRAM is "standard", we |
943 | | * ;; don't include that flag). |
944 | | * |
945 | | * username = "n=" saslname |
946 | | * ;; Usernames are prepared using SASLprep. |
947 | | * |
948 | | * reserved-mext = "m=" 1*(value-char) |
949 | | * ;; Reserved for signaling mandatory extensions. |
950 | | * ;; The exact syntax will be defined in |
951 | | * ;; the future. |
952 | | * |
953 | | * nonce = "r=" c-nonce [s-nonce] |
954 | | * ;; Second part provided by server. |
955 | | * |
956 | | * c-nonce = printable |
957 | | * |
958 | | * client-first-message-bare = |
959 | | * [reserved-mext ","] |
960 | | * username "," nonce ["," extensions] |
961 | | * |
962 | | * client-first-message = |
963 | | * gs2-header client-first-message-bare |
964 | | * |
965 | | * For example: |
966 | | * n,,n=user,r=fyko+d2lbbFgONRv9qkxdawL |
967 | | * |
968 | | * The "n,," in the beginning means that the client doesn't support |
969 | | * channel binding, and no authzid is given. "n=user" is the username. |
970 | | * However, in PostgreSQL the username is sent in the startup packet, and |
971 | | * the username in the SCRAM exchange is ignored. libpq always sends it |
972 | | * as an empty string. The last part, "r=fyko+d2lbbFgONRv9qkxdawL" is |
973 | | * the client nonce. |
974 | | *------ |
975 | | */ |
976 | | |
977 | | /* |
978 | | * Read gs2-cbind-flag. (For details see also RFC 5802 Section 6 "Channel |
979 | | * Binding".) |
980 | | */ |
981 | 0 | state->cbind_flag = *p; |
982 | 0 | switch (*p) |
983 | 0 | { |
984 | 0 | case 'n': |
985 | | |
986 | | /* |
987 | | * The client does not support channel binding or has simply |
988 | | * decided to not use it. In that case just let it go. |
989 | | */ |
990 | 0 | if (state->channel_binding_in_use) |
991 | 0 | ereport(ERROR, |
992 | 0 | (errcode(ERRCODE_PROTOCOL_VIOLATION), |
993 | 0 | errmsg("malformed SCRAM message"), |
994 | 0 | errdetail("The client selected SCRAM-SHA-256-PLUS, but the SCRAM message does not include channel binding data."))); |
995 | | |
996 | 0 | p++; |
997 | 0 | if (*p != ',') |
998 | 0 | ereport(ERROR, |
999 | 0 | (errcode(ERRCODE_PROTOCOL_VIOLATION), |
1000 | 0 | errmsg("malformed SCRAM message"), |
1001 | 0 | errdetail("Comma expected, but found character \"%s\".", |
1002 | 0 | sanitize_char(*p)))); |
1003 | 0 | p++; |
1004 | 0 | break; |
1005 | 0 | case 'y': |
1006 | | |
1007 | | /* |
1008 | | * The client supports channel binding and thinks that the server |
1009 | | * does not. In this case, the server must fail authentication if |
1010 | | * it supports channel binding. |
1011 | | */ |
1012 | 0 | if (state->channel_binding_in_use) |
1013 | 0 | ereport(ERROR, |
1014 | 0 | (errcode(ERRCODE_PROTOCOL_VIOLATION), |
1015 | 0 | errmsg("malformed SCRAM message"), |
1016 | 0 | errdetail("The client selected SCRAM-SHA-256-PLUS, but the SCRAM message does not include channel binding data."))); |
1017 | | |
1018 | | #ifdef USE_SSL |
1019 | | if (state->port->ssl_in_use) |
1020 | | ereport(ERROR, |
1021 | | (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), |
1022 | | errmsg("SCRAM channel binding negotiation error"), |
1023 | | errdetail("The client supports SCRAM channel binding but thinks the server does not. " |
1024 | | "However, this server does support channel binding."))); |
1025 | | #endif |
1026 | 0 | p++; |
1027 | 0 | if (*p != ',') |
1028 | 0 | ereport(ERROR, |
1029 | 0 | (errcode(ERRCODE_PROTOCOL_VIOLATION), |
1030 | 0 | errmsg("malformed SCRAM message"), |
1031 | 0 | errdetail("Comma expected, but found character \"%s\".", |
1032 | 0 | sanitize_char(*p)))); |
1033 | 0 | p++; |
1034 | 0 | break; |
1035 | 0 | case 'p': |
1036 | | |
1037 | | /* |
1038 | | * The client requires channel binding. Channel binding type |
1039 | | * follows, e.g., "p=tls-server-end-point". |
1040 | | */ |
1041 | 0 | if (!state->channel_binding_in_use) |
1042 | 0 | ereport(ERROR, |
1043 | 0 | (errcode(ERRCODE_PROTOCOL_VIOLATION), |
1044 | 0 | errmsg("malformed SCRAM message"), |
1045 | 0 | errdetail("The client selected SCRAM-SHA-256 without channel binding, but the SCRAM message includes channel binding data."))); |
1046 | | |
1047 | 0 | channel_binding_type = read_attr_value(&p, 'p'); |
1048 | | |
1049 | | /* |
1050 | | * The only channel binding type we support is |
1051 | | * tls-server-end-point. |
1052 | | */ |
1053 | 0 | if (strcmp(channel_binding_type, "tls-server-end-point") != 0) |
1054 | 0 | ereport(ERROR, |
1055 | 0 | (errcode(ERRCODE_PROTOCOL_VIOLATION), |
1056 | 0 | errmsg("unsupported SCRAM channel-binding type \"%s\"", |
1057 | 0 | sanitize_str(channel_binding_type)))); |
1058 | 0 | break; |
1059 | 0 | default: |
1060 | 0 | ereport(ERROR, |
1061 | 0 | (errcode(ERRCODE_PROTOCOL_VIOLATION), |
1062 | 0 | errmsg("malformed SCRAM message"), |
1063 | 0 | errdetail("Unexpected channel-binding flag \"%s\".", |
1064 | 0 | sanitize_char(*p)))); |
1065 | 0 | } |
1066 | | |
1067 | | /* |
1068 | | * Forbid optional authzid (authorization identity). We don't support it. |
1069 | | */ |
1070 | 0 | if (*p == 'a') |
1071 | 0 | ereport(ERROR, |
1072 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
1073 | 0 | errmsg("client uses authorization identity, but it is not supported"))); |
1074 | 0 | if (*p != ',') |
1075 | 0 | ereport(ERROR, |
1076 | 0 | (errcode(ERRCODE_PROTOCOL_VIOLATION), |
1077 | 0 | errmsg("malformed SCRAM message"), |
1078 | 0 | errdetail("Unexpected attribute \"%s\" in client-first-message.", |
1079 | 0 | sanitize_char(*p)))); |
1080 | 0 | p++; |
1081 | |
|
1082 | 0 | state->client_first_message_bare = pstrdup(p); |
1083 | | |
1084 | | /* |
1085 | | * Any mandatory extensions would go here. We don't support any. |
1086 | | * |
1087 | | * RFC 5802 specifies error code "e=extensions-not-supported" for this, |
1088 | | * but it can only be sent in the server-final message. We prefer to fail |
1089 | | * immediately (which the RFC also allows). |
1090 | | */ |
1091 | 0 | if (*p == 'm') |
1092 | 0 | ereport(ERROR, |
1093 | 0 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
1094 | 0 | errmsg("client requires an unsupported SCRAM extension"))); |
1095 | | |
1096 | | /* |
1097 | | * Read username. Note: this is ignored. We use the username from the |
1098 | | * startup message instead, still it is kept around if provided as it |
1099 | | * proves to be useful for debugging purposes. |
1100 | | */ |
1101 | 0 | state->client_username = read_attr_value(&p, 'n'); |
1102 | | |
1103 | | /* read nonce and check that it is made of only printable characters */ |
1104 | 0 | state->client_nonce = read_attr_value(&p, 'r'); |
1105 | 0 | if (!is_scram_printable(state->client_nonce)) |
1106 | 0 | ereport(ERROR, |
1107 | 0 | (errcode(ERRCODE_PROTOCOL_VIOLATION), |
1108 | 0 | errmsg("non-printable characters in SCRAM nonce"))); |
1109 | | |
1110 | | /* |
1111 | | * There can be any number of optional extensions after this. We don't |
1112 | | * support any extensions, so ignore them. |
1113 | | */ |
1114 | 0 | while (*p != '\0') |
1115 | 0 | read_any_attr(&p, NULL); |
1116 | | |
1117 | | /* success! */ |
1118 | 0 | } |
1119 | | |
1120 | | /* |
1121 | | * Verify the final nonce contained in the last message received from |
1122 | | * client in an exchange. |
1123 | | */ |
1124 | | static bool |
1125 | | verify_final_nonce(scram_state *state) |
1126 | 0 | { |
1127 | 0 | int client_nonce_len = strlen(state->client_nonce); |
1128 | 0 | int server_nonce_len = strlen(state->server_nonce); |
1129 | 0 | int final_nonce_len = strlen(state->client_final_nonce); |
1130 | |
|
1131 | 0 | if (final_nonce_len != client_nonce_len + server_nonce_len) |
1132 | 0 | return false; |
1133 | 0 | if (timingsafe_bcmp(state->client_final_nonce, state->client_nonce, client_nonce_len) != 0) |
1134 | 0 | return false; |
1135 | 0 | if (timingsafe_bcmp(state->client_final_nonce + client_nonce_len, state->server_nonce, server_nonce_len) != 0) |
1136 | 0 | return false; |
1137 | | |
1138 | 0 | return true; |
1139 | 0 | } |
1140 | | |
1141 | | /* |
1142 | | * Verify the client proof contained in the last message received from |
1143 | | * client in an exchange. Returns true if the verification is a success, |
1144 | | * or false for a failure. |
1145 | | */ |
1146 | | static bool |
1147 | | verify_client_proof(scram_state *state) |
1148 | 0 | { |
1149 | 0 | uint8 ClientSignature[SCRAM_MAX_KEY_LEN]; |
1150 | 0 | uint8 client_StoredKey[SCRAM_MAX_KEY_LEN]; |
1151 | 0 | pg_hmac_ctx *ctx = pg_hmac_create(state->hash_type); |
1152 | 0 | int i; |
1153 | 0 | const char *errstr = NULL; |
1154 | | |
1155 | | /* |
1156 | | * Calculate ClientSignature. Note that we don't log directly a failure |
1157 | | * here even when processing the calculations as this could involve a mock |
1158 | | * authentication. |
1159 | | */ |
1160 | 0 | if (pg_hmac_init(ctx, state->StoredKey, state->key_length) < 0 || |
1161 | 0 | pg_hmac_update(ctx, |
1162 | 0 | (uint8 *) state->client_first_message_bare, |
1163 | 0 | strlen(state->client_first_message_bare)) < 0 || |
1164 | 0 | pg_hmac_update(ctx, (uint8 *) ",", 1) < 0 || |
1165 | 0 | pg_hmac_update(ctx, |
1166 | 0 | (uint8 *) state->server_first_message, |
1167 | 0 | strlen(state->server_first_message)) < 0 || |
1168 | 0 | pg_hmac_update(ctx, (uint8 *) ",", 1) < 0 || |
1169 | 0 | pg_hmac_update(ctx, |
1170 | 0 | (uint8 *) state->client_final_message_without_proof, |
1171 | 0 | strlen(state->client_final_message_without_proof)) < 0 || |
1172 | 0 | pg_hmac_final(ctx, ClientSignature, state->key_length) < 0) |
1173 | 0 | { |
1174 | 0 | elog(ERROR, "could not calculate client signature: %s", |
1175 | 0 | pg_hmac_error(ctx)); |
1176 | 0 | } |
1177 | | |
1178 | 0 | pg_hmac_free(ctx); |
1179 | | |
1180 | | /* Extract the ClientKey that the client calculated from the proof */ |
1181 | 0 | for (i = 0; i < state->key_length; i++) |
1182 | 0 | state->ClientKey[i] = state->ClientProof[i] ^ ClientSignature[i]; |
1183 | | |
1184 | | /* Hash it one more time, and compare with StoredKey */ |
1185 | 0 | if (scram_H(state->ClientKey, state->hash_type, state->key_length, |
1186 | 0 | client_StoredKey, &errstr) < 0) |
1187 | 0 | elog(ERROR, "could not hash stored key: %s", errstr); |
1188 | | |
1189 | 0 | if (timingsafe_bcmp(client_StoredKey, state->StoredKey, state->key_length) != 0) |
1190 | 0 | return false; |
1191 | | |
1192 | 0 | return true; |
1193 | 0 | } |
1194 | | |
1195 | | /* |
1196 | | * Build the first server-side message sent to the client in a SCRAM |
1197 | | * communication exchange. |
1198 | | */ |
1199 | | static char * |
1200 | | build_server_first_message(scram_state *state) |
1201 | 0 | { |
1202 | | /*------ |
1203 | | * The syntax for the server-first-message is: (RFC 5802) |
1204 | | * |
1205 | | * server-first-message = |
1206 | | * [reserved-mext ","] nonce "," salt "," |
1207 | | * iteration-count ["," extensions] |
1208 | | * |
1209 | | * nonce = "r=" c-nonce [s-nonce] |
1210 | | * ;; Second part provided by server. |
1211 | | * |
1212 | | * c-nonce = printable |
1213 | | * |
1214 | | * s-nonce = printable |
1215 | | * |
1216 | | * salt = "s=" base64 |
1217 | | * |
1218 | | * iteration-count = "i=" posit-number |
1219 | | * ;; A positive number. |
1220 | | * |
1221 | | * Example: |
1222 | | * |
1223 | | * r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,s=QSXCR+Q6sek8bf92,i=4096 |
1224 | | *------ |
1225 | | */ |
1226 | | |
1227 | | /* |
1228 | | * Per the spec, the nonce may consist of any printable ASCII characters. |
1229 | | * For convenience, however, we don't use the whole range available, |
1230 | | * rather, we generate some random bytes, and base64 encode them. |
1231 | | */ |
1232 | 0 | uint8 raw_nonce[SCRAM_RAW_NONCE_LEN]; |
1233 | 0 | int encoded_len; |
1234 | |
|
1235 | 0 | if (!pg_strong_random(raw_nonce, SCRAM_RAW_NONCE_LEN)) |
1236 | 0 | ereport(ERROR, |
1237 | 0 | (errcode(ERRCODE_INTERNAL_ERROR), |
1238 | 0 | errmsg("could not generate random nonce"))); |
1239 | | |
1240 | 0 | encoded_len = pg_b64_enc_len(SCRAM_RAW_NONCE_LEN); |
1241 | | /* don't forget the zero-terminator */ |
1242 | 0 | state->server_nonce = palloc(encoded_len + 1); |
1243 | 0 | encoded_len = pg_b64_encode(raw_nonce, SCRAM_RAW_NONCE_LEN, |
1244 | 0 | state->server_nonce, encoded_len); |
1245 | 0 | if (encoded_len < 0) |
1246 | 0 | ereport(ERROR, |
1247 | 0 | (errcode(ERRCODE_INTERNAL_ERROR), |
1248 | 0 | errmsg("could not encode random nonce"))); |
1249 | 0 | state->server_nonce[encoded_len] = '\0'; |
1250 | |
|
1251 | 0 | state->server_first_message = |
1252 | 0 | psprintf("r=%s%s,s=%s,i=%d", |
1253 | 0 | state->client_nonce, state->server_nonce, |
1254 | 0 | state->salt, state->iterations); |
1255 | |
|
1256 | 0 | return pstrdup(state->server_first_message); |
1257 | 0 | } |
1258 | | |
1259 | | |
1260 | | /* |
1261 | | * Read and parse the final message received from client. |
1262 | | */ |
1263 | | static void |
1264 | | read_client_final_message(scram_state *state, const char *input) |
1265 | 0 | { |
1266 | 0 | char attr; |
1267 | 0 | char *channel_binding; |
1268 | 0 | char *value; |
1269 | 0 | char *begin, |
1270 | 0 | *proof; |
1271 | 0 | char *p; |
1272 | 0 | uint8 *client_proof; |
1273 | 0 | int client_proof_len; |
1274 | |
|
1275 | 0 | begin = p = pstrdup(input); |
1276 | | |
1277 | | /*------ |
1278 | | * The syntax for the server-first-message is: (RFC 5802) |
1279 | | * |
1280 | | * gs2-header = gs2-cbind-flag "," [ authzid ] "," |
1281 | | * ;; GS2 header for SCRAM |
1282 | | * ;; (the actual GS2 header includes an optional |
1283 | | * ;; flag to indicate that the GSS mechanism is not |
1284 | | * ;; "standard", but since SCRAM is "standard", we |
1285 | | * ;; don't include that flag). |
1286 | | * |
1287 | | * cbind-input = gs2-header [ cbind-data ] |
1288 | | * ;; cbind-data MUST be present for |
1289 | | * ;; gs2-cbind-flag of "p" and MUST be absent |
1290 | | * ;; for "y" or "n". |
1291 | | * |
1292 | | * channel-binding = "c=" base64 |
1293 | | * ;; base64 encoding of cbind-input. |
1294 | | * |
1295 | | * proof = "p=" base64 |
1296 | | * |
1297 | | * client-final-message-without-proof = |
1298 | | * channel-binding "," nonce ["," |
1299 | | * extensions] |
1300 | | * |
1301 | | * client-final-message = |
1302 | | * client-final-message-without-proof "," proof |
1303 | | *------ |
1304 | | */ |
1305 | | |
1306 | | /* |
1307 | | * Read channel binding. This repeats the channel-binding flags and is |
1308 | | * then followed by the actual binding data depending on the type. |
1309 | | */ |
1310 | 0 | channel_binding = read_attr_value(&p, 'c'); |
1311 | 0 | if (state->channel_binding_in_use) |
1312 | 0 | { |
1313 | | #ifdef USE_SSL |
1314 | | const char *cbind_data = NULL; |
1315 | | size_t cbind_data_len = 0; |
1316 | | size_t cbind_header_len; |
1317 | | char *cbind_input; |
1318 | | size_t cbind_input_len; |
1319 | | char *b64_message; |
1320 | | int b64_message_len; |
1321 | | |
1322 | | Assert(state->cbind_flag == 'p'); |
1323 | | |
1324 | | /* Fetch hash data of server's SSL certificate */ |
1325 | | cbind_data = be_tls_get_certificate_hash(state->port, |
1326 | | &cbind_data_len); |
1327 | | |
1328 | | /* should not happen */ |
1329 | | if (cbind_data == NULL || cbind_data_len == 0) |
1330 | | elog(ERROR, "could not get server certificate hash"); |
1331 | | |
1332 | | cbind_header_len = strlen("p=tls-server-end-point,,"); /* p=type,, */ |
1333 | | cbind_input_len = cbind_header_len + cbind_data_len; |
1334 | | cbind_input = palloc(cbind_input_len); |
1335 | | snprintf(cbind_input, cbind_input_len, "p=tls-server-end-point,,"); |
1336 | | memcpy(cbind_input + cbind_header_len, cbind_data, cbind_data_len); |
1337 | | |
1338 | | b64_message_len = pg_b64_enc_len(cbind_input_len); |
1339 | | /* don't forget the zero-terminator */ |
1340 | | b64_message = palloc(b64_message_len + 1); |
1341 | | b64_message_len = pg_b64_encode((uint8 *) cbind_input, cbind_input_len, |
1342 | | b64_message, b64_message_len); |
1343 | | if (b64_message_len < 0) |
1344 | | elog(ERROR, "could not encode channel binding data"); |
1345 | | b64_message[b64_message_len] = '\0'; |
1346 | | |
1347 | | /* |
1348 | | * Compare the value sent by the client with the value expected by the |
1349 | | * server. |
1350 | | */ |
1351 | | if (strcmp(channel_binding, b64_message) != 0) |
1352 | | ereport(ERROR, |
1353 | | (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), |
1354 | | errmsg("SCRAM channel binding check failed"))); |
1355 | | #else |
1356 | | /* shouldn't happen, because we checked this earlier already */ |
1357 | 0 | elog(ERROR, "channel binding not supported by this build"); |
1358 | 0 | #endif |
1359 | 0 | } |
1360 | 0 | else |
1361 | 0 | { |
1362 | | /* |
1363 | | * If we are not using channel binding, the binding data is expected |
1364 | | * to always be "biws", which is "n,," base64-encoded, or "eSws", |
1365 | | * which is "y,,". We also have to check whether the flag is the same |
1366 | | * one that the client originally sent. |
1367 | | */ |
1368 | 0 | if (!(strcmp(channel_binding, "biws") == 0 && state->cbind_flag == 'n') && |
1369 | 0 | !(strcmp(channel_binding, "eSws") == 0 && state->cbind_flag == 'y')) |
1370 | 0 | ereport(ERROR, |
1371 | 0 | (errcode(ERRCODE_PROTOCOL_VIOLATION), |
1372 | 0 | errmsg("unexpected SCRAM channel-binding attribute in client-final-message"))); |
1373 | 0 | } |
1374 | | |
1375 | 0 | state->client_final_nonce = read_attr_value(&p, 'r'); |
1376 | | |
1377 | | /* ignore optional extensions, read until we find "p" attribute */ |
1378 | 0 | do |
1379 | 0 | { |
1380 | 0 | proof = p - 1; |
1381 | 0 | value = read_any_attr(&p, &attr); |
1382 | 0 | } while (attr != 'p'); |
1383 | |
|
1384 | 0 | client_proof_len = pg_b64_dec_len(strlen(value)); |
1385 | 0 | client_proof = palloc(client_proof_len); |
1386 | 0 | if (pg_b64_decode(value, strlen(value), client_proof, |
1387 | 0 | client_proof_len) != state->key_length) |
1388 | 0 | ereport(ERROR, |
1389 | 0 | (errcode(ERRCODE_PROTOCOL_VIOLATION), |
1390 | 0 | errmsg("malformed SCRAM message"), |
1391 | 0 | errdetail("Malformed proof in client-final-message."))); |
1392 | 0 | memcpy(state->ClientProof, client_proof, state->key_length); |
1393 | 0 | pfree(client_proof); |
1394 | |
|
1395 | 0 | if (*p != '\0') |
1396 | 0 | ereport(ERROR, |
1397 | 0 | (errcode(ERRCODE_PROTOCOL_VIOLATION), |
1398 | 0 | errmsg("malformed SCRAM message"), |
1399 | 0 | errdetail("Garbage found at the end of client-final-message."))); |
1400 | | |
1401 | 0 | state->client_final_message_without_proof = palloc(proof - begin + 1); |
1402 | 0 | memcpy(state->client_final_message_without_proof, input, proof - begin); |
1403 | 0 | state->client_final_message_without_proof[proof - begin] = '\0'; |
1404 | 0 | } |
1405 | | |
1406 | | /* |
1407 | | * Build the final server-side message of an exchange. |
1408 | | */ |
1409 | | static char * |
1410 | | build_server_final_message(scram_state *state) |
1411 | 0 | { |
1412 | 0 | uint8 ServerSignature[SCRAM_MAX_KEY_LEN]; |
1413 | 0 | char *server_signature_base64; |
1414 | 0 | int siglen; |
1415 | 0 | pg_hmac_ctx *ctx = pg_hmac_create(state->hash_type); |
1416 | | |
1417 | | /* calculate ServerSignature */ |
1418 | 0 | if (pg_hmac_init(ctx, state->ServerKey, state->key_length) < 0 || |
1419 | 0 | pg_hmac_update(ctx, |
1420 | 0 | (uint8 *) state->client_first_message_bare, |
1421 | 0 | strlen(state->client_first_message_bare)) < 0 || |
1422 | 0 | pg_hmac_update(ctx, (uint8 *) ",", 1) < 0 || |
1423 | 0 | pg_hmac_update(ctx, |
1424 | 0 | (uint8 *) state->server_first_message, |
1425 | 0 | strlen(state->server_first_message)) < 0 || |
1426 | 0 | pg_hmac_update(ctx, (uint8 *) ",", 1) < 0 || |
1427 | 0 | pg_hmac_update(ctx, |
1428 | 0 | (uint8 *) state->client_final_message_without_proof, |
1429 | 0 | strlen(state->client_final_message_without_proof)) < 0 || |
1430 | 0 | pg_hmac_final(ctx, ServerSignature, state->key_length) < 0) |
1431 | 0 | { |
1432 | 0 | elog(ERROR, "could not calculate server signature: %s", |
1433 | 0 | pg_hmac_error(ctx)); |
1434 | 0 | } |
1435 | | |
1436 | 0 | pg_hmac_free(ctx); |
1437 | |
|
1438 | 0 | siglen = pg_b64_enc_len(state->key_length); |
1439 | | /* don't forget the zero-terminator */ |
1440 | 0 | server_signature_base64 = palloc(siglen + 1); |
1441 | 0 | siglen = pg_b64_encode(ServerSignature, |
1442 | 0 | state->key_length, server_signature_base64, |
1443 | 0 | siglen); |
1444 | 0 | if (siglen < 0) |
1445 | 0 | elog(ERROR, "could not encode server signature"); |
1446 | 0 | server_signature_base64[siglen] = '\0'; |
1447 | | |
1448 | | /*------ |
1449 | | * The syntax for the server-final-message is: (RFC 5802) |
1450 | | * |
1451 | | * verifier = "v=" base64 |
1452 | | * ;; base-64 encoded ServerSignature. |
1453 | | * |
1454 | | * server-final-message = (server-error / verifier) |
1455 | | * ["," extensions] |
1456 | | * |
1457 | | *------ |
1458 | | */ |
1459 | 0 | return psprintf("v=%s", server_signature_base64); |
1460 | 0 | } |
1461 | | |
1462 | | |
1463 | | /* |
1464 | | * Deterministically generate salt for mock authentication, using a SHA256 |
1465 | | * hash based on the username and a cluster-level secret key. Returns a |
1466 | | * pointer to a static buffer of size SCRAM_DEFAULT_SALT_LEN, or NULL. |
1467 | | */ |
1468 | | static uint8 * |
1469 | | scram_mock_salt(const char *username, pg_cryptohash_type hash_type, |
1470 | | int key_length) |
1471 | 0 | { |
1472 | 0 | pg_cryptohash_ctx *ctx; |
1473 | 0 | static uint8 sha_digest[SCRAM_MAX_KEY_LEN]; |
1474 | 0 | char *mock_auth_nonce = GetMockAuthenticationNonce(); |
1475 | | |
1476 | | /* |
1477 | | * Generate salt using a SHA256 hash of the username and the cluster's |
1478 | | * mock authentication nonce. (This works as long as the salt length is |
1479 | | * not larger than the SHA256 digest length. If the salt is smaller, the |
1480 | | * caller will just ignore the extra data.) |
1481 | | */ |
1482 | 0 | StaticAssertDecl(PG_SHA256_DIGEST_LENGTH >= SCRAM_DEFAULT_SALT_LEN, |
1483 | 0 | "salt length greater than SHA256 digest length"); |
1484 | | |
1485 | | /* |
1486 | | * This may be worth refreshing if support for more hash methods is\ |
1487 | | * added. |
1488 | | */ |
1489 | 0 | Assert(hash_type == PG_SHA256); |
1490 | |
|
1491 | 0 | ctx = pg_cryptohash_create(hash_type); |
1492 | 0 | if (pg_cryptohash_init(ctx) < 0 || |
1493 | 0 | pg_cryptohash_update(ctx, (const uint8 *) username, strlen(username)) < 0 || |
1494 | 0 | pg_cryptohash_update(ctx, (const uint8 *) mock_auth_nonce, MOCK_AUTH_NONCE_LEN) < 0 || |
1495 | 0 | pg_cryptohash_final(ctx, sha_digest, key_length) < 0) |
1496 | 0 | { |
1497 | 0 | pg_cryptohash_free(ctx); |
1498 | 0 | return NULL; |
1499 | 0 | } |
1500 | 0 | pg_cryptohash_free(ctx); |
1501 | |
|
1502 | 0 | return sha_digest; |
1503 | 0 | } |