1 /* SCRAM-SHA-1/SHA-2 SASL plugin
2 * Alexey Melnikov
3 */
4 /*
5 * Copyright (c) 2009-2016 Carnegie Mellon University. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
17 * distribution.
18 *
19 * 3. The name "Carnegie Mellon University" must not be used to
20 * endorse or promote products derived from this software without
21 * prior written permission. For permission or any other legal
22 * details, please contact
23 * Carnegie Mellon University
24 * Center for Technology Transfer and Enterprise Creation
25 * 4615 Forbes Avenue
26 * Suite 302
27 * Pittsburgh, PA 15213
28 * (412) 268-7393, fax: (412) 268-7395
29 * innovation@andrew.cmu.edu
30 *
31 * 4. Redistributions of any form whatsoever must retain the following
32 * acknowledgment:
33 * "This product includes software developed by Computing Services
34 * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
35 *
36 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
37 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
38 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
39 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
40 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
41 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
42 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
43 */
44
45 #include <config.h>
46
47 #include <string.h>
48 #include <stdlib.h>
49 #include <stdio.h>
50 #ifndef macintosh
51 #include <sys/stat.h>
52 #endif
53 #include <fcntl.h>
54 #include <errno.h>
55
56 #include <sasl.h>
57 #include <saslplug.h>
58 #include <saslutil.h>
59
60 #include "plugin_common.h"
61
62 #ifdef macintosh
63 #include <sasl_scram_plugin_decl.h>
64 #endif
65
66 #include <openssl/sha.h>
67 #include <openssl/evp.h>
68 #include <openssl/hmac.h>
69
70 /***************************** Common Section *****************************/
71
72 #define NONCE_SIZE (32) /* arbitrary */
73 #define SALT_SIZE (16) /* arbitrary */
74
75 /* TODO: make this a configurable option? */
76 #define DEFAULT_ITERATION_COUNTER 4096
77 #define MIN_ITERATION_COUNTER 4096
78
79 #define MAX_ITERATION_COUNTER 0x10000
80
81 /* maximum length of the iteration_counter (as a string). Assume it is 32bits */
82 #define ITERATION_COUNTER_BUF_LEN 20
83
84 #define BASE64_LEN(size) (((size) / 3 * 4) + (((size) % 3) ? 4 : 0))
85
86 #define MAX_CLIENTIN_LEN 2048
87 #define MAX_SERVERIN_LEN 2048
88
89 #define STRINGIZE(x) #x
90 #define MAX_CLIENTIN_LEN_STR STRINGIZE((MAX_CLIENTIN_LEN))
91 #define MAX_SERVERIN_LEN_STR STRINGIZE((MAX_SERVERIN_LEN))
92
93 #define CLIENT_KEY_CONSTANT "Client Key"
94 #define SERVER_KEY_CONSTANT "Server Key"
95 #define CLIENT_KEY_CONSTANT_LEN sizeof(CLIENT_KEY_CONSTANT)-1
96 #define SERVER_KEY_CONSTANT_LEN sizeof(SERVER_KEY_CONSTANT)-1
97
98 #define SCRAM_CB_FLAG_MASK 0x0F
99 #define SCRAM_CB_FLAG_N 0x00
100 #define SCRAM_CB_FLAG_P 0x01
101 #define SCRAM_CB_FLAG_Y 0x02
102
103 #ifdef SCRAM_DEBUG
104 #define PRINT_HASH(func,hash,size) print_hash(func,hash,size)
105 #else
106 #define PRINT_HASH(func,hash,size)
107 #endif
108
109 /* NB: A temporary mapping for "internal errors". It would be better to add
110 a new SASL error code for that */
111 #define SASL_SCRAM_INTERNAL SASL_NOMEM
112
113
114 /* Holds the core salt to avoid regenerating salt each auth. */
115 static unsigned char g_salt_key[SALT_SIZE];
116
117 /* Note that currently only SHA-* variants are supported! */
118 static const char *
scram_sasl_mech_name(size_t hash_size)119 scram_sasl_mech_name(size_t hash_size)
120 {
121 switch (hash_size) {
122 case 64:
123 return "SCRAM-SHA-512";
124
125 case 48:
126 return "SCRAM-SHA-384";
127
128 case 32:
129 return "SCRAM-SHA-256";
130
131 case 28:
132 return "SCRAM-SHA-224";
133
134 case 20:
135 return "SCRAM-SHA-1";
136 }
137
138 return NULL;
139 }
140
141 /* Convert saslname = 1*(value-safe-char / "=2C" / "=3D") in place.
142 Returns SASL_FAIL if the encoding is invalid, otherwise SASL_OK */
143 static int
decode_saslname(char * buf)144 decode_saslname (char *buf)
145 {
146 char * inp;
147 char * outp;
148
149 inp = outp = buf;
150
151 while (*inp) {
152 if (*inp == '=') {
153 inp++;
154 if (*inp == '\0') {
155 return SASL_FAIL;
156 }
157 if (inp[0] == '2' && inp[1] == 'C') {
158 *outp = ',';
159 inp += 2;
160 } else if (inp[0] == '3' && inp[1] == 'D') {
161 *outp = '=';
162 inp += 2;
163 } else {
164 return SASL_FAIL;
165 }
166 } else {
167 *outp = *inp;
168 inp++;
169 }
170 outp++;
171 }
172
173 *outp = '\0';
174
175 return SASL_OK;
176 }
177
178 /* Convert a username to saslname = 1*(value-safe-char / "=2C" / "=3D")
179 and return an allocated copy.
180 "freeme" contains pointer to the allocated output, or NULL,
181 if encoded_saslname just points to saslname.
182 Returns SASL_NOMEM if can't allocate memory for the output, otherwise SASL_OK */
183 static int
encode_saslname(const char * saslname,const char ** encoded_saslname,char ** freeme)184 encode_saslname (const char *saslname,
185 const char **encoded_saslname,
186 char **freeme)
187 {
188 const char * inp;
189 char * outp;
190 int special_chars = 0;
191
192 /* Found out if anything needs encoding */
193 for (inp = saslname; *inp; inp++) {
194 if (*inp == ',' || *inp == '=') {
195 special_chars++;
196 }
197 }
198
199 if (special_chars == 0) {
200 *encoded_saslname = saslname;
201 *freeme = NULL;
202 return SASL_OK;
203 }
204
205 outp = malloc(strlen(saslname) + special_chars * 2 + 1);
206 *encoded_saslname = outp;
207 *freeme = outp;
208 if (outp == NULL) {
209 return SASL_NOMEM;
210 }
211
212 for (inp = saslname; *inp; inp++) {
213 switch (*inp) {
214 case ',':
215 *outp++ = '=';
216 *outp++ = '2';
217 *outp++ = 'C';
218 break;
219
220 case '=':
221 *outp++ = '=';
222 *outp++ = '3';
223 *outp++ = 'D';
224 break;
225
226 default:
227 *outp++ = *inp;
228 }
229 }
230
231 *outp = '\0';
232
233 return SASL_OK;
234 }
235
236 static char *
create_nonce(const sasl_utils_t * utils,char * buffer,size_t buflen)237 create_nonce(const sasl_utils_t * utils,
238 char *buffer,
239 size_t buflen) /* Including the terminating NUL */
240 {
241 char *intbuf;
242 unsigned int estimated;
243
244 if ((buflen - 1) % 4 != 0) {
245 /* NB: the algorithm below doesn't work for such length.
246 It needs to be adjusted to allocate + 4 bytes,
247 encode the last 4 bytes to a separate buffer and
248 then copy the necessary number of bytes to the end of the output */
249 return NULL;
250 }
251
252 estimated = (unsigned int)((buflen - 1) / 4 * 3);
253 intbuf = (char *) utils->malloc(estimated + 1);
254 if (intbuf == NULL) {
255 return NULL;
256 }
257
258 utils->rand(utils->rpool, intbuf, estimated);
259
260 /* base 64 encode it so it has valid chars */
261 if (utils->encode64(intbuf,
262 estimated,
263 buffer,
264 (unsigned int)buflen,
265 NULL) != SASL_OK) {
266 utils->free(intbuf);
267 return NULL;
268 }
269
270 utils->free(intbuf);
271
272 buffer[buflen-1] = '\0';
273
274 return buffer;
275 }
276
277 #ifdef SCRAM_DEBUG
278 /* Useful for debugging interop issues */
279 static void
print_hash(const char * func,const char * hash,size_t hash_size)280 print_hash (const char * func, const char * hash, size_t hash_size)
281 {
282 int i;
283
284 printf (" HASH in %s:", func);
285 for (i = 0; i < hash_size; i++) {
286 printf (" %.2X", (unsigned char)hash[i]);
287 }
288 printf ("\n");
289 }
290 #endif
291
292
293 /* The result variable need to point to a buffer big enough for the [SHA-*] hash */
294 static void
Hi(const sasl_utils_t * utils,const EVP_MD * md,const char * str,size_t str_len,const char * salt,size_t salt_len,unsigned int iteration_count,char * result)295 Hi (const sasl_utils_t * utils,
296 const EVP_MD *md,
297 const char * str,
298 size_t str_len,
299 const char * salt,
300 size_t salt_len,
301 unsigned int iteration_count,
302 char * result)
303 {
304 char * initial_key = NULL;
305 unsigned int i;
306 char * temp_result;
307 unsigned int hash_len = 0;
308 size_t k, hash_size = EVP_MD_size(md);
309
310 initial_key = utils->malloc(salt_len + 4);
311 memcpy (initial_key, salt, salt_len);
312 initial_key[salt_len] = 0;
313 initial_key[salt_len+1] = 0;
314 initial_key[salt_len+2] = 0;
315 initial_key[salt_len+3] = 1;
316
317 temp_result = utils->malloc(hash_size);
318
319 /* U1 := HMAC(str, salt || INT(1)) */
320
321 if (HMAC(md,
322 (const unsigned char *) str,
323 (int)str_len,
324 (const unsigned char *) initial_key,
325 (int)salt_len + 4,
326 (unsigned char *)result,
327 &hash_len) == NULL) {
328 }
329
330 memcpy(temp_result, result, hash_size);
331
332 PRINT_HASH ("first HMAC in Hi()", temp_result, hash_size);
333
334 /* On each loop iteration j "temp_result" contains Uj,
335 while "result" contains "U1 XOR ... XOR Uj" */
336 for (i = 2; i <= iteration_count; i++) {
337 if (HMAC(md,
338 (const unsigned char *) str,
339 (int)str_len,
340 (const unsigned char *) temp_result,
341 hash_size,
342 (unsigned char *)temp_result,
343 &hash_len) == NULL) {
344 }
345
346 PRINT_HASH ("Hi() HMAC inside loop", temp_result, hash_size);
347
348 for (k = 0; k < hash_size; k++) {
349 result[k] ^= temp_result[k];
350 }
351
352 PRINT_HASH ("Hi() - accumulated result inside loop", result, hash_size);
353 }
354
355 utils->free(initial_key);
356 utils->free(temp_result);
357 }
358
359 /**
360 * User salt is Hi(username,salt_key);
361 * This is fixed per reboot, to allow caching of SCRAM
362 * SaltedPassword.
363 */
364 static unsigned char *
scram_server_user_salt(const sasl_utils_t * utils,const EVP_MD * md,const char * username,size_t * p_salt_len)365 scram_server_user_salt(const sasl_utils_t * utils,
366 const EVP_MD *md,
367 const char * username,
368 size_t * p_salt_len)
369 {
370 size_t hash_size = EVP_MD_size(md);
371 char * result = utils->malloc(hash_size);
372 Hi(utils, md, username, strlen(username), (const char *) g_salt_key, SALT_SIZE,
373 20 /* iterations */, result);
374 *p_salt_len = hash_size;
375 return (unsigned char *) result;
376 }
377
378 static int
GenerateScramSecrets(const sasl_utils_t * utils,const EVP_MD * md,const char * password,size_t password_len,char * salt,size_t salt_len,unsigned int iteration_count,char * StoredKey,char * ServerKey,char ** error_text)379 GenerateScramSecrets (const sasl_utils_t * utils,
380 const EVP_MD *md,
381 const char * password,
382 size_t password_len,
383 char * salt,
384 size_t salt_len,
385 unsigned int iteration_count,
386 char * StoredKey,
387 char * ServerKey,
388 char ** error_text)
389 {
390 char SaltedPassword[EVP_MAX_MD_SIZE];
391 char ClientKey[EVP_MAX_MD_SIZE];
392 sasl_secret_t *sec = NULL;
393 unsigned int hash_len = 0;
394 int result;
395 size_t hash_size = EVP_MD_size(md);
396
397 *error_text = NULL;
398
399 if (password_len == 0) {
400 *error_text = "empty secret";
401 result = SASL_FAIL;
402 goto cleanup;
403 }
404
405 sec = utils->malloc(sizeof(sasl_secret_t) + password_len);
406 if (sec == NULL) {
407 result = SASL_NOMEM;
408 goto cleanup;
409 }
410
411 sec->len = (unsigned) password_len;
412 strncpy((char *)sec->data, password, password_len + 1);
413
414 /* SaltedPassword := Hi(password, salt) */
415 Hi (utils,
416 md,
417 (const char *) sec->data,
418 sec->len,
419 salt,
420 salt_len,
421 iteration_count,
422 SaltedPassword);
423
424 /* ClientKey := HMAC(SaltedPassword, "Client Key") */
425 if (HMAC(md,
426 (const unsigned char *) SaltedPassword,
427 hash_size,
428 (const unsigned char *) CLIENT_KEY_CONSTANT,
429 CLIENT_KEY_CONSTANT_LEN,
430 (unsigned char *)ClientKey,
431 &hash_len) == NULL) {
432 *error_text = "HMAC call failed";
433 result = SASL_SCRAM_INTERNAL;
434 goto cleanup;
435 }
436
437 /* StoredKey := H(ClientKey) */
438 if (EVP_Digest((const unsigned char *) ClientKey, hash_size,
439 (unsigned char *) StoredKey, NULL, md, NULL) == 0) {
440 *error_text = "Digest call failed";
441 result = SASL_SCRAM_INTERNAL;
442 goto cleanup;
443
444 }
445
446 /* ServerKey := HMAC(SaltedPassword, "Server Key") */
447 if (HMAC(md,
448 (const unsigned char *) SaltedPassword,
449 hash_size,
450 (const unsigned char *) SERVER_KEY_CONSTANT,
451 SERVER_KEY_CONSTANT_LEN,
452 (unsigned char *)ServerKey,
453 &hash_len) == NULL) {
454 *error_text = "HMAC call failed";
455 result = SASL_SCRAM_INTERNAL;
456 goto cleanup;
457 }
458
459 result = SASL_OK;
460
461 cleanup:
462 if (sec) {
463 _plug_free_secret(utils, &sec);
464 }
465 return result;
466 }
467
468 /***************************** Server Section *****************************/
469
470 typedef struct server_context {
471 int state;
472
473 const EVP_MD *md; /* underlying MDA */
474
475 char * authentication_id;
476 char * authorization_id;
477
478 char * out_buf;
479 unsigned out_buf_len;
480 char * auth_message;
481 size_t auth_message_len;
482 char * nonce;
483 /* in binary form */
484 char * salt;
485 size_t salt_len;
486 unsigned int iteration_count;
487 char StoredKey[EVP_MAX_MD_SIZE + 1];
488 char ServerKey[EVP_MAX_MD_SIZE + 1];
489
490 int cb_flags;
491 char *cbindingname;
492 char *gs2_header;
493 size_t gs2_header_length;
494 } server_context_t;
495
496 static int
scram_server_mech_new(void * glob_context,sasl_server_params_t * sparams,const char * challenge,unsigned challen,void ** conn_context)497 scram_server_mech_new(void *glob_context,
498 sasl_server_params_t *sparams,
499 const char *challenge __attribute__((unused)),
500 unsigned challen __attribute__((unused)),
501 void **conn_context)
502 {
503 server_context_t *text;
504
505 /* holds state are in */
506 text = sparams->utils->malloc(sizeof(server_context_t));
507 if (text == NULL) {
508 MEMERROR( sparams->utils );
509 return SASL_NOMEM;
510 }
511
512 memset(text, 0, sizeof(server_context_t));
513 /* text->state = 0; */
514
515 text->md = EVP_get_digestbyname((const char *) glob_context);
516
517 *conn_context = text;
518
519 return SASL_OK;
520 }
521
522 static int
scram_server_mech_step1(server_context_t * text,sasl_server_params_t * sparams,const char * clientin,unsigned clientinlen,const char ** serverout,unsigned * serveroutlen,sasl_out_params_t * oparams)523 scram_server_mech_step1(server_context_t *text,
524 sasl_server_params_t *sparams,
525 const char *clientin,
526 unsigned clientinlen,
527 const char **serverout,
528 unsigned *serveroutlen,
529 sasl_out_params_t *oparams __attribute__((unused)))
530 {
531 char * authorization_id;
532 char * authentication_id;
533 char * p;
534 char * nonce;
535 size_t client_nonce_len;
536 char * base64_salt = NULL;
537 size_t base64len;
538 size_t estimated_challenge_len;
539 size_t pure_scram_length;
540 char * inbuf = NULL;
541 const char *password_request[] = { SASL_AUX_PASSWORD,
542 "*authPassword",
543 NULL };
544 int canon_flags;
545 struct propval auxprop_values[3];
546 int result;
547 size_t hash_size = EVP_MD_size(text->md);
548 const char *scram_sasl_mech = scram_sasl_mech_name(hash_size);
549
550 if (clientinlen == 0) {
551 sparams->utils->seterror(sparams->utils->conn, 0,
552 "%s input expected", scram_sasl_mech);
553 return SASL_BADPROT;
554 }
555
556 /* Expecting: 'gs2-cbind-flag "," [ authzid ] "," [reserved-mext ","]
557 username "," nonce ["," extensions]' */
558
559 if (clientinlen < 10) {
560 sparams->utils->seterror(sparams->utils->conn, 0,
561 "Invalid %s input", scram_sasl_mech);
562 return SASL_BADPROT;
563 }
564
565 inbuf = sparams->utils->malloc (clientinlen + 1);
566
567 if (inbuf == NULL) {
568 MEMERROR( sparams->utils );
569 return SASL_NOMEM;
570 }
571
572 memcpy(inbuf, clientin, clientinlen);
573 inbuf[clientinlen] = 0;
574
575 if (strlen(inbuf) != clientinlen) {
576 sparams->utils->seterror(sparams->utils->conn, 0,
577 "NULs found in %s input", scram_sasl_mech);
578 result = SASL_BADPROT;
579 goto cleanup;
580 }
581
582 p = inbuf;
583
584 /* gs2-cbind-flag = "p=" cb-name / "n" / "y"
585 ;; "n" -> client doesn't support channel binding
586 ;; "y" -> client does support channel binding
587 ;; but thinks the server does not.
588 ;; "p" -> client requires channel binding.
589 ;; The selected channel binding follows "p=". */
590 switch (p[0]) {
591 case 'p':
592 if (p[1] != '=') {
593 sparams->utils->seterror(sparams->utils->conn, 0,
594 "The initial 'p' needs to be followed by '=' in %s input",
595 scram_sasl_mech);
596 result = SASL_BADPROT;
597 goto cleanup;
598 }
599 p++;
600
601 text->cbindingname = p + 1;
602 p = strchr (p, ',');
603 if (p == NULL) {
604 text->cbindingname = NULL;
605
606 sparams->utils->seterror(sparams->utils->conn, 0,
607 "Channel binding name must be terminated by a comma in %s input",
608 scram_sasl_mech);
609 result = SASL_BADPROT;
610 goto cleanup;
611 }
612
613 *p = '\0';
614 _plug_strdup(sparams->utils, text->cbindingname, &text->cbindingname, NULL);
615 *p = ',';
616
617 text->cb_flags = SCRAM_CB_FLAG_P;
618 break;
619
620 case 'n':
621 text->cb_flags = SCRAM_CB_FLAG_N;
622 /* We always have at least 10 bytes, so this is safe */
623 p++;
624 break;
625
626 case 'y':
627 text->cb_flags = SCRAM_CB_FLAG_Y;
628 /* We always have at least 10 bytes, so this is safe */
629 p++;
630 break;
631
632 default:
633 sparams->utils->seterror(sparams->utils->conn, 0,
634 "The initial %s client response needs to start with 'y', 'n' or 'p'",
635 scram_sasl_mech);
636 result = SASL_BADPROT;
637 goto cleanup;
638 }
639
640 if (p[0] != ',') {
641 sparams->utils->seterror(sparams->utils->conn, 0,
642 "',' expected in %s input", scram_sasl_mech);
643 result = SASL_BADPROT;
644 goto cleanup;
645 }
646 p++;
647
648 if (p[0] == 'a' && p[1] == '=') {
649 authorization_id = p + 2;
650
651 p = strchr (authorization_id, ',');
652 if (p == NULL) {
653 sparams->utils->seterror(sparams->utils->conn, 0,
654 "At least nonce is expected in %s input",
655 scram_sasl_mech);
656 result = SASL_BADPROT;
657 goto cleanup;
658 }
659
660 /* End of the GS2 header */
661 p[0] = '\0';
662 /* The GS2 header length DOES include the terminating comma */
663 text->gs2_header_length = p - inbuf + 1;
664
665 p++;
666
667 /* Make a read-write copy we can modify */
668 _plug_strdup(sparams->utils, authorization_id, &text->authorization_id, NULL);
669
670 if (decode_saslname(text->authorization_id) != SASL_OK) {
671 sparams->utils->seterror(sparams->utils->conn, 0,
672 "Invalid authorization identity encoding in %s input",
673 scram_sasl_mech);
674 result = SASL_BADPROT;
675 goto cleanup;
676 }
677 } else if (p[0] != ',') {
678 sparams->utils->seterror(sparams->utils->conn, 0,
679 "',' expected in %s input", scram_sasl_mech);
680 result = SASL_BADPROT;
681 goto cleanup;
682 } else {
683 /* End of the GS2 header */
684 p[0] = '\0';
685 /* The GS2 header length DOES include the terminating comma */
686 text->gs2_header_length = p - inbuf + 1;
687
688 p++;
689 }
690
691 text->gs2_header = sparams->utils->malloc (text->gs2_header_length + 1);
692 if (text->gs2_header == NULL) {
693 MEMERROR( sparams->utils );
694 result = SASL_NOMEM;
695 goto cleanup;
696 }
697
698 memcpy(text->gs2_header, inbuf, text->gs2_header_length - 1);
699 /* Remember the comma */
700 text->gs2_header[text->gs2_header_length - 1] = ',';
701 text->gs2_header[text->gs2_header_length] = 0;
702
703
704
705 if (p[1] != '=') {
706 sparams->utils->seterror(sparams->utils->conn, 0,
707 "Invalid %s input", scram_sasl_mech);
708 result = SASL_BADPROT;
709 goto cleanup;
710 }
711
712 if (p[0] == 'm') {
713 sparams->utils->seterror(sparams->utils->conn, 0,
714 "Unsupported mandatory extension to %s",
715 scram_sasl_mech);
716 result = SASL_BADPROT;
717 goto cleanup;
718 }
719
720 if (p[0] != 'n') {
721 sparams->utils->seterror(sparams->utils->conn, 0,
722 "Username (n=) expected in %s input",
723 scram_sasl_mech);
724 result = SASL_BADPROT;
725 goto cleanup;
726 }
727
728 authentication_id = p + 2;
729 p = strchr (authentication_id, ',');
730
731 /* MUST be followed by a nonce */
732 if (p == NULL) {
733 sparams->utils->seterror(sparams->utils->conn, 0,
734 "Nonce expected after the username in %s input",
735 scram_sasl_mech);
736 result = SASL_BADPROT;
737 goto cleanup;
738 }
739
740 *p = '\0';
741 p++;
742
743 if (decode_saslname(authentication_id) != SASL_OK) {
744 sparams->utils->seterror(sparams->utils->conn, 0,
745 "Invalid username encoding in %s input",
746 scram_sasl_mech);
747 result = SASL_BADPROT;
748 goto cleanup;
749 }
750
751 _plug_strdup(sparams->utils, authentication_id, &text->authentication_id, NULL);
752
753 if (strncmp(p, "r=", 2) != 0) {
754 sparams->utils->seterror(sparams->utils->conn, 0,
755 "Nonce expected after the username in %s input",
756 scram_sasl_mech);
757 result = SASL_BADPROT;
758 goto cleanup;
759 }
760
761 p += 2;
762 nonce = p;
763 p = strchr (nonce, ',');
764
765 if (p == NULL) {
766 p = nonce + strlen(nonce);
767 } else {
768 *p = '\0';
769 }
770
771 /* Generate server nonce, by appending some random stuff to the client nonce */
772 client_nonce_len = strlen(nonce);
773 text->nonce = sparams->utils->malloc (client_nonce_len + NONCE_SIZE + 1);
774
775 if (text->nonce == NULL) {
776 MEMERROR( sparams->utils );
777 result = SASL_NOMEM;
778 goto cleanup;
779 }
780
781 strcpy (text->nonce, nonce);
782
783 if (create_nonce(sparams->utils,
784 text->nonce + client_nonce_len,
785 NONCE_SIZE + 1) == NULL) {
786 MEMERROR( sparams->utils );
787 result = SASL_NOMEM;
788 goto cleanup;
789 }
790
791
792
793 /* Now we fetch user's password and calculate our secret */
794 result = sparams->utils->prop_request(sparams->propctx, password_request);
795 if (result != SASL_OK) {
796 goto cleanup;
797 }
798
799 /* this will trigger the getting of the aux properties */
800 canon_flags = SASL_CU_AUTHID;
801 if (text->authorization_id == NULL || *text->authorization_id == '\0') {
802 canon_flags |= SASL_CU_AUTHZID;
803 }
804
805 result = sparams->canon_user(sparams->utils->conn,
806 text->authentication_id,
807 0,
808 canon_flags,
809 oparams);
810 if (result != SASL_OK) {
811 SETERROR(sparams->utils, "unable to canonify user and get auxprops");
812 goto cleanup;
813 }
814
815 if (text->authorization_id != NULL && *text->authorization_id != '\0') {
816 result = sparams->canon_user(sparams->utils->conn,
817 text->authorization_id,
818 0,
819 SASL_CU_AUTHZID,
820 oparams);
821 }
822 if (result != SASL_OK) {
823 SETERROR(sparams->utils, "unable to canonify authorization ID");
824 goto cleanup;
825 }
826
827 result = sparams->utils->prop_getnames(sparams->propctx,
828 password_request,
829 auxprop_values);
830 if (result < 0 ||
831 ((!auxprop_values[0].name || !auxprop_values[0].values) &&
832 (!auxprop_values[1].name || !auxprop_values[1].values))) {
833 /* We didn't find this username */
834 sparams->utils->seterror(sparams->utils->conn,0,
835 "no secret in database");
836 result = sparams->transition ? SASL_TRANS : SASL_NOUSER;
837 goto cleanup;
838 }
839
840 if (auxprop_values[0].name && auxprop_values[0].values) {
841 char * error_text = NULL;
842 char * s_iteration_count;
843 char * end;
844
845 text->salt = (char *) scram_server_user_salt(sparams->utils, text->md, text->authentication_id, &text->salt_len);
846
847 sparams->utils->getopt(sparams->utils->getopt_context,
848 /* Different SCRAM hashes can have different strengh */
849 scram_sasl_mech,
850 "scram_iteration_counter",
851 (const char **) &s_iteration_count,
852 NULL);
853
854 if (s_iteration_count != NULL) {
855 errno = 0;
856 text->iteration_count = strtoul(s_iteration_count, &end, 10);
857 if (s_iteration_count == end || *end != '\0' || errno != 0) {
858 sparams->utils->log(NULL,
859 SASL_LOG_DEBUG,
860 "Invalid iteration-count in scram_iteration_count SASL option: not a number. Using the default instead.");
861 s_iteration_count = NULL;
862 }
863 }
864
865 if (s_iteration_count == NULL) {
866 text->iteration_count = DEFAULT_ITERATION_COUNTER;
867 }
868
869 result = GenerateScramSecrets (sparams->utils,
870 text->md,
871 auxprop_values[0].values[0],
872 strlen(auxprop_values[0].values[0]),
873 text->salt,
874 text->salt_len,
875 text->iteration_count,
876 text->StoredKey,
877 text->ServerKey,
878 &error_text);
879 if (result != SASL_OK) {
880 if (error_text != NULL) {
881 sparams->utils->seterror(sparams->utils->conn, 0, "%s",
882 error_text);
883 }
884 goto cleanup;
885 }
886
887 } else if (auxprop_values[1].name && auxprop_values[1].values) {
888 char s_iteration_count[ITERATION_COUNTER_BUF_LEN+1];
889 size_t base64_salt_len;
890 unsigned int exact_key_len;
891 const char * scram_hash;
892 const char * p_field;
893 char * end;
894 int i;
895 size_t scram_sasl_mech_len = strlen(scram_sasl_mech);
896
897 result = SASL_SCRAM_INTERNAL;
898
899 for (i = 0; auxprop_values[1].values[i] != NULL; i++) {
900 scram_hash = auxprop_values[1].values[i];
901
902 /* Skip the leading spaces */
903 while (*scram_hash == ' ') {
904 scram_hash++;
905 }
906
907 if (strncmp(scram_hash, scram_sasl_mech, scram_sasl_mech_len) != 0) {
908 continue;
909 }
910 scram_hash += scram_sasl_mech_len;
911
912 /* Skip spaces */
913 while (*scram_hash == ' ') {
914 scram_hash++;
915 }
916
917 if (*scram_hash != '$') {
918 /* syntax error, ignore the value */
919 continue;
920 }
921 scram_hash++;
922
923 /* Skip spaces */
924 while (*scram_hash == ' ') {
925 scram_hash++;
926 }
927
928 p_field = strchr(scram_hash, ':');
929 if (p_field == NULL || p_field == scram_hash) {
930 /* syntax error, ignore the value */
931 continue;
932 }
933
934 if ((p_field - scram_hash) > ITERATION_COUNTER_BUF_LEN) {
935 /* The iteration counter is too big for us */
936 sparams->utils->seterror(sparams->utils->conn, 0,
937 "Invalid iteration-count in %s input: the value is too big",
938 scram_sasl_mech);
939 continue;
940 }
941
942 memcpy(s_iteration_count, scram_hash, p_field - scram_hash);
943 s_iteration_count[p_field - scram_hash] = '\0';
944
945 errno = 0;
946 text->iteration_count = strtoul(s_iteration_count, &end, 10);
947 if (s_iteration_count == end || *end != '\0' || errno != 0) {
948 sparams->utils->seterror(sparams->utils->conn, 0,
949 "Invalid iteration-count in %s input: not a number",
950 scram_sasl_mech);
951 continue;
952 }
953
954 scram_hash = p_field + 1;
955
956 p_field = scram_hash + strcspn(scram_hash, "$ ");
957 if (p_field == scram_hash || *p_field == '\0') {
958 /* syntax error, ignore the value */
959 continue;
960 }
961
962 base64_salt_len = p_field - scram_hash;
963 text->salt = (char *) sparams->utils->malloc(base64_salt_len);
964 if (sparams->utils->decode64(scram_hash,
965 (unsigned int)base64_salt_len,
966 text->salt,
967 (unsigned int)base64_salt_len,
968 (unsigned int *) &text->salt_len) != SASL_OK) {
969 sparams->utils->seterror(sparams->utils->conn, 0,
970 "Invalid base64 encoding of the salt in %s stored value",
971 scram_sasl_mech);
972 continue;
973 }
974
975 scram_hash = p_field;
976
977 /* Skip spaces */
978 while (*scram_hash == ' ') {
979 scram_hash++;
980 }
981
982 if (*scram_hash != '$') {
983 /* syntax error, ignore the value */
984 sparams->utils->free(text->salt);
985 text->salt = NULL;
986 continue;
987 }
988 scram_hash++;
989
990 /* Skip spaces */
991 while (*scram_hash == ' ') {
992 scram_hash++;
993 }
994
995 p_field = strchr(scram_hash, ':');
996 if (p_field == NULL || p_field == scram_hash) {
997 /* syntax error, ignore the value */
998 sparams->utils->free(text->salt);
999 text->salt = NULL;
1000 continue;
1001 }
1002
1003 if (sparams->utils->decode64(scram_hash,
1004 (unsigned int)(p_field - scram_hash),
1005 text->StoredKey,
1006 hash_size + 1,
1007 &exact_key_len) != SASL_OK) {
1008 sparams->utils->seterror(sparams->utils->conn, 0,
1009 "Invalid base64 encoding of StoredKey in %s per-user storage",
1010 scram_sasl_mech);
1011 sparams->utils->free(text->salt);
1012 text->salt = NULL;
1013 continue;
1014 }
1015
1016 if (exact_key_len != hash_size) {
1017 sparams->utils->seterror(sparams->utils->conn, 0,
1018 "Invalid StoredKey in %s per-user storage",
1019 scram_sasl_mech);
1020 sparams->utils->free(text->salt);
1021 text->salt = NULL;
1022 continue;
1023 }
1024
1025 scram_hash = p_field + 1;
1026
1027 p_field = strchr(scram_hash, ' ');
1028 if (p_field == NULL) {
1029 p_field = scram_hash + strlen(scram_hash);
1030 }
1031
1032
1033 if (sparams->utils->decode64(scram_hash,
1034 (unsigned int)(p_field - scram_hash),
1035 text->ServerKey,
1036 hash_size + 1,
1037 &exact_key_len) != SASL_OK) {
1038 sparams->utils->seterror(sparams->utils->conn, 0,
1039 "Invalid base64 encoding of ServerKey in %s per-user storage",
1040 scram_sasl_mech);
1041 sparams->utils->free(text->salt);
1042 text->salt = NULL;
1043 continue;
1044 }
1045
1046 if (exact_key_len != hash_size) {
1047 sparams->utils->seterror(sparams->utils->conn, 0,
1048 "Invalid ServerKey in %s per-user storage", scram_sasl_mech);
1049 sparams->utils->free(text->salt);
1050 text->salt = NULL;
1051 continue;
1052 }
1053
1054 result = SASL_OK;
1055 break;
1056 }
1057
1058 if (result != SASL_OK) {
1059 sparams->utils->seterror(sparams->utils->conn,
1060 0, "No valid %s secret found",
1061 scram_sasl_mech);
1062 goto cleanup;
1063 }
1064
1065 } else {
1066 sparams->utils->seterror(sparams->utils->conn,
1067 0,
1068 "Have neither type of secret");
1069 return SASL_FAIL;
1070 }
1071
1072 /* erase the plaintext password */
1073 sparams->utils->prop_erase(sparams->propctx, password_request[0]);
1074
1075
1076
1077 /* base 64 encode it so it has valid chars */
1078 base64len = (text->salt_len / 3 * 4) + ((text->salt_len % 3) ? 4 : 0);
1079
1080 base64_salt = (char *) sparams->utils->malloc(base64len + 1);
1081 if (base64_salt == NULL) {
1082 MEMERROR( sparams->utils );
1083 result = SASL_NOMEM;
1084 goto cleanup;
1085 }
1086
1087 /*
1088 * Returns SASL_OK on success, SASL_BUFOVER if result won't fit
1089 */
1090 if (sparams->utils->encode64(text->salt,
1091 (unsigned int)text->salt_len,
1092 base64_salt,
1093 (unsigned int)base64len + 1,
1094 NULL) != SASL_OK) {
1095 MEMERROR( sparams->utils );
1096 result = SASL_NOMEM;
1097 goto cleanup;
1098 }
1099
1100 base64_salt[base64len] = '\0';
1101
1102 /* Now we generate server challenge */
1103 estimated_challenge_len = client_nonce_len + NONCE_SIZE +
1104 base64len +
1105 ITERATION_COUNTER_BUF_LEN +
1106 strlen("r=,s=,i=");
1107 result = _plug_buf_alloc(sparams->utils,
1108 &(text->out_buf),
1109 &(text->out_buf_len),
1110 (unsigned) estimated_challenge_len + 1);
1111 if (result != SASL_OK) {
1112 MEMERROR( sparams->utils );
1113 result = SASL_NOMEM;
1114 goto cleanup;
1115 }
1116
1117 sprintf(text->out_buf,
1118 "r=%s,s=%s,i=%u",
1119 text->nonce,
1120 base64_salt,
1121 text->iteration_count);
1122
1123
1124 /* Save the (client response, ",", server challenge, ",").
1125 Note, we skip the GS2 prefix here */
1126 pure_scram_length = clientinlen - text->gs2_header_length;
1127 text->auth_message_len = pure_scram_length + 1 + estimated_challenge_len + 1;
1128 text->auth_message = sparams->utils->malloc (text->auth_message_len + 1);
1129 if (text->auth_message == NULL) {
1130 MEMERROR( sparams->utils );
1131 result = SASL_NOMEM;
1132 goto cleanup;
1133 }
1134
1135 memcpy(text->auth_message, clientin + text->gs2_header_length, pure_scram_length);
1136 text->auth_message[pure_scram_length] = ',';
1137 strcpy (text->auth_message + pure_scram_length + 1, text->out_buf);
1138 strcat (text->auth_message + pure_scram_length + 1, ",");
1139
1140 /* Now remember the exact length, not the estimated one */
1141 text->auth_message_len = strlen(text->auth_message);
1142
1143 *serverout = text->out_buf;
1144 *serveroutlen = (unsigned) strlen(text->out_buf);
1145
1146 result = SASL_CONTINUE;
1147 text->state = 2;
1148
1149 cleanup:
1150 if (inbuf != NULL) {
1151 sparams->utils->free(inbuf);
1152 }
1153 if (base64_salt != NULL) {
1154 sparams->utils->free(base64_salt);
1155 }
1156 return result;
1157 }
1158
1159 static int
scram_server_mech_step2(server_context_t * text,sasl_server_params_t * sparams,const char * clientin,unsigned clientinlen,const char ** serverout,unsigned * serveroutlen,sasl_out_params_t * oparams)1160 scram_server_mech_step2(server_context_t *text,
1161 sasl_server_params_t *sparams,
1162 const char *clientin,
1163 unsigned clientinlen,
1164 const char **serverout,
1165 unsigned *serveroutlen,
1166 sasl_out_params_t *oparams)
1167 {
1168 char *channel_binding = NULL;
1169 size_t channel_binding_len = 0;
1170 char *binary_channel_binding = NULL;
1171 unsigned binary_channel_binding_len = 0;
1172 char *client_proof = NULL;
1173 char *inbuf = NULL;
1174 char *p;
1175 int result = SASL_FAIL;
1176 size_t proof_offset;
1177 char * full_auth_message;
1178 char ReceivedClientKey[EVP_MAX_MD_SIZE];
1179 char DecodedClientProof[EVP_MAX_MD_SIZE + 1];
1180 char CalculatedStoredKey[EVP_MAX_MD_SIZE];
1181 char ClientSignature[EVP_MAX_MD_SIZE];
1182 char ServerSignature[EVP_MAX_MD_SIZE];
1183 char * nonce;
1184 size_t client_proof_len;
1185 size_t server_proof_len;
1186 unsigned exact_client_proof_len;
1187 unsigned int hash_len = 0;
1188 size_t k, hash_size = EVP_MD_size(text->md);
1189 const char *scram_sasl_mech = scram_sasl_mech_name(hash_size);
1190
1191 if (clientinlen == 0) {
1192 sparams->utils->seterror(sparams->utils->conn, 0,
1193 "%s input expected", scram_sasl_mech);
1194 return SASL_BADPROT;
1195 }
1196
1197 if (clientinlen < 3 || clientin[1] != '=') {
1198 sparams->utils->seterror(sparams->utils->conn, 0,
1199 "Invalid %s input", scram_sasl_mech);
1200 return SASL_BADPROT;
1201 }
1202
1203 inbuf = sparams->utils->malloc (clientinlen + 1);
1204
1205 if (inbuf == NULL) {
1206 MEMERROR( sparams->utils );
1207 return SASL_NOMEM;
1208 }
1209
1210 memcpy(inbuf, clientin, clientinlen);
1211 inbuf[clientinlen] = 0;
1212
1213 if (strlen(inbuf) != clientinlen) {
1214 sparams->utils->seterror(sparams->utils->conn, 0,
1215 "NULs found in %s input", scram_sasl_mech);
1216 result = SASL_BADPROT;
1217 goto cleanup;
1218 }
1219
1220 /* Expecting: channel-binding "," nonce ["," extensions] "," proof */
1221
1222 p = inbuf;
1223
1224 if (strncmp(p, "c=", 2) != 0) {
1225 sparams->utils->seterror(sparams->utils->conn, 0,
1226 "Channel binding expected in %s input",
1227 scram_sasl_mech);
1228 result = SASL_BADPROT;
1229 goto cleanup;
1230 }
1231
1232 channel_binding = p + 2;
1233
1234 p = strchr (channel_binding, ',');
1235 if (p == NULL) {
1236 sparams->utils->seterror(sparams->utils->conn, 0,
1237 "At least nonce is expected in %s input",
1238 scram_sasl_mech);
1239 result = SASL_BADPROT;
1240 goto cleanup;
1241 }
1242 *p = '\0';
1243 p++;
1244
1245 channel_binding_len = strlen(channel_binding);
1246
1247 /* We can calculate the exact length, but the decoded (binary) data
1248 is always shorter than its base64 version. */
1249 binary_channel_binding = (char *) sparams->utils->malloc(channel_binding_len + 1);
1250
1251 if (sparams->utils->decode64(channel_binding,
1252 (unsigned int)channel_binding_len,
1253 binary_channel_binding,
1254 (unsigned int)channel_binding_len,
1255 &binary_channel_binding_len) != SASL_OK) {
1256 sparams->utils->seterror(sparams->utils->conn, 0,
1257 "Invalid base64 encoding of the channel bindings in %s",
1258 scram_sasl_mech);
1259 result = SASL_BADPROT;
1260 goto cleanup;
1261 }
1262
1263 if (binary_channel_binding_len < text->gs2_header_length ||
1264 strncmp(binary_channel_binding, text->gs2_header, text->gs2_header_length) != 0) {
1265 sparams->utils->seterror (sparams->utils->conn,
1266 0,
1267 "Channel bindings prefix doesn't match the one received in the GS2 header of %s. Expected \"%s\"",
1268 scram_sasl_mech, text->gs2_header);
1269 result = SASL_BADPROT;
1270 goto cleanup;
1271 }
1272
1273 switch (text->cb_flags & SCRAM_CB_FLAG_MASK) {
1274 case SCRAM_CB_FLAG_P:
1275 binary_channel_binding_len -= (unsigned)text->gs2_header_length;
1276 if (binary_channel_binding_len == 0) {
1277 sparams->utils->seterror(sparams->utils->conn, 0,
1278 "Channel bindings data expected in %s",
1279 scram_sasl_mech);
1280 result = SASL_BADPROT;
1281 goto cleanup;
1282 }
1283
1284 if (sparams->cbinding == NULL) {
1285 sparams->utils->seterror (sparams->utils->conn,
1286 0,
1287 "Server does not support channel binding type received in %s. Received: %s",
1288 scram_sasl_mech,
1289 text->cbindingname);
1290 result = SASL_BADPROT;
1291 goto cleanup;
1292 }
1293
1294 if (strcmp(sparams->cbinding->name, text->cbindingname) != 0) {
1295 sparams->utils->seterror (sparams->utils->conn,
1296 0,
1297 "Unsupported channel bindings type received in %s. Expected: %s, received: %s",
1298 scram_sasl_mech,
1299 sparams->cbinding->name,
1300 text->cbindingname);
1301 result = SASL_BADPROT;
1302 goto cleanup;
1303 }
1304
1305 if (binary_channel_binding_len != sparams->cbinding->len) {
1306 sparams->utils->seterror (sparams->utils->conn,
1307 0,
1308 "Unsupported channel bindings length received in %s. Expected length: %lu, received: %d",
1309 scram_sasl_mech,
1310 sparams->cbinding->len,
1311 binary_channel_binding_len);
1312 result = SASL_BADPROT;
1313 goto cleanup;
1314 }
1315
1316 if (memcmp(binary_channel_binding + text->gs2_header_length,
1317 sparams->cbinding->data,
1318 binary_channel_binding_len) != 0) {
1319 sparams->utils->seterror(sparams->utils->conn, 0,
1320 "Channel bindings mismatch in %s",
1321 scram_sasl_mech);
1322 result = SASL_BADPROT;
1323 goto cleanup;
1324 }
1325 break;
1326 }
1327
1328 if (strncmp(p, "r=", 2) != 0) {
1329 sparams->utils->seterror(sparams->utils->conn, 0,
1330 "Nonce expected in %s input",
1331 scram_sasl_mech);
1332 result = SASL_BADPROT;
1333 goto cleanup;
1334 }
1335
1336 nonce = p + 2;
1337
1338 p = strchr (nonce, ',');
1339 if (p == NULL) {
1340 sparams->utils->seterror(sparams->utils->conn, 0,
1341 "At least proof is expected in %s input",
1342 scram_sasl_mech);
1343 result = SASL_BADPROT;
1344 goto cleanup;
1345 }
1346 *p = '\0';
1347 p++;
1348
1349 if (strcmp(nonce, text->nonce) != 0) {
1350 sparams->utils->seterror(sparams->utils->conn, 0,
1351 "Nonce mismatch %s input",
1352 scram_sasl_mech);
1353 result = SASL_BADPROT;
1354 goto cleanup;
1355 }
1356
1357 while (p[0] != '\0') {
1358 if (strncmp(p, "p=", 2) == 0) {
1359 client_proof = p + 2;
1360 proof_offset = p - inbuf - 1;
1361 break;
1362 }
1363
1364 p = strchr (p, ',');
1365 if (p == NULL) {
1366 break;
1367 }
1368 p++;
1369 }
1370
1371 if (client_proof == NULL) {
1372 sparams->utils->seterror(sparams->utils->conn, 0,
1373 "Client proof is expected in %s input",
1374 scram_sasl_mech);
1375 result = SASL_BADPROT;
1376 goto cleanup;
1377 }
1378
1379 /* Check that no extension data exists after the proof */
1380 p = strchr (client_proof, ',');
1381 if (p != NULL) {
1382 sparams->utils->seterror(sparams->utils->conn, 0,
1383 "No extension data is allowed after the client proof in %s input",
1384 scram_sasl_mech);
1385 result = SASL_BADPROT;
1386 goto cleanup;
1387 }
1388
1389 if (strlen(client_proof) != (hash_size / 3 * 4 + (hash_size % 3 ? 4 : 0))) {
1390 sparams->utils->seterror(sparams->utils->conn, 0,
1391 "Invalid client proof length in %s input",
1392 scram_sasl_mech);
1393 result = SASL_BADPROT;
1394 goto cleanup;
1395 }
1396
1397 /* Construct the full AuthMessage */
1398 full_auth_message = sparams->utils->realloc(text->auth_message,
1399 text->auth_message_len + proof_offset + 1);
1400 if (full_auth_message == NULL) {
1401 MEMERROR( sparams->utils );
1402 result = SASL_NOMEM;
1403 goto cleanup;
1404 }
1405 text->auth_message = full_auth_message;
1406
1407 memcpy(text->auth_message + text->auth_message_len, clientin, proof_offset);
1408
1409 text->auth_message_len += proof_offset;
1410 text->auth_message[text->auth_message_len] = '\0';
1411
1412
1413 /* ClientSignature := HMAC(StoredKey, AuthMessage) */
1414 if (HMAC(text->md,
1415 (const unsigned char *) text->StoredKey,
1416 hash_size,
1417 (const unsigned char *)text->auth_message,
1418 (int)text->auth_message_len,
1419 (unsigned char *)ClientSignature,
1420 &hash_len) == NULL) {
1421 sparams->utils->seterror(sparams->utils->conn, 0,
1422 "HMAC-%s call failed", scram_sasl_mech+6);
1423 result = SASL_SCRAM_INTERNAL;
1424 goto cleanup;
1425 }
1426
1427 client_proof_len = strlen(client_proof);
1428 if (sparams->utils->decode64(client_proof,
1429 (unsigned int)client_proof_len,
1430 DecodedClientProof,
1431 hash_size + 1,
1432 &exact_client_proof_len) != SASL_OK) {
1433 sparams->utils->seterror(sparams->utils->conn, 0,
1434 "Invalid base64 encoding of the client proof in %s input",
1435 scram_sasl_mech);
1436 result = SASL_BADPROT;
1437 goto cleanup;
1438 }
1439
1440 if (exact_client_proof_len != hash_size) {
1441 sparams->utils->seterror(sparams->utils->conn, 0,
1442 "Invalid client proof (truncated) in %s input",
1443 scram_sasl_mech);
1444 result = SASL_BADPROT;
1445 goto cleanup;
1446 }
1447
1448 for (k = 0; k < hash_size; k++) {
1449 ReceivedClientKey[k] = DecodedClientProof[k] ^ ClientSignature[k];
1450 }
1451
1452 /* StoredKey := H(ClientKey) */
1453 if (EVP_Digest((const unsigned char *) ReceivedClientKey, hash_size,
1454 (unsigned char *) CalculatedStoredKey, NULL, text->md, NULL) == 0) {
1455 sparams->utils->seterror(sparams->utils->conn,0,
1456 "%s call failed", scram_sasl_mech+6);
1457 result = SASL_SCRAM_INTERNAL;
1458 goto cleanup;
1459 }
1460
1461 for (k = 0; k < hash_size; k++) {
1462 if (CalculatedStoredKey[k] != text->StoredKey[k]) {
1463 SETERROR(sparams->utils, "StoredKey mismatch");
1464 result = SASL_BADAUTH;
1465 goto cleanup;
1466 }
1467 }
1468
1469 /* ServerSignature := HMAC(ServerKey, AuthMessage) */
1470 if (HMAC(text->md,
1471 (const unsigned char *) text->ServerKey,
1472 hash_size,
1473 (unsigned char *) text->auth_message,
1474 (int)text->auth_message_len,
1475 (unsigned char *)ServerSignature,
1476 &hash_len) == NULL) {
1477 sparams->utils->seterror(sparams->utils->conn,0,
1478 "HMAC-%s call failed", scram_sasl_mech+6);
1479 result = SASL_SCRAM_INTERNAL;
1480 goto cleanup;
1481 }
1482
1483 server_proof_len = (hash_size / 3 * 4 + (hash_size % 3 ? 4 : 0));
1484 result = _plug_buf_alloc(sparams->utils,
1485 &(text->out_buf),
1486 &(text->out_buf_len),
1487 (unsigned) server_proof_len + strlen("v=") + 1);
1488 if (result != SASL_OK) {
1489 MEMERROR( sparams->utils );
1490 result = SASL_NOMEM;
1491 goto cleanup;
1492 }
1493
1494 text->out_buf[0] = 'v';
1495 text->out_buf[1] = '=';
1496
1497
1498 if (sparams->utils->encode64(ServerSignature,
1499 hash_size,
1500 text->out_buf+2,
1501 (unsigned int)server_proof_len + 1,
1502 NULL) != SASL_OK) {
1503 SETERROR(sparams->utils, "Internal error");
1504 /* This is not quite right, but better than alternatives */
1505 result = SASL_NOMEM;
1506 goto cleanup;
1507 }
1508
1509 text->out_buf[server_proof_len + 2] = '\0';
1510
1511 *serverout = text->out_buf;
1512 *serveroutlen = (unsigned) strlen(text->out_buf);
1513
1514
1515 /* set oparams */
1516
1517 switch (text->cb_flags & SCRAM_CB_FLAG_MASK) {
1518 case SCRAM_CB_FLAG_N:
1519 oparams->cbindingdisp = SASL_CB_DISP_NONE;
1520 break;
1521 case SCRAM_CB_FLAG_P:
1522 oparams->cbindingdisp = SASL_CB_DISP_USED;
1523 oparams->cbindingname = text->cbindingname;
1524 break;
1525 case SCRAM_CB_FLAG_Y:
1526 oparams->cbindingdisp = SASL_CB_DISP_WANT;
1527 break;
1528 }
1529
1530 oparams->doneflag = 1;
1531 oparams->mech_ssf = 0;
1532 oparams->maxoutbuf = 0;
1533 oparams->encode_context = NULL;
1534 oparams->encode = NULL;
1535 oparams->decode_context = NULL;
1536 oparams->decode = NULL;
1537 oparams->param_version = 0;
1538
1539 result = SASL_OK;
1540
1541 cleanup:
1542 if (inbuf != NULL) {
1543 sparams->utils->free(inbuf);
1544 }
1545 if (binary_channel_binding != NULL) {
1546 sparams->utils->free(binary_channel_binding);
1547 }
1548
1549 return result;
1550 }
1551
scram_server_mech_step(void * conn_context,sasl_server_params_t * sparams,const char * clientin,unsigned clientinlen,const char ** serverout,unsigned * serveroutlen,sasl_out_params_t * oparams)1552 static int scram_server_mech_step(void *conn_context,
1553 sasl_server_params_t *sparams,
1554 const char *clientin,
1555 unsigned clientinlen,
1556 const char **serverout,
1557 unsigned *serveroutlen,
1558 sasl_out_params_t *oparams)
1559 {
1560 server_context_t *text = (server_context_t *) conn_context;
1561 const char *scram_sasl_mech = NULL;
1562
1563 *serverout = NULL;
1564 *serveroutlen = 0;
1565
1566 if (text == NULL) {
1567 return SASL_BADPROT;
1568 }
1569
1570 scram_sasl_mech = scram_sasl_mech_name(EVP_MD_size(text->md));
1571
1572 /* this should be well more than is ever needed */
1573 if (clientinlen > MAX_CLIENTIN_LEN) {
1574 sparams->utils->seterror(sparams->utils->conn, 0,
1575 "%s input longer than "
1576 STRINGIZE((MAX_CLIENTIN_LEN)) " bytes",
1577 scram_sasl_mech);
1578 return SASL_BADPROT;
1579 }
1580
1581 switch (text->state) {
1582 case 0:
1583 text->state++;
1584 /* Assume the protocol doesn't support initial client response */
1585 if (clientinlen == 0) {
1586 return SASL_CONTINUE;
1587 }
1588 /* fall through */
1589
1590 case 1:
1591 return scram_server_mech_step1(text,
1592 sparams,
1593 clientin,
1594 clientinlen,
1595 serverout,
1596 serveroutlen,
1597 oparams);
1598
1599 case 2:
1600 text->state++;
1601 return scram_server_mech_step2(text,
1602 sparams,
1603 clientin,
1604 clientinlen,
1605 serverout,
1606 serveroutlen,
1607 oparams);
1608
1609 default: /* should never get here */
1610 sparams->utils->log(NULL, SASL_LOG_ERR,
1611 "Invalid %s server step %d\n",
1612 scram_sasl_mech, text->state);
1613 return SASL_FAIL;
1614 }
1615
1616 return SASL_FAIL; /* should never get here */
1617 }
1618
scram_setpass(void * glob_context,sasl_server_params_t * sparams,const char * userstr,const char * pass,unsigned passlen,const char * oldpass,unsigned oldpasslen,unsigned flags)1619 static int scram_setpass(void *glob_context,
1620 sasl_server_params_t *sparams,
1621 const char *userstr,
1622 const char *pass,
1623 unsigned passlen,
1624 const char *oldpass __attribute__((unused)),
1625 unsigned oldpasslen __attribute__((unused)),
1626 unsigned flags)
1627 {
1628 int r;
1629 char *user = NULL;
1630 char *user_only = NULL;
1631 char *realm = NULL;
1632 sasl_secret_t *sec = NULL;
1633 struct propctx *propctx = NULL;
1634 const char *store_request[] = { "authPassword",
1635 NULL };
1636 const char *generate_scram_secret;
1637 const EVP_MD *md = EVP_get_digestbyname((const char *) glob_context);
1638 size_t hash_size = EVP_MD_size(md);
1639 const char *scram_sasl_mech = scram_sasl_mech_name(hash_size);
1640
1641 /* Do we have a backend that can store properties? */
1642 if (!sparams->utils->auxprop_store ||
1643 sparams->utils->auxprop_store(NULL, NULL, NULL) != SASL_OK) {
1644 sparams->utils->seterror(sparams->utils->conn, 0,
1645 "%s: auxprop backend can't store properties",
1646 scram_sasl_mech);
1647 return SASL_NOMECH;
1648 }
1649
1650 sparams->utils->getopt(sparams->utils->getopt_context,
1651 /* This affects all SCRAM plugins, not just SCRAM-SHA-1 */
1652 "SCRAM",
1653 "scram_secret_generate",
1654 &generate_scram_secret,
1655 NULL);
1656
1657 /* NOTE: The default (when this option is not set) is NOT to generate authPassword secret */
1658 if (!(generate_scram_secret &&
1659 (generate_scram_secret[0] == '1' || generate_scram_secret[0] == 'y' ||
1660 (generate_scram_secret[0] == 'o' && generate_scram_secret[1] == 'n') ||
1661 generate_scram_secret[0] == 't'))) {
1662 /* Pretend that everything is Ok, no need to generate noise in the logs */
1663 return SASL_OK;
1664 }
1665
1666 r = _plug_parseuser(sparams->utils,
1667 &user_only,
1668 &realm,
1669 sparams->user_realm,
1670 sparams->serverFQDN,
1671 userstr);
1672 if (r) {
1673 sparams->utils->seterror(sparams->utils->conn, 0,
1674 "%s: Error parsing user", scram_sasl_mech);
1675 return r;
1676 }
1677
1678 r = _plug_make_fulluser(sparams->utils, &user, user_only, realm);
1679 if (r) {
1680 goto cleanup;
1681 }
1682
1683 if ((flags & SASL_SET_DISABLE) || pass == NULL) {
1684 sec = NULL;
1685 } else {
1686 char * error_text = NULL;
1687 char salt[SALT_SIZE + 1];
1688 char base64_salt[BASE64_LEN(SALT_SIZE) + 1];
1689 /* size_t salt_len = SALT_SIZE; */
1690 char StoredKey[EVP_MAX_MD_SIZE + 1];
1691 char ServerKey[EVP_MAX_MD_SIZE + 1];
1692 char base64_StoredKey[BASE64_LEN(EVP_MAX_MD_SIZE) + 1];
1693 char base64_ServerKey[BASE64_LEN(EVP_MAX_MD_SIZE) + 1];
1694 size_t secret_len;
1695 unsigned int iteration_count = DEFAULT_ITERATION_COUNTER;
1696 char * s_iteration_count;
1697 char * end;
1698
1699 sparams->utils->getopt(sparams->utils->getopt_context,
1700 /* Different SCRAM hashes can have different strengh */
1701 scram_sasl_mech,
1702 "scram_iteration_counter",
1703 (const char **) &s_iteration_count,
1704 NULL);
1705
1706 if (s_iteration_count != NULL) {
1707 errno = 0;
1708 iteration_count = strtoul(s_iteration_count, &end, 10);
1709 if (s_iteration_count == end || *end != '\0' || errno != 0) {
1710 sparams->utils->log(NULL,
1711 SASL_LOG_DEBUG,
1712 "Invalid iteration-count in scram_iteration_count SASL option: not a number. Using the default instead.");
1713 s_iteration_count = NULL;
1714 }
1715 }
1716
1717 if (s_iteration_count == NULL) {
1718 iteration_count = DEFAULT_ITERATION_COUNTER;
1719 }
1720
1721 sparams->utils->rand(sparams->utils->rpool, salt, SALT_SIZE);
1722
1723 r = GenerateScramSecrets (sparams->utils,
1724 md,
1725 pass,
1726 passlen,
1727 salt,
1728 SALT_SIZE,
1729 iteration_count,
1730 StoredKey,
1731 ServerKey,
1732 &error_text);
1733 if (r != SASL_OK) {
1734 if (error_text != NULL) {
1735 sparams->utils->seterror(sparams->utils->conn, 0, "%s",
1736 error_text);
1737 }
1738 goto cleanup;
1739 }
1740
1741 /* Returns SASL_OK on success, SASL_BUFOVER if result won't fit */
1742 if (sparams->utils->encode64(salt,
1743 SALT_SIZE,
1744 base64_salt,
1745 BASE64_LEN(SALT_SIZE) + 1,
1746 NULL) != SASL_OK) {
1747 MEMERROR( sparams->utils );
1748 r = SASL_NOMEM;
1749 goto cleanup;
1750 }
1751
1752 base64_salt[BASE64_LEN(SALT_SIZE)] = '\0';
1753
1754
1755 /* Returns SASL_OK on success, SASL_BUFOVER if result won't fit */
1756 if (sparams->utils->encode64(StoredKey,
1757 hash_size,
1758 base64_StoredKey,
1759 BASE64_LEN(hash_size) + 1,
1760 NULL) != SASL_OK) {
1761 MEMERROR( sparams->utils );
1762 r = SASL_NOMEM;
1763 goto cleanup;
1764 }
1765
1766 base64_StoredKey[BASE64_LEN(hash_size)] = '\0';
1767
1768
1769
1770 /* Returns SASL_OK on success, SASL_BUFOVER if result won't fit */
1771 if (sparams->utils->encode64(ServerKey,
1772 hash_size,
1773 base64_ServerKey,
1774 BASE64_LEN(hash_size) + 1,
1775 NULL) != SASL_OK) {
1776 MEMERROR( sparams->utils );
1777 r = SASL_NOMEM;
1778 goto cleanup;
1779 }
1780
1781 base64_ServerKey[BASE64_LEN(hash_size)] = '\0';
1782
1783 secret_len = strlen(scram_sasl_mech) + strlen("$:$:") +
1784 ITERATION_COUNTER_BUF_LEN +
1785 sizeof(base64_salt) +
1786 sizeof(base64_StoredKey) +
1787 sizeof(base64_ServerKey);
1788
1789 sec = sparams->utils->malloc(sizeof(sasl_secret_t) + secret_len);
1790 if (sec == NULL) {
1791 MEMERROR( sparams->utils );
1792 r = SASL_NOMEM;
1793 goto cleanup;
1794 }
1795
1796 sprintf((char *) sec->data,
1797 "%s$%u:%s$%s:%s",
1798 scram_sasl_mech,
1799 iteration_count,
1800 base64_salt,
1801 base64_StoredKey,
1802 base64_ServerKey);
1803 sec->len = (unsigned int) strlen((const char *) sec->data);
1804 }
1805
1806 /* do the store */
1807 propctx = sparams->utils->prop_new(0);
1808 if (!propctx) {
1809 r = SASL_FAIL;
1810 }
1811 if (!r) {
1812 r = sparams->utils->prop_request(propctx, store_request);
1813 }
1814 if (!r) {
1815 r = sparams->utils->prop_set(propctx,
1816 "authPassword",
1817 (const char *) (sec ? sec->data : NULL),
1818 (sec ? sec->len : 0));
1819 }
1820 if (!r) {
1821 r = sparams->utils->auxprop_store(sparams->utils->conn, propctx, user);
1822 }
1823 if (propctx) {
1824 sparams->utils->prop_dispose(&propctx);
1825 }
1826
1827 if (r) {
1828 sparams->utils->seterror(sparams->utils->conn, 0,
1829 "Error putting %s secret",
1830 scram_sasl_mech);
1831 goto cleanup;
1832 }
1833
1834 sparams->utils->log(NULL, SASL_LOG_DEBUG, "Setpass for %s successful\n",
1835 scram_sasl_mech);
1836
1837 cleanup:
1838 if (user) _plug_free_string(sparams->utils, &user);
1839 if (user_only) _plug_free_string(sparams->utils, &user_only);
1840 if (realm) _plug_free_string(sparams->utils, &realm);
1841 if (sec) _plug_free_secret(sparams->utils, &sec);
1842
1843 return r;
1844 }
1845
scram_server_mech_dispose(void * conn_context,const sasl_utils_t * utils)1846 static void scram_server_mech_dispose(void *conn_context,
1847 const sasl_utils_t *utils)
1848 {
1849 server_context_t *text = (server_context_t *) conn_context;
1850
1851 if (!text) return;
1852
1853 if (text->authentication_id) _plug_free_string(utils,&(text->authentication_id));
1854 if (text->authorization_id) _plug_free_string(utils,&(text->authorization_id));
1855 if (text->out_buf) _plug_free_string(utils,&(text->out_buf));
1856 if (text->auth_message) _plug_free_string(utils,&(text->auth_message));
1857 if (text->nonce) _plug_free_string(utils,&(text->nonce));
1858 if (text->salt) utils->free(text->salt);
1859 if (text->cbindingname != NULL) {
1860 utils->free(text->cbindingname);
1861 text->cbindingname = NULL;
1862 }
1863 if (text->gs2_header != NULL) {
1864 utils->free(text->gs2_header);
1865 text->gs2_header = NULL;
1866 }
1867
1868 utils->free(text);
1869 }
1870
1871 static sasl_server_plug_t scram_server_plugins[] =
1872 {
1873 #ifdef HAVE_SHA512
1874 {
1875 "SCRAM-SHA-512", /* mech_name */
1876 0, /* max_ssf */
1877 SASL_SET_HASH_STRENGTH_BITS(512) |
1878 SASL_SEC_NOPLAINTEXT
1879 | SASL_SEC_NOACTIVE
1880 | SASL_SEC_NOANONYMOUS
1881 | SASL_SEC_MUTUAL_AUTH, /* security_flags */
1882 SASL_FEAT_ALLOWS_PROXY
1883 | SASL_FEAT_SUPPORTS_HTTP
1884 | SASL_FEAT_CHANNEL_BINDING, /* features */
1885 "SHA512", /* glob_context */
1886 &scram_server_mech_new, /* mech_new */
1887 &scram_server_mech_step, /* mech_step */
1888 &scram_server_mech_dispose, /* mech_dispose */
1889 NULL, /* mech_free */
1890 &scram_setpass, /* setpass */
1891 NULL, /* user_query */
1892 NULL, /* idle */
1893 NULL, /* mech avail */
1894 NULL /* spare */
1895 },
1896 {
1897 "SCRAM-SHA-384", /* mech_name */
1898 0, /* max_ssf */
1899 SASL_SET_HASH_STRENGTH_BITS(384) |
1900 SASL_SEC_NOPLAINTEXT
1901 | SASL_SEC_NOACTIVE
1902 | SASL_SEC_NOANONYMOUS
1903 | SASL_SEC_MUTUAL_AUTH, /* security_flags */
1904 SASL_FEAT_ALLOWS_PROXY
1905 | SASL_FEAT_SUPPORTS_HTTP
1906 | SASL_FEAT_CHANNEL_BINDING, /* features */
1907 "SHA384", /* glob_context */
1908 &scram_server_mech_new, /* mech_new */
1909 &scram_server_mech_step, /* mech_step */
1910 &scram_server_mech_dispose, /* mech_dispose */
1911 NULL, /* mech_free */
1912 &scram_setpass, /* setpass */
1913 NULL, /* user_query */
1914 NULL, /* idle */
1915 NULL, /* mech avail */
1916 NULL /* spare */
1917 },
1918 {
1919 "SCRAM-SHA-256", /* mech_name */
1920 0, /* max_ssf */
1921 SASL_SET_HASH_STRENGTH_BITS(256) |
1922 SASL_SEC_NOPLAINTEXT
1923 | SASL_SEC_NOACTIVE
1924 | SASL_SEC_NOANONYMOUS
1925 | SASL_SEC_MUTUAL_AUTH, /* security_flags */
1926 SASL_FEAT_ALLOWS_PROXY
1927 | SASL_FEAT_SUPPORTS_HTTP
1928 | SASL_FEAT_CHANNEL_BINDING, /* features */
1929 "SHA256", /* glob_context */
1930 &scram_server_mech_new, /* mech_new */
1931 &scram_server_mech_step, /* mech_step */
1932 &scram_server_mech_dispose, /* mech_dispose */
1933 NULL, /* mech_free */
1934 &scram_setpass, /* setpass */
1935 NULL, /* user_query */
1936 NULL, /* idle */
1937 NULL, /* mech avail */
1938 NULL /* spare */
1939 },
1940 {
1941 "SCRAM-SHA-224", /* mech_name */
1942 0, /* max_ssf */
1943 SASL_SET_HASH_STRENGTH_BITS(224) |
1944 SASL_SEC_NOPLAINTEXT
1945 | SASL_SEC_NOACTIVE
1946 | SASL_SEC_NOANONYMOUS
1947 | SASL_SEC_MUTUAL_AUTH, /* security_flags */
1948 SASL_FEAT_ALLOWS_PROXY
1949 | SASL_FEAT_SUPPORTS_HTTP
1950 | SASL_FEAT_CHANNEL_BINDING, /* features */
1951 "SHA224", /* glob_context */
1952 &scram_server_mech_new, /* mech_new */
1953 &scram_server_mech_step, /* mech_step */
1954 &scram_server_mech_dispose, /* mech_dispose */
1955 NULL, /* mech_free */
1956 &scram_setpass, /* setpass */
1957 NULL, /* user_query */
1958 NULL, /* idle */
1959 NULL, /* mech avail */
1960 NULL /* spare */
1961 },
1962 #endif
1963 {
1964 "SCRAM-SHA-1", /* mech_name */
1965 0, /* max_ssf */
1966 SASL_SET_HASH_STRENGTH_BITS(160) |
1967 SASL_SEC_NOPLAINTEXT
1968 | SASL_SEC_NOACTIVE
1969 | SASL_SEC_NOANONYMOUS
1970 | SASL_SEC_MUTUAL_AUTH, /* security_flags */
1971 SASL_FEAT_ALLOWS_PROXY
1972 | SASL_FEAT_SUPPORTS_HTTP
1973 | SASL_FEAT_CHANNEL_BINDING, /* features */
1974 "SHA1", /* glob_context */
1975 &scram_server_mech_new, /* mech_new */
1976 &scram_server_mech_step, /* mech_step */
1977 &scram_server_mech_dispose, /* mech_dispose */
1978 NULL, /* mech_free */
1979 &scram_setpass, /* setpass */
1980 NULL, /* user_query */
1981 NULL, /* idle */
1982 NULL, /* mech avail */
1983 NULL /* spare */
1984 }
1985 };
1986
scram_server_plug_init(const sasl_utils_t * utils,int maxversion,int * out_version,sasl_server_plug_t ** pluglist,int * plugcount)1987 int scram_server_plug_init(const sasl_utils_t *utils,
1988 int maxversion,
1989 int *out_version,
1990 sasl_server_plug_t **pluglist,
1991 int *plugcount)
1992 {
1993 if (maxversion < SASL_SERVER_PLUG_VERSION) {
1994 SETERROR( utils, "SCRAM-SHA-* version mismatch");
1995 return SASL_BADVERS;
1996 }
1997
1998 *out_version = SASL_SERVER_PLUG_VERSION;
1999 *pluglist = scram_server_plugins;
2000 #ifdef HAVE_SHA512
2001 *plugcount = 5;
2002 #else
2003 *plugcount = 1;
2004 #endif
2005 utils->rand(utils->rpool, (char *)g_salt_key, SALT_SIZE);
2006
2007 return SASL_OK;
2008 }
2009
2010 /***************************** Client Section *****************************/
2011
2012 typedef struct client_context {
2013 int state;
2014
2015 const EVP_MD *md; /* underlying MDA */
2016
2017 sasl_secret_t *password; /* user password */
2018 unsigned int free_password; /* set if we need to free the password */
2019
2020 char * gs2_header;
2021 size_t gs2_header_length;
2022 char * out_buf;
2023 unsigned out_buf_len;
2024 char * auth_message;
2025 size_t auth_message_len;
2026 char * nonce;
2027 /* in binary form */
2028 char * salt;
2029 size_t salt_len;
2030 unsigned int iteration_count;
2031 char SaltedPassword[EVP_MAX_MD_SIZE];
2032
2033 int cb_flags;
2034 } client_context_t;
2035
scram_client_mech_new(void * glob_context,sasl_client_params_t * params,void ** conn_context)2036 static int scram_client_mech_new(void *glob_context,
2037 sasl_client_params_t *params,
2038 void **conn_context)
2039 {
2040 client_context_t *text;
2041
2042 /* holds state are in */
2043 text = params->utils->malloc(sizeof(client_context_t));
2044 if (text == NULL) {
2045 MEMERROR(params->utils);
2046 return SASL_NOMEM;
2047 }
2048
2049 memset(text, 0, sizeof(client_context_t));
2050
2051 text->md = EVP_get_digestbyname((const char *) glob_context);
2052
2053 *conn_context = text;
2054
2055 return SASL_OK;
2056 }
2057
2058 static int
scram_client_mech_step1(client_context_t * text,sasl_client_params_t * params,const char * serverin,unsigned serverinlen,sasl_interact_t ** prompt_need,const char ** clientout,unsigned * clientoutlen,sasl_out_params_t * oparams)2059 scram_client_mech_step1(client_context_t *text,
2060 sasl_client_params_t *params,
2061 const char *serverin __attribute__((unused)),
2062 unsigned serverinlen __attribute__((unused)),
2063 sasl_interact_t **prompt_need,
2064 const char **clientout,
2065 unsigned *clientoutlen,
2066 sasl_out_params_t *oparams)
2067 {
2068 const char *authid = NULL;
2069 const char *userid = NULL;
2070 int user_result = SASL_OK;
2071 int auth_result = SASL_OK;
2072 int pass_result = SASL_OK;
2073 int result;
2074 size_t maxsize;
2075 char * encoded_authcid;
2076 char * freeme = NULL;
2077 char * freeme2 = NULL;
2078 char channel_binding_state = 'n';
2079 const char * channel_binding_name = NULL;
2080 char * encoded_authorization_id = NULL;
2081 const char *scram_sasl_mech = scram_sasl_mech_name(EVP_MD_size(text->md));
2082
2083 /* check if sec layer strong enough */
2084 if (params->props.min_ssf > params->external_ssf) {
2085 params->utils->seterror(params->utils->conn, 0,
2086 "SSF requested of %s plugin",
2087 scram_sasl_mech);
2088 return SASL_TOOWEAK;
2089 }
2090
2091 /* try to get the userid */
2092 if (oparams->authid == NULL) {
2093 auth_result=_plug_get_authid(params->utils, &authid, prompt_need);
2094
2095 if ((auth_result != SASL_OK) && (auth_result != SASL_INTERACT))
2096 return auth_result;
2097 }
2098
2099 /* try to get the userid */
2100 if (oparams->user == NULL) {
2101 user_result = _plug_get_userid(params->utils, &userid, prompt_need);
2102
2103 if ((user_result != SASL_OK) && (user_result != SASL_INTERACT)) {
2104 return user_result;
2105 }
2106 }
2107
2108 /* try to get the password */
2109 if (text->password == NULL) {
2110 pass_result = _plug_get_password(params->utils,
2111 &text->password,
2112 &text->free_password,
2113 prompt_need);
2114 if ((pass_result != SASL_OK) && (pass_result != SASL_INTERACT)) {
2115 return pass_result;
2116 }
2117 }
2118
2119 /* free prompts we got */
2120 if (prompt_need && *prompt_need) {
2121 params->utils->free(*prompt_need);
2122 *prompt_need = NULL;
2123 }
2124
2125 /* if there are prompts not filled in */
2126 if ((auth_result == SASL_INTERACT) ||
2127 (user_result == SASL_INTERACT) ||
2128 (pass_result == SASL_INTERACT)) {
2129 /* make the prompt list */
2130 result =
2131 _plug_make_prompts(params->utils,
2132 prompt_need,
2133 user_result == SASL_INTERACT ?
2134 "Please enter your authorization name" : NULL,
2135 NULL,
2136 auth_result == SASL_INTERACT ?
2137 "Please enter your authentication name" : NULL,
2138 NULL,
2139 pass_result == SASL_INTERACT ?
2140 "Please enter your password" : NULL,
2141 NULL,
2142 NULL,
2143 NULL,
2144 NULL,
2145 NULL,
2146 NULL,
2147 NULL);
2148
2149 if (result != SASL_OK) {
2150 goto cleanup;
2151 }
2152
2153 return SASL_INTERACT;
2154 }
2155
2156 if (!text->password) {
2157 PARAMERROR(params->utils);
2158 return SASL_BADPARAM;
2159 }
2160
2161 if (oparams->authid == NULL) {
2162 if (!userid || !*userid) {
2163 result = params->canon_user(params->utils->conn,
2164 authid,
2165 0,
2166 SASL_CU_AUTHID | SASL_CU_AUTHZID,
2167 oparams);
2168 }
2169 else {
2170 result = params->canon_user(params->utils->conn,
2171 authid,
2172 0,
2173 SASL_CU_AUTHID,
2174 oparams);
2175 if (result != SASL_OK) {
2176 goto cleanup;
2177 }
2178
2179 result = params->canon_user(params->utils->conn,
2180 userid,
2181 0,
2182 SASL_CU_AUTHZID,
2183 oparams);
2184 }
2185 if (result != SASL_OK) {
2186 goto cleanup;
2187 }
2188 }
2189
2190 switch (params->cbindingdisp) {
2191 case SASL_CB_DISP_NONE:
2192 text->cb_flags = SCRAM_CB_FLAG_N;
2193 channel_binding_state = 'n';
2194 break;
2195 case SASL_CB_DISP_USED:
2196 if (!SASL_CB_PRESENT(params)) {
2197 result = SASL_BADPARAM;
2198 goto cleanup;
2199 }
2200 channel_binding_name = params->cbinding->name;
2201 text->cb_flags = SCRAM_CB_FLAG_P;
2202 channel_binding_state = 'p';
2203 break;
2204 case SASL_CB_DISP_WANT:
2205 text->cb_flags = SCRAM_CB_FLAG_Y;
2206 channel_binding_state = 'y';
2207 break;
2208 }
2209
2210 text->nonce = params->utils->malloc (NONCE_SIZE + 1);
2211
2212 if (text->nonce == NULL) {
2213 MEMERROR( params->utils );
2214 result = SASL_NOMEM;
2215 goto cleanup;
2216 }
2217
2218 if (create_nonce(params->utils,
2219 text->nonce,
2220 NONCE_SIZE + 1) == NULL) {
2221 MEMERROR( params->utils );
2222 result = SASL_NOMEM;
2223 goto cleanup;
2224 }
2225
2226 if (userid != NULL && *userid != '\0') {
2227 result = encode_saslname (oparams->user,
2228 (const char **) &encoded_authorization_id,
2229 &freeme2);
2230
2231 if (result != SASL_OK) {
2232 MEMERROR( params->utils );
2233 result = SASL_NOMEM;
2234 goto cleanup;
2235 }
2236 }
2237
2238 result = encode_saslname (oparams->authid,
2239 (const char **) &encoded_authcid,
2240 &freeme);
2241 if (result != SASL_OK) {
2242 MEMERROR( params->utils );
2243 result = SASL_NOMEM;
2244 goto cleanup;
2245 }
2246
2247 maxsize = strlen("p=,a=,n=,r=") +
2248 ((channel_binding_name != NULL) ? strlen(channel_binding_name) : 0) +
2249 ((encoded_authorization_id != NULL) ? strlen(encoded_authorization_id) : 0) +
2250 strlen(encoded_authcid) +
2251 strlen(text->nonce);
2252 result = _plug_buf_alloc(params->utils,
2253 &(text->out_buf),
2254 &(text->out_buf_len),
2255 (unsigned) maxsize + 1);
2256 if (result != SASL_OK) {
2257 MEMERROR( params->utils );
2258 result = SASL_NOMEM;
2259 goto cleanup;
2260 }
2261
2262 snprintf(text->out_buf,
2263 maxsize + 1,
2264 "%c%s%s,%s%s,",
2265 channel_binding_state,
2266 (channel_binding_name != NULL) ? "=" : "",
2267 (channel_binding_name != NULL) ? channel_binding_name : "",
2268 (encoded_authorization_id != NULL) ? "a=" : "",
2269 (encoded_authorization_id != NULL) ? encoded_authorization_id : "");
2270
2271 text->gs2_header_length = strlen(text->out_buf);
2272 _plug_strdup(params->utils, text->out_buf, &text->gs2_header, NULL);
2273
2274 sprintf(text->out_buf + text->gs2_header_length,
2275 "n=%s,r=%s",
2276 encoded_authcid,
2277 text->nonce);
2278
2279 /* Save the copy of the client-first-message */
2280
2281 /* Need to skip the GS2 prefix here */
2282 _plug_strdup(params->utils,
2283 text->out_buf + text->gs2_header_length,
2284 &text->auth_message,
2285 NULL);
2286 if (text->auth_message == NULL) {
2287 MEMERROR( params->utils );
2288 result = SASL_NOMEM;
2289 goto cleanup;
2290 }
2291
2292 text->auth_message_len = strlen(text->auth_message);
2293
2294 *clientout = text->out_buf;
2295 *clientoutlen = (unsigned) strlen(*clientout);
2296
2297 result = SASL_CONTINUE;
2298
2299 cleanup:
2300 if (freeme != NULL) _plug_free_string(params->utils, &freeme);
2301 if (freeme2 != NULL) _plug_free_string(params->utils, &freeme2);
2302
2303 return result;
2304 }
2305
2306 static int
scram_client_mech_step2(client_context_t * text,sasl_client_params_t * params,const char * serverin,unsigned serverinlen,sasl_interact_t ** prompt_need,const char ** clientout,unsigned * clientoutlen,sasl_out_params_t * oparams)2307 scram_client_mech_step2(client_context_t *text,
2308 sasl_client_params_t *params,
2309 const char *serverin,
2310 unsigned serverinlen,
2311 sasl_interact_t **prompt_need __attribute__((unused)),
2312 const char **clientout,
2313 unsigned *clientoutlen,
2314 sasl_out_params_t *oparams __attribute__((unused)))
2315 {
2316 char * p;
2317 char * nonce;
2318 size_t server_nonce_len;
2319 char * base64_salt = NULL;
2320 size_t base64_salt_len;
2321 unsigned exact_salt_len;
2322 char * counter;
2323 char * end;
2324 char * inbuf = NULL;
2325 size_t estimated_response_len;
2326 size_t length_no_proof;
2327 char * full_auth_message;
2328 size_t cb_bin_length;
2329 size_t channel_binding_data_len = 0;
2330 size_t cb_encoded_length;
2331 const char * channel_binding_data = NULL;
2332 char * cb_encoded = NULL;
2333 char * cb_bin = NULL;
2334 int result;
2335 char ClientKey[EVP_MAX_MD_SIZE];
2336 char StoredKey[EVP_MAX_MD_SIZE];
2337 char ClientSignature[EVP_MAX_MD_SIZE];
2338 char ClientProof[EVP_MAX_MD_SIZE];
2339 char * client_proof = NULL;
2340 size_t client_proof_len;
2341 unsigned int hash_len = 0;
2342 size_t k, hash_size = EVP_MD_size(text->md);
2343 const char *scram_sasl_mech = scram_sasl_mech_name(hash_size);
2344
2345 if (serverinlen == 0) {
2346 params->utils->seterror(params->utils->conn, 0,
2347 "%s input expected", scram_sasl_mech);
2348 return SASL_BADPROT;
2349 }
2350
2351 /* [reserved-mext ","] nonce "," salt "," iteration-count ["," extensions] */
2352
2353 if (serverinlen < 3 || serverin[1] != '=') {
2354 params->utils->seterror(params->utils->conn, 0,
2355 "Invalid %s input", scram_sasl_mech);
2356 return SASL_BADPROT;
2357 }
2358
2359 if (serverin[0] == 'm') {
2360 params->utils->seterror(params->utils->conn, 0,
2361 "Unsupported mandatory extension to %s",
2362 scram_sasl_mech);
2363 return SASL_BADPROT;
2364 }
2365
2366 if (serverin[0] != 'r') {
2367 params->utils->seterror(params->utils->conn, 0,
2368 "Nonce (r=) expected in %s input",
2369 scram_sasl_mech);
2370 return SASL_BADPROT;
2371 }
2372
2373 inbuf = params->utils->malloc (serverinlen + 1);
2374 if (inbuf == NULL) {
2375 MEMERROR( params->utils );
2376 return SASL_NOMEM;
2377 }
2378
2379 memcpy(inbuf, serverin, serverinlen);
2380 inbuf[serverinlen] = 0;
2381
2382 if (strlen(inbuf) != serverinlen) {
2383 params->utils->seterror(params->utils->conn, 0,
2384 "NULs found in %s input", scram_sasl_mech);
2385 result = SASL_BADPROT;
2386 goto cleanup;
2387 }
2388
2389 nonce = inbuf + 2;
2390 p = strchr (nonce, ',');
2391
2392 /* MUST be followed by a salt */
2393 if (p == NULL) {
2394 params->utils->seterror(params->utils->conn, 0,
2395 "Salt expected after the nonce in %s input",
2396 scram_sasl_mech);
2397 result = SASL_BADPROT;
2398 goto cleanup;
2399 }
2400
2401 *p = '\0';
2402 p++;
2403
2404 if (strncmp(p, "s=", 2) != 0) {
2405 params->utils->seterror(params->utils->conn, 0,
2406 "Salt expected after the nonce in %s input",
2407 scram_sasl_mech);
2408 result = SASL_BADPROT;
2409 goto cleanup;
2410 }
2411
2412 p += 2;
2413 base64_salt = p;
2414
2415 p = strchr (base64_salt, ',');
2416
2417 /* MUST be followed by an iteration-count */
2418 if (p == NULL) {
2419 params->utils->seterror(params->utils->conn, 0,
2420 "iteration-count expected after the salt in %s input",
2421 scram_sasl_mech);
2422 result = SASL_BADPROT;
2423 goto cleanup;
2424 }
2425
2426 *p = '\0';
2427 p++;
2428
2429 if (strncmp(p, "i=", 2) != 0) {
2430 params->utils->seterror(params->utils->conn, 0,
2431 "iteration-count expected after the salt in %s input",
2432 scram_sasl_mech);
2433 result = SASL_BADPROT;
2434 goto cleanup;
2435 }
2436
2437 p += 2;
2438 counter = p;
2439 p = strchr (counter, ',');
2440
2441 if (p == NULL) {
2442 p = counter + strlen(counter);
2443 } else {
2444 *p = '\0';
2445 }
2446
2447 errno = 0;
2448 text->iteration_count = strtoul(counter, &end, 10);
2449 if (counter == end || *end != '\0' || errno != 0) {
2450 params->utils->seterror(params->utils->conn, 0,
2451 "Invalid iteration-count in %s input",
2452 scram_sasl_mech);
2453 result = SASL_BADPROT;
2454 goto cleanup;
2455 }
2456
2457 if (text->iteration_count < MIN_ITERATION_COUNTER) {
2458 }
2459
2460 if (text->iteration_count > MAX_ITERATION_COUNTER) {
2461 SETERROR(params->utils, "iteration-count is too big, refusing to compute");
2462 result = SASL_BADPROT;
2463 goto cleanup;
2464 }
2465
2466 /* The client MUST verify that the initial part of the nonce
2467 used in subsequent messages is the same as the nonce it
2468 initially specified. */
2469 server_nonce_len = strlen(nonce);
2470
2471 if (server_nonce_len <= NONCE_SIZE ||
2472 strncmp(nonce, text->nonce, NONCE_SIZE) != 0) {
2473 SETERROR(params->utils, "The nonce received from the server doesn't start from the nonce sent by the client");
2474 result = SASL_BADPROT;
2475 goto cleanup;
2476 }
2477
2478 /* Now we can forget about our nonce */
2479 params->utils->free(text->nonce);
2480
2481 _plug_strdup(params->utils, nonce, &text->nonce, NULL);
2482
2483 if (text->nonce == NULL) {
2484 MEMERROR( params->utils );
2485 result = SASL_NOMEM;
2486 goto cleanup;
2487 }
2488
2489 /* base64 decode salt */
2490 base64_salt_len = strlen(base64_salt);
2491
2492 if (base64_salt_len == 0) {
2493 SETERROR(params->utils, "The salt can't be empty");
2494 result = SASL_BADPROT;
2495 goto cleanup;
2496 }
2497
2498 if (base64_salt_len % 4 != 0) {
2499 SETERROR(params->utils, "Invalid base64 encoding of the salt");
2500 result = SASL_BADPROT;
2501 goto cleanup;
2502 }
2503
2504 text->salt_len = base64_salt_len / 4 * 3;
2505
2506 text->salt = (char *) params->utils->malloc(text->salt_len + 1);
2507 if (text->salt == NULL) {
2508 MEMERROR( params->utils );
2509 result = SASL_NOMEM;
2510 goto cleanup;
2511 }
2512
2513 if (params->utils->decode64(base64_salt,
2514 (unsigned int)base64_salt_len,
2515 text->salt,
2516 (unsigned int)text->salt_len + 1,
2517 &exact_salt_len) != SASL_OK) {
2518 params->utils->seterror(params->utils->conn, 0,
2519 "Invalid base64 encoding of the salt in %s input",
2520 scram_sasl_mech);
2521 result = SASL_BADPROT;
2522 goto cleanup;
2523 }
2524
2525 text->salt_len = exact_salt_len;
2526
2527 /* Now we generate client response */
2528
2529 if (text->gs2_header[0] == 'p') {
2530
2531 if (params->cbinding == NULL) {
2532 result = SASL_FAIL;
2533 goto cleanup;
2534 }
2535
2536 channel_binding_data = (const char *) params->cbinding->data;
2537 channel_binding_data_len = params->cbinding->len;
2538 }
2539
2540 cb_bin_length = text->gs2_header_length +
2541 ((channel_binding_data != NULL) ? channel_binding_data_len : 0);
2542 cb_encoded_length = (cb_bin_length / 3 * 4) + ((cb_bin_length % 3) ? 4 : 0);
2543
2544 if (channel_binding_data != NULL) {
2545 cb_bin = (char *) params->utils->malloc(cb_bin_length + 1);
2546 if (cb_bin == NULL) {
2547 MEMERROR( params->utils );
2548 result = SASL_NOMEM;
2549 goto cleanup;
2550 }
2551
2552 memcpy(cb_bin, text->gs2_header, text->gs2_header_length);
2553 memcpy(cb_bin + text->gs2_header_length, channel_binding_data, channel_binding_data_len);
2554 }
2555
2556 cb_encoded = (char *) params->utils->malloc(cb_encoded_length + 1);
2557 if (cb_encoded == NULL) {
2558 MEMERROR( params->utils );
2559 result = SASL_NOMEM;
2560 goto cleanup;
2561 }
2562
2563 /*
2564 * Returns SASL_OK on success, SASL_BUFOVER if result won't fit
2565 */
2566 if (params->utils->encode64((cb_bin != NULL) ? cb_bin : text->gs2_header,
2567 (unsigned int)cb_bin_length,
2568 cb_encoded,
2569 (unsigned int)cb_encoded_length + 1,
2570 NULL) != SASL_OK) {
2571 MEMERROR( params->utils );
2572 result = SASL_NOMEM;
2573 goto cleanup;
2574 }
2575
2576 cb_encoded[cb_encoded_length] = '\0';
2577
2578 client_proof_len = hash_size / 3 * 4 + ((hash_size % 3) ? 4 : 0);
2579 estimated_response_len = strlen(cb_encoded)+
2580 strlen(text->nonce)+
2581 client_proof_len +
2582 strlen("c=,r=,p=");
2583 result = _plug_buf_alloc(params->utils,
2584 &(text->out_buf),
2585 &(text->out_buf_len),
2586 (unsigned) estimated_response_len + 1);
2587 if (result != SASL_OK) {
2588 MEMERROR( params->utils );
2589 result = SASL_NOMEM;
2590 goto cleanup;
2591 }
2592
2593 /* channel-binding "," nonce ["," extensions] */
2594 sprintf(text->out_buf,
2595 "c=%s,r=%s",
2596 cb_encoded,
2597 text->nonce);
2598
2599 length_no_proof = strlen(text->out_buf);
2600
2601 /* Build AuthMessage */
2602 full_auth_message = params->utils->realloc(text->auth_message,
2603 text->auth_message_len + 1 +
2604 serverinlen + 1 +
2605 length_no_proof + 1);
2606 if (full_auth_message == NULL) {
2607 MEMERROR( params->utils );
2608 result = SASL_NOMEM;
2609 goto cleanup;
2610 }
2611
2612 text->auth_message = full_auth_message;
2613
2614 text->auth_message[text->auth_message_len] = ',';
2615 memcpy(text->auth_message + text->auth_message_len + 1, serverin, serverinlen);
2616 text->auth_message[text->auth_message_len + 1 + serverinlen] = ',';
2617 memcpy(text->auth_message + text->auth_message_len + 1 + serverinlen + 1,
2618 text->out_buf,
2619 length_no_proof);
2620 text->auth_message_len += serverinlen + 2 + length_no_proof;
2621 text->auth_message[text->auth_message_len] = '\0';
2622
2623 /* Calculate ClientProof */
2624
2625 /* SaltedPassword := Hi(password, salt) */
2626 Hi (params->utils,
2627 text->md,
2628 (const char *) text->password->data,
2629 text->password->len,
2630 text->salt,
2631 text->salt_len,
2632 text->iteration_count,
2633 text->SaltedPassword);
2634
2635 PRINT_HASH ("SaltedPassword", text->SaltedPassword, hash_size);
2636
2637 /* ClientKey := HMAC(SaltedPassword, "Client Key") */
2638 if (HMAC(text->md,
2639 (const unsigned char *) text->SaltedPassword,
2640 hash_size,
2641 (const unsigned char *) CLIENT_KEY_CONSTANT,
2642 CLIENT_KEY_CONSTANT_LEN,
2643 (unsigned char *)ClientKey,
2644 &hash_len) == NULL) {
2645 params->utils->seterror(params->utils->conn,0,
2646 "HMAC-%s call failed", scram_sasl_mech+6);
2647 result = SASL_SCRAM_INTERNAL;
2648 goto cleanup;
2649 }
2650
2651 PRINT_HASH ("ClientKey", ClientKey, hash_size);
2652
2653 /* StoredKey := H(ClientKey) */
2654 if (EVP_Digest((const unsigned char *) ClientKey, hash_size,
2655 (unsigned char *) StoredKey, NULL, text->md, NULL) == 0) {
2656 params->utils->seterror(params->utils->conn,0,
2657 "%s call failed", scram_sasl_mech+6);
2658 result = SASL_SCRAM_INTERNAL;
2659 goto cleanup;
2660 }
2661
2662 PRINT_HASH ("StoredKey", StoredKey, hash_size);
2663
2664 /* ClientSignature := HMAC(StoredKey, AuthMessage) */
2665 if (HMAC(text->md,
2666 (const unsigned char *)StoredKey,
2667 hash_size,
2668 (const unsigned char *) text->auth_message,
2669 (int)text->auth_message_len,
2670 (unsigned char *)ClientSignature,
2671 &hash_len) == NULL) {
2672 params->utils->seterror(params->utils->conn,0,
2673 "HMAC-%s call failed", scram_sasl_mech+6);
2674 result = SASL_SCRAM_INTERNAL;
2675 goto cleanup;
2676 }
2677
2678 PRINT_HASH ("ClientSignature", ClientSignature, hash_size);
2679
2680 /* ClientProof := ClientKey XOR ClientSignature */
2681 for (k = 0; k < hash_size; k++) {
2682 ClientProof[k] = ClientKey[k] ^ ClientSignature[k];
2683 }
2684
2685 PRINT_HASH ("ClientProof", ClientProof, hash_size);
2686
2687 /* base64-encode ClientProof */
2688 client_proof = (char *) params->utils->malloc(client_proof_len + 1);
2689 if (client_proof == NULL) {
2690 MEMERROR( params->utils );
2691 result = SASL_NOMEM;
2692 goto cleanup;
2693 }
2694
2695 result = params->utils->encode64(ClientProof,
2696 hash_size,
2697 client_proof,
2698 (unsigned int)client_proof_len + 1,
2699 NULL);
2700
2701 if (result != SASL_OK) {
2702 goto cleanup;
2703 }
2704
2705 client_proof[client_proof_len] = '\0';
2706
2707 sprintf(text->out_buf + length_no_proof,
2708 ",p=%s",
2709 client_proof);
2710
2711 *clientout = text->out_buf;
2712 *clientoutlen = (unsigned) strlen(text->out_buf);
2713
2714 result = SASL_CONTINUE;
2715
2716 cleanup:
2717 if (inbuf != NULL) {
2718 params->utils->free(inbuf);
2719 }
2720
2721 if (client_proof != NULL) {
2722 params->utils->free(client_proof);
2723 }
2724
2725 if (cb_encoded != NULL) {
2726 params->utils->free(cb_encoded);
2727 }
2728
2729 if (cb_bin != NULL) {
2730 params->utils->free(cb_bin);
2731 }
2732
2733 return result;
2734 }
2735
2736
2737 static int
scram_client_mech_step3(client_context_t * text,sasl_client_params_t * params,const char * serverin,unsigned serverinlen,sasl_interact_t ** prompt_need,const char ** clientout,unsigned * clientoutlen,sasl_out_params_t * oparams)2738 scram_client_mech_step3(client_context_t *text,
2739 sasl_client_params_t *params,
2740 const char *serverin,
2741 unsigned serverinlen,
2742 sasl_interact_t **prompt_need __attribute__((unused)),
2743 const char **clientout __attribute__((unused)),
2744 unsigned *clientoutlen __attribute__((unused)),
2745 sasl_out_params_t *oparams)
2746 {
2747 char * p;
2748 int result;
2749 size_t server_proof_len;
2750 unsigned exact_server_proof_len;
2751 char DecodedServerProof[EVP_MAX_MD_SIZE + 1];
2752 char ServerKey[EVP_MAX_MD_SIZE];
2753 char ServerSignature[EVP_MAX_MD_SIZE];
2754 unsigned int hash_len = 0;
2755 size_t k, hash_size = EVP_MD_size(text->md);
2756 const char *scram_sasl_mech = scram_sasl_mech_name(hash_size);
2757
2758 if (serverinlen < 3) {
2759 params->utils->seterror(params->utils->conn, 0,
2760 "Invalid %s input expected",
2761 scram_sasl_mech);
2762 return SASL_BADPROT;
2763 }
2764
2765 /* Expecting: 'verifier ["," extensions]' */
2766
2767 if (strncmp(serverin, "v=", 2) != 0) {
2768 params->utils->seterror(params->utils->conn, 0,
2769 "ServerSignature expected in %s input",
2770 scram_sasl_mech);
2771 return SASL_BADPROT;
2772 }
2773
2774 /* Use memchr instead of the original strchr as there is no guarantee that
2775 the input data is NUL terminated */
2776 p = memchr (serverin + 2, ',', serverinlen - 2);
2777 if (p != NULL) {
2778 server_proof_len = p - (serverin + 2) - 1;
2779 } else {
2780 server_proof_len = serverinlen - 2;
2781 }
2782
2783 if (params->utils->decode64(serverin + 2, /* ServerProof */
2784 (unsigned int)server_proof_len,
2785 DecodedServerProof,
2786 hash_size + 1,
2787 &exact_server_proof_len) != SASL_OK) {
2788 params->utils->seterror(params->utils->conn, 0,
2789 "Invalid base64 encoding of the server proof in %s input",
2790 scram_sasl_mech);
2791 result = SASL_BADPROT;
2792 goto cleanup;
2793 }
2794
2795 if (exact_server_proof_len != hash_size) {
2796 params->utils->seterror(params->utils->conn, 0,
2797 "Invalid server proof (truncated) in %s input",
2798 scram_sasl_mech);
2799 result = SASL_BADPROT;
2800 goto cleanup;
2801 }
2802
2803 /* ServerKey := HMAC(SaltedPassword, "Server Key") */
2804 if (HMAC(text->md,
2805 (const unsigned char *)text->SaltedPassword,
2806 hash_size,
2807 (const unsigned char *) SERVER_KEY_CONSTANT,
2808 SERVER_KEY_CONSTANT_LEN,
2809 (unsigned char *)ServerKey,
2810 &hash_len) == NULL) {
2811 params->utils->seterror(params->utils->conn,0,
2812 "HMAC-%s call failed", scram_sasl_mech+6);
2813 result = SASL_SCRAM_INTERNAL;
2814 goto cleanup;
2815 }
2816
2817 /* ServerSignature := HMAC(ServerKey, AuthMessage) */
2818 if (HMAC(text->md,
2819 (const unsigned char *)ServerKey,
2820 hash_size,
2821 (const unsigned char *) text->auth_message,
2822 (int)text->auth_message_len,
2823 (unsigned char *)ServerSignature,
2824 &hash_len) == NULL) {
2825 params->utils->seterror(params->utils->conn,0,
2826 "HMAC-%s call failed", scram_sasl_mech+6);
2827 result = SASL_SCRAM_INTERNAL;
2828 goto cleanup;
2829 }
2830
2831 for (k = 0; k < hash_size; k++) {
2832 if (DecodedServerProof[k] != ServerSignature[k]) {
2833 SETERROR(params->utils, "ServerSignature mismatch");
2834 result = SASL_BADAUTH;
2835 goto cleanup;
2836 }
2837 }
2838
2839 /* set oparams */
2840 oparams->doneflag = 1;
2841 oparams->mech_ssf = 0;
2842 oparams->maxoutbuf = 0;
2843 oparams->encode_context = NULL;
2844 oparams->encode = NULL;
2845 oparams->decode_context = NULL;
2846 oparams->decode = NULL;
2847 oparams->param_version = 0;
2848
2849 result = SASL_OK;
2850
2851 cleanup:
2852 return result;
2853 }
2854
scram_client_mech_step(void * conn_context,sasl_client_params_t * params,const char * serverin,unsigned serverinlen,sasl_interact_t ** prompt_need,const char ** clientout,unsigned * clientoutlen,sasl_out_params_t * oparams)2855 static int scram_client_mech_step(void *conn_context,
2856 sasl_client_params_t *params,
2857 const char *serverin,
2858 unsigned serverinlen,
2859 sasl_interact_t **prompt_need,
2860 const char **clientout,
2861 unsigned *clientoutlen,
2862 sasl_out_params_t *oparams)
2863 {
2864 int result = SASL_FAIL;
2865 client_context_t *text = (client_context_t *) conn_context;
2866 const char *scram_sasl_mech = scram_sasl_mech_name(EVP_MD_size(text->md));
2867
2868 *clientout = NULL;
2869 *clientoutlen = 0;
2870
2871 /* this should be well more than is ever needed */
2872 if (serverinlen > MAX_SERVERIN_LEN) {
2873 params->utils->seterror(params->utils->conn, 0,
2874 "%s input longer than " STRINGIZE((MAX_SERVERIN_LEN)) " bytes",
2875 scram_sasl_mech);
2876 return SASL_BADPROT;
2877 }
2878
2879 switch (text->state) {
2880 case 0:
2881 result = scram_client_mech_step1(text,
2882 params,
2883 serverin,
2884 serverinlen,
2885 prompt_need,
2886 clientout,
2887 clientoutlen,
2888 oparams);
2889 break;
2890
2891 case 1:
2892 result = scram_client_mech_step2(text,
2893 params,
2894 serverin,
2895 serverinlen,
2896 prompt_need,
2897 clientout,
2898 clientoutlen,
2899 oparams);
2900 break;
2901
2902 case 2:
2903 result = scram_client_mech_step3(text,
2904 params,
2905 serverin,
2906 serverinlen,
2907 prompt_need,
2908 clientout,
2909 clientoutlen,
2910 oparams);
2911 break;
2912
2913 default: /* should never get here */
2914 params->utils->log(NULL, SASL_LOG_ERR,
2915 "Invalid %s client step %d\n",
2916 scram_sasl_mech, text->state);
2917 return SASL_FAIL;
2918 }
2919
2920 if (result != SASL_INTERACT) {
2921 text->state++;
2922 }
2923 return result;
2924 }
2925
scram_client_mech_dispose(void * conn_context,const sasl_utils_t * utils)2926 static void scram_client_mech_dispose(void *conn_context,
2927 const sasl_utils_t *utils)
2928 {
2929 client_context_t *text = (client_context_t *) conn_context;
2930
2931 if (!text) return;
2932
2933 /* get rid of all sensitive info */
2934 if (text->free_password) {
2935 _plug_free_secret(utils, &text->password);
2936 text->free_password = 0;
2937 }
2938
2939 if (text->gs2_header) {
2940 utils->free(text->gs2_header);
2941 text->gs2_header = NULL;
2942 }
2943
2944 if (text->out_buf) {
2945 utils->free(text->out_buf);
2946 text->out_buf = NULL;
2947 }
2948
2949 if (text->auth_message) _plug_free_string(utils,&(text->auth_message));
2950 if (text->nonce) _plug_free_string(utils,&(text->nonce));
2951 if (text->salt) utils->free(text->salt);
2952
2953 utils->free(text);
2954 }
2955
2956 static sasl_client_plug_t scram_client_plugins[] =
2957 {
2958 #ifdef HAVE_SHA512
2959 {
2960 "SCRAM-SHA-512", /* mech_name */
2961 0, /* max_ssf */
2962 SASL_SET_HASH_STRENGTH_BITS(512) |
2963 SASL_SEC_NOPLAINTEXT
2964 | SASL_SEC_NOANONYMOUS
2965 | SASL_SEC_NOACTIVE
2966 | SASL_SEC_MUTUAL_AUTH, /* security_flags */
2967 SASL_FEAT_ALLOWS_PROXY
2968 | SASL_FEAT_SUPPORTS_HTTP
2969 | SASL_FEAT_CHANNEL_BINDING, /* features */
2970 NULL, /* required_prompts */
2971 "SHA512", /* glob_context */
2972 &scram_client_mech_new, /* mech_new */
2973 &scram_client_mech_step, /* mech_step */
2974 &scram_client_mech_dispose, /* mech_dispose */
2975 NULL, /* mech_free */
2976 NULL, /* idle */
2977 NULL, /* spare */
2978 NULL /* spare */
2979 },
2980 {
2981 "SCRAM-SHA-384", /* mech_name */
2982 0, /* max_ssf */
2983 SASL_SET_HASH_STRENGTH_BITS(384) |
2984 SASL_SEC_NOPLAINTEXT
2985 | SASL_SEC_NOANONYMOUS
2986 | SASL_SEC_NOACTIVE
2987 | SASL_SEC_MUTUAL_AUTH, /* security_flags */
2988 SASL_FEAT_ALLOWS_PROXY
2989 | SASL_FEAT_SUPPORTS_HTTP
2990 | SASL_FEAT_CHANNEL_BINDING, /* features */
2991 NULL, /* required_prompts */
2992 "SHA384", /* glob_context */
2993 &scram_client_mech_new, /* mech_new */
2994 &scram_client_mech_step, /* mech_step */
2995 &scram_client_mech_dispose, /* mech_dispose */
2996 NULL, /* mech_free */
2997 NULL, /* idle */
2998 NULL, /* spare */
2999 NULL /* spare */
3000 },
3001 {
3002 "SCRAM-SHA-256", /* mech_name */
3003 0, /* max_ssf */
3004 SASL_SET_HASH_STRENGTH_BITS(256) |
3005 SASL_SEC_NOPLAINTEXT
3006 | SASL_SEC_NOANONYMOUS
3007 | SASL_SEC_NOACTIVE
3008 | SASL_SEC_MUTUAL_AUTH, /* security_flags */
3009 SASL_FEAT_ALLOWS_PROXY
3010 | SASL_FEAT_SUPPORTS_HTTP
3011 | SASL_FEAT_CHANNEL_BINDING, /* features */
3012 NULL, /* required_prompts */
3013 "SHA256", /* glob_context */
3014 &scram_client_mech_new, /* mech_new */
3015 &scram_client_mech_step, /* mech_step */
3016 &scram_client_mech_dispose, /* mech_dispose */
3017 NULL, /* mech_free */
3018 NULL, /* idle */
3019 NULL, /* spare */
3020 NULL /* spare */
3021 },
3022 {
3023 "SCRAM-SHA-224", /* mech_name */
3024 0, /* max_ssf */
3025 SASL_SET_HASH_STRENGTH_BITS(224) |
3026 SASL_SEC_NOPLAINTEXT
3027 | SASL_SEC_NOANONYMOUS
3028 | SASL_SEC_NOACTIVE
3029 | SASL_SEC_MUTUAL_AUTH, /* security_flags */
3030 SASL_FEAT_ALLOWS_PROXY
3031 | SASL_FEAT_SUPPORTS_HTTP
3032 | SASL_FEAT_CHANNEL_BINDING, /* features */
3033 NULL, /* required_prompts */
3034 "SHA224", /* glob_context */
3035 &scram_client_mech_new, /* mech_new */
3036 &scram_client_mech_step, /* mech_step */
3037 &scram_client_mech_dispose, /* mech_dispose */
3038 NULL, /* mech_free */
3039 NULL, /* idle */
3040 NULL, /* spare */
3041 NULL /* spare */
3042 },
3043 #endif
3044 {
3045 "SCRAM-SHA-1", /* mech_name */
3046 0, /* max_ssf */
3047 SASL_SET_HASH_STRENGTH_BITS(160) |
3048 SASL_SEC_NOPLAINTEXT
3049 | SASL_SEC_NOANONYMOUS
3050 | SASL_SEC_NOACTIVE
3051 | SASL_SEC_MUTUAL_AUTH, /* security_flags */
3052 SASL_FEAT_ALLOWS_PROXY
3053 | SASL_FEAT_SUPPORTS_HTTP
3054 | SASL_FEAT_CHANNEL_BINDING, /* features */
3055 NULL, /* required_prompts */
3056 "SHA1", /* glob_context */
3057 &scram_client_mech_new, /* mech_new */
3058 &scram_client_mech_step, /* mech_step */
3059 &scram_client_mech_dispose, /* mech_dispose */
3060 NULL, /* mech_free */
3061 NULL, /* idle */
3062 NULL, /* spare */
3063 NULL /* spare */
3064 }
3065 };
3066
scram_client_plug_init(const sasl_utils_t * utils,int maxversion,int * out_version,sasl_client_plug_t ** pluglist,int * plugcount)3067 int scram_client_plug_init(const sasl_utils_t *utils,
3068 int maxversion,
3069 int *out_version,
3070 sasl_client_plug_t **pluglist,
3071 int *plugcount)
3072 {
3073 if (maxversion < SASL_CLIENT_PLUG_VERSION) {
3074 SETERROR( utils, "SCRAM-SHA-* version mismatch");
3075 return SASL_BADVERS;
3076 }
3077
3078 *out_version = SASL_CLIENT_PLUG_VERSION;
3079 *pluglist = scram_client_plugins;
3080 #ifdef HAVE_SHA512
3081 *plugcount = 5;
3082 #else
3083 *plugcount = 1;
3084 #endif
3085
3086 return SASL_OK;
3087 }
3088