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