Line data Source code
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 : * - Channel binding is not supported yet.
21 : *
22 : *
23 : * The password stored in pg_authid consists of the iteration count, salt,
24 : * StoredKey and ServerKey.
25 : *
26 : * SASLprep usage
27 : * --------------
28 : *
29 : * One notable difference to the SCRAM specification is that while the
30 : * specification dictates that the password is in UTF-8, and prohibits
31 : * certain characters, we are more lenient. If the password isn't a valid
32 : * UTF-8 string, or contains prohibited characters, the raw bytes are used
33 : * to calculate the hash instead, without SASLprep processing. This is
34 : * because PostgreSQL supports other encodings too, and the encoding being
35 : * used during authentication is undefined (client_encoding isn't set until
36 : * after authentication). In effect, we try to interpret the password as
37 : * UTF-8 and apply SASLprep processing, but if it looks invalid, we assume
38 : * that it's in some other encoding.
39 : *
40 : * In the worst case, we misinterpret a password that's in a different
41 : * encoding as being Unicode, because it happens to consists entirely of
42 : * valid UTF-8 bytes, and we apply Unicode normalization to it. As long
43 : * as we do that consistently, that will not lead to failed logins.
44 : * Fortunately, the UTF-8 byte sequences that are ignored by SASLprep
45 : * don't correspond to any commonly used characters in any of the other
46 : * supported encodings, so it should not lead to any significant loss in
47 : * entropy, even if the normalization is incorrectly applied to a
48 : * non-UTF-8 password.
49 : *
50 : * Error handling
51 : * --------------
52 : *
53 : * Don't reveal user information to an unauthenticated client. We don't
54 : * want an attacker to be able to probe whether a particular username is
55 : * valid. In SCRAM, the server has to read the salt and iteration count
56 : * from the user's password verifier, and send it to the client. To avoid
57 : * revealing whether a user exists, when the client tries to authenticate
58 : * with a username that doesn't exist, or doesn't have a valid SCRAM
59 : * verifier in pg_authid, we create a fake salt and iteration count
60 : * on-the-fly, and proceed with the authentication with that. In the end,
61 : * we'll reject the attempt, as if an incorrect password was given. When
62 : * we are performing a "mock" authentication, the 'doomed' flag in
63 : * scram_state is set.
64 : *
65 : * In the error messages, avoid printing strings from the client, unless
66 : * you check that they are pure ASCII. We don't want an unauthenticated
67 : * attacker to be able to spam the logs with characters that are not valid
68 : * to the encoding being used, whatever that is. We cannot avoid that in
69 : * general, after logging in, but let's do what we can here.
70 : *
71 : *
72 : * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
73 : * Portions Copyright (c) 1994, Regents of the University of California
74 : *
75 : * src/backend/libpq/auth-scram.c
76 : *
77 : *-------------------------------------------------------------------------
78 : */
79 : #include "postgres.h"
80 :
81 : #include <unistd.h>
82 :
83 : #include "access/xlog.h"
84 : #include "catalog/pg_authid.h"
85 : #include "catalog/pg_control.h"
86 : #include "common/base64.h"
87 : #include "common/saslprep.h"
88 : #include "common/scram-common.h"
89 : #include "common/sha2.h"
90 : #include "libpq/auth.h"
91 : #include "libpq/crypt.h"
92 : #include "libpq/scram.h"
93 : #include "miscadmin.h"
94 : #include "utils/backend_random.h"
95 : #include "utils/builtins.h"
96 : #include "utils/timestamp.h"
97 :
98 : /*
99 : * Status data for a SCRAM authentication exchange. This should be kept
100 : * internal to this file.
101 : */
102 : typedef enum
103 : {
104 : SCRAM_AUTH_INIT,
105 : SCRAM_AUTH_SALT_SENT,
106 : SCRAM_AUTH_FINISHED
107 : } scram_state_enum;
108 :
109 : typedef struct
110 : {
111 : scram_state_enum state;
112 :
113 : const char *username; /* username from startup packet */
114 :
115 : int iterations;
116 : char *salt; /* base64-encoded */
117 : uint8 StoredKey[SCRAM_KEY_LEN];
118 : uint8 ServerKey[SCRAM_KEY_LEN];
119 :
120 : /* Fields of the first message from client */
121 : char *client_first_message_bare;
122 : char *client_username;
123 : char *client_nonce;
124 :
125 : /* Fields from the last message from client */
126 : char *client_final_message_without_proof;
127 : char *client_final_nonce;
128 : char ClientProof[SCRAM_KEY_LEN];
129 :
130 : /* Fields generated in the server */
131 : char *server_first_message;
132 : char *server_nonce;
133 :
134 : /*
135 : * If something goes wrong during the authentication, or we are performing
136 : * a "mock" authentication (see comments at top of file), the 'doomed'
137 : * flag is set. A reason for the failure, for the server log, is put in
138 : * 'logdetail'.
139 : */
140 : bool doomed;
141 : char *logdetail;
142 : } scram_state;
143 :
144 : static void read_client_first_message(scram_state *state, char *input);
145 : static void read_client_final_message(scram_state *state, char *input);
146 : static char *build_server_first_message(scram_state *state);
147 : static char *build_server_final_message(scram_state *state);
148 : static bool verify_client_proof(scram_state *state);
149 : static bool verify_final_nonce(scram_state *state);
150 : static bool parse_scram_verifier(const char *verifier, int *iterations,
151 : char **salt, uint8 *stored_key, uint8 *server_key);
152 : static void mock_scram_verifier(const char *username, int *iterations,
153 : char **salt, uint8 *stored_key, uint8 *server_key);
154 : static bool is_scram_printable(char *p);
155 : static char *sanitize_char(char c);
156 : static char *scram_mock_salt(const char *username);
157 :
158 : /*
159 : * pg_be_scram_init
160 : *
161 : * Initialize a new SCRAM authentication exchange status tracker. This
162 : * needs to be called before doing any exchange. It will be filled later
163 : * after the beginning of the exchange with verifier data.
164 : *
165 : * 'username' is the username provided by the client in the startup message.
166 : * 'shadow_pass' is the role's password verifier, from pg_authid.rolpassword.
167 : * If 'shadow_pass' is NULL, we still perform an authentication exchange, but
168 : * it will fail, as if an incorrect password was given.
169 : */
170 : void *
171 0 : pg_be_scram_init(const char *username, const char *shadow_pass)
172 : {
173 : scram_state *state;
174 : bool got_verifier;
175 :
176 0 : state = (scram_state *) palloc0(sizeof(scram_state));
177 0 : state->state = SCRAM_AUTH_INIT;
178 0 : state->username = username;
179 :
180 : /*
181 : * Parse the stored password verifier.
182 : */
183 0 : if (shadow_pass)
184 : {
185 0 : int password_type = get_password_type(shadow_pass);
186 :
187 0 : if (password_type == PASSWORD_TYPE_SCRAM_SHA_256)
188 : {
189 0 : if (parse_scram_verifier(shadow_pass, &state->iterations, &state->salt,
190 0 : state->StoredKey, state->ServerKey))
191 0 : got_verifier = true;
192 : else
193 : {
194 : /*
195 : * The password looked like a SCRAM verifier, but could not be
196 : * parsed.
197 : */
198 0 : ereport(LOG,
199 : (errmsg("invalid SCRAM verifier for user \"%s\"",
200 : username)));
201 0 : got_verifier = false;
202 : }
203 : }
204 : else
205 : {
206 : /*
207 : * The user doesn't have SCRAM verifier. (You cannot do SCRAM
208 : * authentication with an MD5 hash.)
209 : */
210 0 : state->logdetail = psprintf(_("User \"%s\" does not have a valid SCRAM verifier."),
211 : state->username);
212 0 : got_verifier = false;
213 : }
214 : }
215 : else
216 : {
217 : /*
218 : * The caller requested us to perform a dummy authentication. This is
219 : * considered normal, since the caller requested it, so don't set log
220 : * detail.
221 : */
222 0 : got_verifier = false;
223 : }
224 :
225 : /*
226 : * If the user did not have a valid SCRAM verifier, we still go through
227 : * the motions with a mock one, and fail as if the client supplied an
228 : * incorrect password. This is to avoid revealing information to an
229 : * attacker.
230 : */
231 0 : if (!got_verifier)
232 : {
233 0 : mock_scram_verifier(username, &state->iterations, &state->salt,
234 0 : state->StoredKey, state->ServerKey);
235 0 : state->doomed = true;
236 : }
237 :
238 0 : return state;
239 : }
240 :
241 : /*
242 : * Continue a SCRAM authentication exchange.
243 : *
244 : * 'input' is the SCRAM payload sent by the client. On the first call,
245 : * 'input' contains the "Initial Client Response" that the client sent as
246 : * part of the SASLInitialResponse message, or NULL if no Initial Client
247 : * Response was given. (The SASL specification distinguishes between an
248 : * empty response and non-existing one.) On subsequent calls, 'input'
249 : * cannot be NULL. For convenience in this function, the caller must
250 : * ensure that there is a null terminator at input[inputlen].
251 : *
252 : * The next message to send to client is saved in 'output', for a length
253 : * of 'outputlen'. In the case of an error, optionally store a palloc'd
254 : * string at *logdetail that will be sent to the postmaster log (but not
255 : * the client).
256 : */
257 : int
258 0 : pg_be_scram_exchange(void *opaq, char *input, int inputlen,
259 : char **output, int *outputlen, char **logdetail)
260 : {
261 0 : scram_state *state = (scram_state *) opaq;
262 : int result;
263 :
264 0 : *output = NULL;
265 :
266 : /*
267 : * If the client didn't include an "Initial Client Response" in the
268 : * SASLInitialResponse message, send an empty challenge, to which the
269 : * client will respond with the same data that usually comes in the
270 : * Initial Client Response.
271 : */
272 0 : if (input == NULL)
273 : {
274 0 : Assert(state->state == SCRAM_AUTH_INIT);
275 :
276 0 : *output = pstrdup("");
277 0 : *outputlen = 0;
278 0 : return SASL_EXCHANGE_CONTINUE;
279 : }
280 :
281 : /*
282 : * Check that the input length agrees with the string length of the input.
283 : * We can ignore inputlen after this.
284 : */
285 0 : if (inputlen == 0)
286 0 : ereport(ERROR,
287 : (errcode(ERRCODE_PROTOCOL_VIOLATION),
288 : errmsg("malformed SCRAM message"),
289 : errdetail("The message is empty.")));
290 0 : if (inputlen != strlen(input))
291 0 : ereport(ERROR,
292 : (errcode(ERRCODE_PROTOCOL_VIOLATION),
293 : errmsg("malformed SCRAM message"),
294 : errdetail("Message length does not match input length.")));
295 :
296 0 : switch (state->state)
297 : {
298 : case SCRAM_AUTH_INIT:
299 :
300 : /*
301 : * Initialization phase. Receive the first message from client
302 : * and be sure that it parsed correctly. Then send the challenge
303 : * to the client.
304 : */
305 0 : read_client_first_message(state, input);
306 :
307 : /* prepare message to send challenge */
308 0 : *output = build_server_first_message(state);
309 :
310 0 : state->state = SCRAM_AUTH_SALT_SENT;
311 0 : result = SASL_EXCHANGE_CONTINUE;
312 0 : break;
313 :
314 : case SCRAM_AUTH_SALT_SENT:
315 :
316 : /*
317 : * Final phase for the server. Receive the response to the
318 : * challenge previously sent, verify, and let the client know that
319 : * everything went well (or not).
320 : */
321 0 : read_client_final_message(state, input);
322 :
323 0 : if (!verify_final_nonce(state))
324 0 : ereport(ERROR,
325 : (errcode(ERRCODE_PROTOCOL_VIOLATION),
326 : errmsg("invalid SCRAM response"),
327 : errdetail("Nonce does not match.")));
328 :
329 : /*
330 : * Now check the final nonce and the client proof.
331 : *
332 : * If we performed a "mock" authentication that we knew would fail
333 : * from the get go, this is where we fail.
334 : *
335 : * The SCRAM specification includes an error code,
336 : * "invalid-proof", for authentication failure, but it also allows
337 : * erroring out in an application-specific way. We choose to do
338 : * the latter, so that the error message for invalid password is
339 : * the same for all authentication methods. The caller will call
340 : * ereport(), when we return SASL_EXCHANGE_FAILURE with no output.
341 : *
342 : * NB: the order of these checks is intentional. We calculate the
343 : * client proof even in a mock authentication, even though it's
344 : * bound to fail, to thwart timing attacks to determine if a role
345 : * with the given name exists or not.
346 : */
347 0 : if (!verify_client_proof(state) || state->doomed)
348 : {
349 0 : result = SASL_EXCHANGE_FAILURE;
350 0 : break;
351 : }
352 :
353 : /* Build final message for client */
354 0 : *output = build_server_final_message(state);
355 :
356 : /* Success! */
357 0 : result = SASL_EXCHANGE_SUCCESS;
358 0 : state->state = SCRAM_AUTH_FINISHED;
359 0 : break;
360 :
361 : default:
362 0 : elog(ERROR, "invalid SCRAM exchange state");
363 : result = SASL_EXCHANGE_FAILURE;
364 : }
365 :
366 0 : if (result == SASL_EXCHANGE_FAILURE && state->logdetail && logdetail)
367 0 : *logdetail = state->logdetail;
368 :
369 0 : if (*output)
370 0 : *outputlen = strlen(*output);
371 :
372 0 : return result;
373 : }
374 :
375 : /*
376 : * Construct a verifier string for SCRAM, stored in pg_authid.rolpassword.
377 : *
378 : * The result is palloc'd, so caller is responsible for freeing it.
379 : */
380 : char *
381 2 : pg_be_scram_build_verifier(const char *password)
382 : {
383 2 : char *prep_password = NULL;
384 : pg_saslprep_rc rc;
385 : char saltbuf[SCRAM_DEFAULT_SALT_LEN];
386 : char *result;
387 :
388 : /*
389 : * Normalize the password with SASLprep. If that doesn't work, because
390 : * the password isn't valid UTF-8 or contains prohibited characters, just
391 : * proceed with the original password. (See comments at top of file.)
392 : */
393 2 : rc = pg_saslprep(password, &prep_password);
394 2 : if (rc == SASLPREP_SUCCESS)
395 2 : password = (const char *) prep_password;
396 :
397 : /* Generate random salt */
398 2 : if (!pg_backend_random(saltbuf, SCRAM_DEFAULT_SALT_LEN))
399 0 : ereport(ERROR,
400 : (errcode(ERRCODE_INTERNAL_ERROR),
401 : errmsg("could not generate random salt")));
402 :
403 2 : result = scram_build_verifier(saltbuf, SCRAM_DEFAULT_SALT_LEN,
404 : SCRAM_DEFAULT_ITERATIONS, password);
405 :
406 2 : if (prep_password)
407 2 : pfree(prep_password);
408 :
409 2 : return result;
410 : }
411 :
412 : /*
413 : * Verify a plaintext password against a SCRAM verifier. This is used when
414 : * performing plaintext password authentication for a user that has a SCRAM
415 : * verifier stored in pg_authid.
416 : */
417 : bool
418 2 : scram_verify_plain_password(const char *username, const char *password,
419 : const char *verifier)
420 : {
421 : char *encoded_salt;
422 : char *salt;
423 : int saltlen;
424 : int iterations;
425 : uint8 salted_password[SCRAM_KEY_LEN];
426 : uint8 stored_key[SCRAM_KEY_LEN];
427 : uint8 server_key[SCRAM_KEY_LEN];
428 : uint8 computed_key[SCRAM_KEY_LEN];
429 2 : char *prep_password = NULL;
430 : pg_saslprep_rc rc;
431 :
432 2 : if (!parse_scram_verifier(verifier, &iterations, &encoded_salt,
433 : stored_key, server_key))
434 : {
435 : /*
436 : * The password looked like a SCRAM verifier, but could not be parsed.
437 : */
438 0 : ereport(LOG,
439 : (errmsg("invalid SCRAM verifier for user \"%s\"", username)));
440 0 : return false;
441 : }
442 :
443 2 : salt = palloc(pg_b64_dec_len(strlen(encoded_salt)));
444 2 : saltlen = pg_b64_decode(encoded_salt, strlen(encoded_salt), salt);
445 2 : if (saltlen == -1)
446 : {
447 0 : ereport(LOG,
448 : (errmsg("invalid SCRAM verifier for user \"%s\"", username)));
449 0 : return false;
450 : }
451 :
452 : /* Normalize the password */
453 2 : rc = pg_saslprep(password, &prep_password);
454 2 : if (rc == SASLPREP_SUCCESS)
455 2 : password = prep_password;
456 :
457 : /* Compute Server Key based on the user-supplied plaintext password */
458 2 : scram_SaltedPassword(password, salt, saltlen, iterations, salted_password);
459 2 : scram_ServerKey(salted_password, computed_key);
460 :
461 2 : if (prep_password)
462 2 : pfree(prep_password);
463 :
464 : /*
465 : * Compare the verifier's Server Key with the one computed from the
466 : * user-supplied password.
467 : */
468 2 : return memcmp(computed_key, server_key, SCRAM_KEY_LEN) == 0;
469 : }
470 :
471 :
472 : /*
473 : * Parse and validate format of given SCRAM verifier.
474 : *
475 : * Returns true if the SCRAM verifier has been parsed, and false otherwise.
476 : */
477 : static bool
478 2 : parse_scram_verifier(const char *verifier, int *iterations, char **salt,
479 : uint8 *stored_key, uint8 *server_key)
480 : {
481 : char *v;
482 : char *p;
483 : char *scheme_str;
484 : char *salt_str;
485 : char *iterations_str;
486 : char *storedkey_str;
487 : char *serverkey_str;
488 : int decoded_len;
489 : char *decoded_salt_buf;
490 :
491 : /*
492 : * The verifier is of form:
493 : *
494 : * SCRAM-SHA-256$<iterations>:<salt>$<storedkey>:<serverkey>
495 : */
496 2 : v = pstrdup(verifier);
497 2 : if ((scheme_str = strtok(v, "$")) == NULL)
498 0 : goto invalid_verifier;
499 2 : if ((iterations_str = strtok(NULL, ":")) == NULL)
500 0 : goto invalid_verifier;
501 2 : if ((salt_str = strtok(NULL, "$")) == NULL)
502 0 : goto invalid_verifier;
503 2 : if ((storedkey_str = strtok(NULL, ":")) == NULL)
504 0 : goto invalid_verifier;
505 2 : if ((serverkey_str = strtok(NULL, "")) == NULL)
506 0 : goto invalid_verifier;
507 :
508 : /* Parse the fields */
509 2 : if (strcmp(scheme_str, "SCRAM-SHA-256") != 0)
510 0 : goto invalid_verifier;
511 :
512 2 : errno = 0;
513 2 : *iterations = strtol(iterations_str, &p, 10);
514 2 : if (*p || errno != 0)
515 : goto invalid_verifier;
516 :
517 : /*
518 : * Verify that the salt is in Base64-encoded format, by decoding it,
519 : * although we return the encoded version to the caller.
520 : */
521 2 : decoded_salt_buf = palloc(pg_b64_dec_len(strlen(salt_str)));
522 2 : decoded_len = pg_b64_decode(salt_str, strlen(salt_str), decoded_salt_buf);
523 2 : if (decoded_len < 0)
524 0 : goto invalid_verifier;
525 2 : *salt = pstrdup(salt_str);
526 :
527 : /*
528 : * Decode StoredKey and ServerKey.
529 : */
530 2 : if (pg_b64_dec_len(strlen(storedkey_str) != SCRAM_KEY_LEN))
531 0 : goto invalid_verifier;
532 2 : decoded_len = pg_b64_decode(storedkey_str, strlen(storedkey_str),
533 : (char *) stored_key);
534 2 : if (decoded_len != SCRAM_KEY_LEN)
535 0 : goto invalid_verifier;
536 :
537 2 : if (pg_b64_dec_len(strlen(serverkey_str) != SCRAM_KEY_LEN))
538 0 : goto invalid_verifier;
539 2 : decoded_len = pg_b64_decode(serverkey_str, strlen(serverkey_str),
540 : (char *) server_key);
541 2 : if (decoded_len != SCRAM_KEY_LEN)
542 0 : goto invalid_verifier;
543 :
544 2 : return true;
545 :
546 : invalid_verifier:
547 0 : pfree(v);
548 0 : *salt = NULL;
549 0 : return false;
550 : }
551 :
552 : static void
553 0 : mock_scram_verifier(const char *username, int *iterations, char **salt,
554 : uint8 *stored_key, uint8 *server_key)
555 : {
556 : char *raw_salt;
557 : char *encoded_salt;
558 : int encoded_len;
559 :
560 : /* Generate deterministic salt */
561 0 : raw_salt = scram_mock_salt(username);
562 :
563 0 : encoded_salt = (char *) palloc(pg_b64_enc_len(SCRAM_DEFAULT_SALT_LEN) + 1);
564 0 : encoded_len = pg_b64_encode(raw_salt, SCRAM_DEFAULT_SALT_LEN, encoded_salt);
565 0 : encoded_salt[encoded_len] = '\0';
566 :
567 0 : *salt = encoded_salt;
568 0 : *iterations = SCRAM_DEFAULT_ITERATIONS;
569 :
570 : /* StoredKey and ServerKey are not used in a doomed authentication */
571 0 : memset(stored_key, 0, SCRAM_KEY_LEN);
572 0 : memset(server_key, 0, SCRAM_KEY_LEN);
573 0 : }
574 :
575 : /*
576 : * Read the value in a given SCRAM exchange message for given attribute.
577 : */
578 : static char *
579 0 : read_attr_value(char **input, char attr)
580 : {
581 0 : char *begin = *input;
582 : char *end;
583 :
584 0 : if (*begin != attr)
585 0 : ereport(ERROR,
586 : (errcode(ERRCODE_PROTOCOL_VIOLATION),
587 : errmsg("malformed SCRAM message"),
588 : errdetail("Expected attribute \"%c\" but found \"%s\".",
589 : attr, sanitize_char(*begin))));
590 0 : begin++;
591 :
592 0 : if (*begin != '=')
593 0 : ereport(ERROR,
594 : (errcode(ERRCODE_PROTOCOL_VIOLATION),
595 : errmsg("malformed SCRAM message"),
596 : errdetail("Expected character \"=\" for attribute \"%c\".", attr)));
597 0 : begin++;
598 :
599 0 : end = begin;
600 0 : while (*end && *end != ',')
601 0 : end++;
602 :
603 0 : if (*end)
604 : {
605 0 : *end = '\0';
606 0 : *input = end + 1;
607 : }
608 : else
609 0 : *input = end;
610 :
611 0 : return begin;
612 : }
613 :
614 : static bool
615 0 : is_scram_printable(char *p)
616 : {
617 : /*------
618 : * Printable characters, as defined by SCRAM spec: (RFC 5802)
619 : *
620 : * printable = %x21-2B / %x2D-7E
621 : * ;; Printable ASCII except ",".
622 : * ;; Note that any "printable" is also
623 : * ;; a valid "value".
624 : *------
625 : */
626 0 : for (; *p; p++)
627 : {
628 0 : if (*p < 0x21 || *p > 0x7E || *p == 0x2C /* comma */ )
629 0 : return false;
630 : }
631 0 : return true;
632 : }
633 :
634 : /*
635 : * Convert an arbitrary byte to printable form. For error messages.
636 : *
637 : * If it's a printable ASCII character, print it as a single character.
638 : * otherwise, print it in hex.
639 : *
640 : * The returned pointer points to a static buffer.
641 : */
642 : static char *
643 0 : sanitize_char(char c)
644 : {
645 : static char buf[5];
646 :
647 0 : if (c >= 0x21 && c <= 0x7E)
648 0 : snprintf(buf, sizeof(buf), "'%c'", c);
649 : else
650 0 : snprintf(buf, sizeof(buf), "0x%02x", (unsigned char) c);
651 0 : return buf;
652 : }
653 :
654 : /*
655 : * Read the next attribute and value in a SCRAM exchange message.
656 : *
657 : * Returns NULL if there is attribute.
658 : */
659 : static char *
660 0 : read_any_attr(char **input, char *attr_p)
661 : {
662 0 : char *begin = *input;
663 : char *end;
664 0 : char attr = *begin;
665 :
666 : /*------
667 : * attr-val = ALPHA "=" value
668 : * ;; Generic syntax of any attribute sent
669 : * ;; by server or client
670 : *------
671 : */
672 0 : if (!((attr >= 'A' && attr <= 'Z') ||
673 0 : (attr >= 'a' && attr <= 'z')))
674 0 : ereport(ERROR,
675 : (errcode(ERRCODE_PROTOCOL_VIOLATION),
676 : errmsg("malformed SCRAM message"),
677 : errdetail("Attribute expected, but found invalid character \"%s\".",
678 : sanitize_char(attr))));
679 0 : if (attr_p)
680 0 : *attr_p = attr;
681 0 : begin++;
682 :
683 0 : if (*begin != '=')
684 0 : ereport(ERROR,
685 : (errcode(ERRCODE_PROTOCOL_VIOLATION),
686 : errmsg("malformed SCRAM message"),
687 : errdetail("Expected character \"=\" for attribute \"%c\".", attr)));
688 0 : begin++;
689 :
690 0 : end = begin;
691 0 : while (*end && *end != ',')
692 0 : end++;
693 :
694 0 : if (*end)
695 : {
696 0 : *end = '\0';
697 0 : *input = end + 1;
698 : }
699 : else
700 0 : *input = end;
701 :
702 0 : return begin;
703 : }
704 :
705 : /*
706 : * Read and parse the first message from client in the context of a SCRAM
707 : * authentication exchange message.
708 : *
709 : * At this stage, any errors will be reported directly with ereport(ERROR).
710 : */
711 : static void
712 0 : read_client_first_message(scram_state *state, char *input)
713 : {
714 0 : input = pstrdup(input);
715 :
716 : /*------
717 : * The syntax for the client-first-message is: (RFC 5802)
718 : *
719 : * saslname = 1*(value-safe-char / "=2C" / "=3D")
720 : * ;; Conforms to <value>.
721 : *
722 : * authzid = "a=" saslname
723 : * ;; Protocol specific.
724 : *
725 : * cb-name = 1*(ALPHA / DIGIT / "." / "-")
726 : * ;; See RFC 5056, Section 7.
727 : * ;; E.g., "tls-server-end-point" or
728 : * ;; "tls-unique".
729 : *
730 : * gs2-cbind-flag = ("p=" cb-name) / "n" / "y"
731 : * ;; "n" -> client doesn't support channel binding.
732 : * ;; "y" -> client does support channel binding
733 : * ;; but thinks the server does not.
734 : * ;; "p" -> client requires channel binding.
735 : * ;; The selected channel binding follows "p=".
736 : *
737 : * gs2-header = gs2-cbind-flag "," [ authzid ] ","
738 : * ;; GS2 header for SCRAM
739 : * ;; (the actual GS2 header includes an optional
740 : * ;; flag to indicate that the GSS mechanism is not
741 : * ;; "standard", but since SCRAM is "standard", we
742 : * ;; don't include that flag).
743 : *
744 : * username = "n=" saslname
745 : * ;; Usernames are prepared using SASLprep.
746 : *
747 : * reserved-mext = "m=" 1*(value-char)
748 : * ;; Reserved for signaling mandatory extensions.
749 : * ;; The exact syntax will be defined in
750 : * ;; the future.
751 : *
752 : * nonce = "r=" c-nonce [s-nonce]
753 : * ;; Second part provided by server.
754 : *
755 : * c-nonce = printable
756 : *
757 : * client-first-message-bare =
758 : * [reserved-mext ","]
759 : * username "," nonce ["," extensions]
760 : *
761 : * client-first-message =
762 : * gs2-header client-first-message-bare
763 : *
764 : * For example:
765 : * n,,n=user,r=fyko+d2lbbFgONRv9qkxdawL
766 : *
767 : * The "n,," in the beginning means that the client doesn't support
768 : * channel binding, and no authzid is given. "n=user" is the username.
769 : * However, in PostgreSQL the username is sent in the startup packet, and
770 : * the username in the SCRAM exchange is ignored. libpq always sends it
771 : * as an empty string. The last part, "r=fyko+d2lbbFgONRv9qkxdawL" is
772 : * the client nonce.
773 : *------
774 : */
775 :
776 : /* read gs2-cbind-flag */
777 0 : switch (*input)
778 : {
779 : case 'n':
780 : /* Client does not support channel binding */
781 0 : input++;
782 0 : break;
783 : case 'y':
784 : /* Client supports channel binding, but we're not doing it today */
785 0 : input++;
786 0 : break;
787 : case 'p':
788 :
789 : /*
790 : * Client requires channel binding. We don't support it.
791 : *
792 : * RFC 5802 specifies a particular error code,
793 : * e=server-does-support-channel-binding, for this. But it can
794 : * only be sent in the server-final message, and we don't want to
795 : * go through the motions of the authentication, knowing it will
796 : * fail, just to send that error message.
797 : */
798 0 : ereport(ERROR,
799 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
800 : errmsg("client requires SCRAM channel binding, but it is not supported")));
801 : default:
802 0 : ereport(ERROR,
803 : (errcode(ERRCODE_PROTOCOL_VIOLATION),
804 : errmsg("malformed SCRAM message"),
805 : errdetail("Unexpected channel-binding flag \"%s\".",
806 : sanitize_char(*input))));
807 : }
808 0 : if (*input != ',')
809 0 : ereport(ERROR,
810 : (errcode(ERRCODE_PROTOCOL_VIOLATION),
811 : errmsg("malformed SCRAM message"),
812 : errdetail("Comma expected, but found character \"%s\".",
813 : sanitize_char(*input))));
814 0 : input++;
815 :
816 : /*
817 : * Forbid optional authzid (authorization identity). We don't support it.
818 : */
819 0 : if (*input == 'a')
820 0 : ereport(ERROR,
821 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
822 : errmsg("client uses authorization identity, but it is not supported")));
823 0 : if (*input != ',')
824 0 : ereport(ERROR,
825 : (errcode(ERRCODE_PROTOCOL_VIOLATION),
826 : errmsg("malformed SCRAM message"),
827 : errdetail("Unexpected attribute \"%s\" in client-first-message.",
828 : sanitize_char(*input))));
829 0 : input++;
830 :
831 0 : state->client_first_message_bare = pstrdup(input);
832 :
833 : /*
834 : * Any mandatory extensions would go here. We don't support any.
835 : *
836 : * RFC 5802 specifies error code "e=extensions-not-supported" for this,
837 : * but it can only be sent in the server-final message. We prefer to fail
838 : * immediately (which the RFC also allows).
839 : */
840 0 : if (*input == 'm')
841 0 : ereport(ERROR,
842 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
843 : errmsg("client requires an unsupported SCRAM extension")));
844 :
845 : /*
846 : * Read username. Note: this is ignored. We use the username from the
847 : * startup message instead, still it is kept around if provided as it
848 : * proves to be useful for debugging purposes.
849 : */
850 0 : state->client_username = read_attr_value(&input, 'n');
851 :
852 : /* read nonce and check that it is made of only printable characters */
853 0 : state->client_nonce = read_attr_value(&input, 'r');
854 0 : if (!is_scram_printable(state->client_nonce))
855 0 : ereport(ERROR,
856 : (errcode(ERRCODE_PROTOCOL_VIOLATION),
857 : errmsg("non-printable characters in SCRAM nonce")));
858 :
859 : /*
860 : * There can be any number of optional extensions after this. We don't
861 : * support any extensions, so ignore them.
862 : */
863 0 : while (*input != '\0')
864 0 : read_any_attr(&input, NULL);
865 :
866 : /* success! */
867 0 : }
868 :
869 : /*
870 : * Verify the final nonce contained in the last message received from
871 : * client in an exchange.
872 : */
873 : static bool
874 0 : verify_final_nonce(scram_state *state)
875 : {
876 0 : int client_nonce_len = strlen(state->client_nonce);
877 0 : int server_nonce_len = strlen(state->server_nonce);
878 0 : int final_nonce_len = strlen(state->client_final_nonce);
879 :
880 0 : if (final_nonce_len != client_nonce_len + server_nonce_len)
881 0 : return false;
882 0 : if (memcmp(state->client_final_nonce, state->client_nonce, client_nonce_len) != 0)
883 0 : return false;
884 0 : if (memcmp(state->client_final_nonce + client_nonce_len, state->server_nonce, server_nonce_len) != 0)
885 0 : return false;
886 :
887 0 : return true;
888 : }
889 :
890 : /*
891 : * Verify the client proof contained in the last message received from
892 : * client in an exchange.
893 : */
894 : static bool
895 0 : verify_client_proof(scram_state *state)
896 : {
897 : uint8 ClientSignature[SCRAM_KEY_LEN];
898 : uint8 ClientKey[SCRAM_KEY_LEN];
899 : uint8 client_StoredKey[SCRAM_KEY_LEN];
900 : scram_HMAC_ctx ctx;
901 : int i;
902 :
903 : /* calculate ClientSignature */
904 0 : scram_HMAC_init(&ctx, state->StoredKey, SCRAM_KEY_LEN);
905 0 : scram_HMAC_update(&ctx,
906 0 : state->client_first_message_bare,
907 0 : strlen(state->client_first_message_bare));
908 0 : scram_HMAC_update(&ctx, ",", 1);
909 0 : scram_HMAC_update(&ctx,
910 0 : state->server_first_message,
911 0 : strlen(state->server_first_message));
912 0 : scram_HMAC_update(&ctx, ",", 1);
913 0 : scram_HMAC_update(&ctx,
914 0 : state->client_final_message_without_proof,
915 0 : strlen(state->client_final_message_without_proof));
916 0 : scram_HMAC_final(ClientSignature, &ctx);
917 :
918 : /* Extract the ClientKey that the client calculated from the proof */
919 0 : for (i = 0; i < SCRAM_KEY_LEN; i++)
920 0 : ClientKey[i] = state->ClientProof[i] ^ ClientSignature[i];
921 :
922 : /* Hash it one more time, and compare with StoredKey */
923 0 : scram_H(ClientKey, SCRAM_KEY_LEN, client_StoredKey);
924 :
925 0 : if (memcmp(client_StoredKey, state->StoredKey, SCRAM_KEY_LEN) != 0)
926 0 : return false;
927 :
928 0 : return true;
929 : }
930 :
931 : /*
932 : * Build the first server-side message sent to the client in a SCRAM
933 : * communication exchange.
934 : */
935 : static char *
936 0 : build_server_first_message(scram_state *state)
937 : {
938 : /*------
939 : * The syntax for the server-first-message is: (RFC 5802)
940 : *
941 : * server-first-message =
942 : * [reserved-mext ","] nonce "," salt ","
943 : * iteration-count ["," extensions]
944 : *
945 : * nonce = "r=" c-nonce [s-nonce]
946 : * ;; Second part provided by server.
947 : *
948 : * c-nonce = printable
949 : *
950 : * s-nonce = printable
951 : *
952 : * salt = "s=" base64
953 : *
954 : * iteration-count = "i=" posit-number
955 : * ;; A positive number.
956 : *
957 : * Example:
958 : *
959 : * r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,s=QSXCR+Q6sek8bf92,i=4096
960 : *------
961 : */
962 :
963 : /*
964 : * Per the spec, the nonce may consist of any printable ASCII characters.
965 : * For convenience, however, we don't use the whole range available,
966 : * rather, we generate some random bytes, and base64 encode them.
967 : */
968 : char raw_nonce[SCRAM_RAW_NONCE_LEN];
969 : int encoded_len;
970 :
971 0 : if (!pg_backend_random(raw_nonce, SCRAM_RAW_NONCE_LEN))
972 0 : ereport(ERROR,
973 : (errcode(ERRCODE_INTERNAL_ERROR),
974 : errmsg("could not generate random nonce")));
975 :
976 0 : state->server_nonce = palloc(pg_b64_enc_len(SCRAM_RAW_NONCE_LEN) + 1);
977 0 : encoded_len = pg_b64_encode(raw_nonce, SCRAM_RAW_NONCE_LEN, state->server_nonce);
978 0 : state->server_nonce[encoded_len] = '\0';
979 :
980 0 : state->server_first_message =
981 0 : psprintf("r=%s%s,s=%s,i=%u",
982 : state->client_nonce, state->server_nonce,
983 : state->salt, state->iterations);
984 :
985 0 : return pstrdup(state->server_first_message);
986 : }
987 :
988 :
989 : /*
990 : * Read and parse the final message received from client.
991 : */
992 : static void
993 0 : read_client_final_message(scram_state *state, char *input)
994 : {
995 : char attr;
996 : char *channel_binding;
997 : char *value;
998 : char *begin,
999 : *proof;
1000 : char *p;
1001 : char *client_proof;
1002 :
1003 0 : begin = p = pstrdup(input);
1004 :
1005 : /*------
1006 : * The syntax for the server-first-message is: (RFC 5802)
1007 : *
1008 : * gs2-header = gs2-cbind-flag "," [ authzid ] ","
1009 : * ;; GS2 header for SCRAM
1010 : * ;; (the actual GS2 header includes an optional
1011 : * ;; flag to indicate that the GSS mechanism is not
1012 : * ;; "standard", but since SCRAM is "standard", we
1013 : * ;; don't include that flag).
1014 : *
1015 : * cbind-input = gs2-header [ cbind-data ]
1016 : * ;; cbind-data MUST be present for
1017 : * ;; gs2-cbind-flag of "p" and MUST be absent
1018 : * ;; for "y" or "n".
1019 : *
1020 : * channel-binding = "c=" base64
1021 : * ;; base64 encoding of cbind-input.
1022 : *
1023 : * proof = "p=" base64
1024 : *
1025 : * client-final-message-without-proof =
1026 : * channel-binding "," nonce [","
1027 : * extensions]
1028 : *
1029 : * client-final-message =
1030 : * client-final-message-without-proof "," proof
1031 : *------
1032 : */
1033 :
1034 : /*
1035 : * Read channel-binding. We don't support channel binding, so it's
1036 : * expected to always be "biws", which is "n,,", base64-encoded.
1037 : */
1038 0 : channel_binding = read_attr_value(&p, 'c');
1039 0 : if (strcmp(channel_binding, "biws") != 0)
1040 0 : ereport(ERROR,
1041 : (errcode(ERRCODE_PROTOCOL_VIOLATION),
1042 : (errmsg("unexpected SCRAM channel-binding attribute in client-final-message"))));
1043 0 : state->client_final_nonce = read_attr_value(&p, 'r');
1044 :
1045 : /* ignore optional extensions */
1046 : do
1047 : {
1048 0 : proof = p - 1;
1049 0 : value = read_any_attr(&p, &attr);
1050 0 : } while (attr != 'p');
1051 :
1052 0 : client_proof = palloc(pg_b64_dec_len(strlen(value)));
1053 0 : if (pg_b64_decode(value, strlen(value), client_proof) != SCRAM_KEY_LEN)
1054 0 : ereport(ERROR,
1055 : (errcode(ERRCODE_PROTOCOL_VIOLATION),
1056 : errmsg("malformed SCRAM message"),
1057 : errdetail("Malformed proof in client-final-message.")));
1058 0 : memcpy(state->ClientProof, client_proof, SCRAM_KEY_LEN);
1059 0 : pfree(client_proof);
1060 :
1061 0 : if (*p != '\0')
1062 0 : ereport(ERROR,
1063 : (errcode(ERRCODE_PROTOCOL_VIOLATION),
1064 : errmsg("malformed SCRAM message"),
1065 : errdetail("Garbage found at the end of client-final-message.")));
1066 :
1067 0 : state->client_final_message_without_proof = palloc(proof - begin + 1);
1068 0 : memcpy(state->client_final_message_without_proof, input, proof - begin);
1069 0 : state->client_final_message_without_proof[proof - begin] = '\0';
1070 0 : }
1071 :
1072 : /*
1073 : * Build the final server-side message of an exchange.
1074 : */
1075 : static char *
1076 0 : build_server_final_message(scram_state *state)
1077 : {
1078 : uint8 ServerSignature[SCRAM_KEY_LEN];
1079 : char *server_signature_base64;
1080 : int siglen;
1081 : scram_HMAC_ctx ctx;
1082 :
1083 : /* calculate ServerSignature */
1084 0 : scram_HMAC_init(&ctx, state->ServerKey, SCRAM_KEY_LEN);
1085 0 : scram_HMAC_update(&ctx,
1086 0 : state->client_first_message_bare,
1087 0 : strlen(state->client_first_message_bare));
1088 0 : scram_HMAC_update(&ctx, ",", 1);
1089 0 : scram_HMAC_update(&ctx,
1090 0 : state->server_first_message,
1091 0 : strlen(state->server_first_message));
1092 0 : scram_HMAC_update(&ctx, ",", 1);
1093 0 : scram_HMAC_update(&ctx,
1094 0 : state->client_final_message_without_proof,
1095 0 : strlen(state->client_final_message_without_proof));
1096 0 : scram_HMAC_final(ServerSignature, &ctx);
1097 :
1098 0 : server_signature_base64 = palloc(pg_b64_enc_len(SCRAM_KEY_LEN) + 1);
1099 0 : siglen = pg_b64_encode((const char *) ServerSignature,
1100 : SCRAM_KEY_LEN, server_signature_base64);
1101 0 : server_signature_base64[siglen] = '\0';
1102 :
1103 : /*------
1104 : * The syntax for the server-final-message is: (RFC 5802)
1105 : *
1106 : * verifier = "v=" base64
1107 : * ;; base-64 encoded ServerSignature.
1108 : *
1109 : * server-final-message = (server-error / verifier)
1110 : * ["," extensions]
1111 : *
1112 : *------
1113 : */
1114 0 : return psprintf("v=%s", server_signature_base64);
1115 : }
1116 :
1117 :
1118 : /*
1119 : * Determinisitcally generate salt for mock authentication, using a SHA256
1120 : * hash based on the username and a cluster-level secret key. Returns a
1121 : * pointer to a static buffer of size SCRAM_DEFAULT_SALT_LEN.
1122 : */
1123 : static char *
1124 0 : scram_mock_salt(const char *username)
1125 : {
1126 : pg_sha256_ctx ctx;
1127 : static uint8 sha_digest[PG_SHA256_DIGEST_LENGTH];
1128 0 : char *mock_auth_nonce = GetMockAuthenticationNonce();
1129 :
1130 : /*
1131 : * Generate salt using a SHA256 hash of the username and the cluster's
1132 : * mock authentication nonce. (This works as long as the salt length is
1133 : * not larger the SHA256 digest length. If the salt is smaller, the caller
1134 : * will just ignore the extra data.)
1135 : */
1136 : StaticAssertStmt(PG_SHA256_DIGEST_LENGTH >= SCRAM_DEFAULT_SALT_LEN,
1137 : "salt length greater than SHA256 digest length");
1138 :
1139 0 : pg_sha256_init(&ctx);
1140 0 : pg_sha256_update(&ctx, (uint8 *) username, strlen(username));
1141 0 : pg_sha256_update(&ctx, (uint8 *) mock_auth_nonce, MOCK_AUTH_NONCE_LEN);
1142 0 : pg_sha256_final(&ctx, sha_digest);
1143 :
1144 0 : return (char *) sha_digest;
1145 : }
|