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