1 /* OTP SASL plugin
2  * Ken Murchison
3  */
4 /*
5  * Copyright (c) 1998-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 #include <stdio.h>
47 #include <stdlib.h>
48 #ifdef HAVE_UNISTD_H
49 #include <unistd.h>
50 #endif
51 #include <errno.h>
52 #include <string.h>
53 #include <ctype.h>
54 #include <assert.h>
55 
56 #include <openssl/evp.h>
57 #include <openssl/md5.h> /* XXX hack for OpenBSD/OpenSSL cruftiness */
58 
59 #include <sasl.h>
60 #define MD5_H  /* suppress internal MD5 */
61 #include <saslplug.h>
62 
63 #include "plugin_common.h"
64 
65 #ifdef macintosh
66 #include <sasl_otp_plugin_decl.h>
67 #endif
68 
69 /*****************************  Common Section  *****************************/
70 
71 #define OTP_SEQUENCE_MAX	9999
72 #define OTP_SEQUENCE_DEFAULT	499
73 #define OTP_SEQUENCE_REINIT	490
74 #define OTP_SEED_MIN		1
75 #define OTP_SEED_MAX		16
76 #define OTP_HASH_SIZE		8		/* 64 bits */
77 #define OTP_CHALLENGE_MAX	100
78 #define OTP_RESPONSE_MAX	100
79 #define OTP_HEX_TYPE		"hex:"
80 #define OTP_WORD_TYPE		"word:"
81 #define OTP_INIT_HEX_TYPE	"init-hex:"
82 #define OTP_INIT_WORD_TYPE	"init-word:"
83 
84 typedef struct algorithm_option_s {
85     const char *name;		/* name used in challenge/response */
86     int swab;			/* number of bytes to swab (0, 1, 2, 4, 8) */
87     const char *evp_name;	/* name used for lookup in EVP table */
88 } algorithm_option_t;
89 
90 static algorithm_option_t algorithm_options[] = {
91     {"md4",	0,	"md4"},
92     {"md5",	0,	"md5"},
93     {"sha1",	4,	"sha1"},
94     {NULL,	0,	NULL}
95 };
96 
_plug_EVP_MD_CTX_new(const sasl_utils_t * utils)97 static EVP_MD_CTX *_plug_EVP_MD_CTX_new(const sasl_utils_t *utils)
98 {
99     utils->log(NULL, SASL_LOG_DEBUG, "_plug_EVP_MD_CTX_new()");
100 
101 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
102     return EVP_MD_CTX_new();
103 #else
104     return utils->malloc(sizeof(EVP_MD_CTX));
105 #endif
106 }
107 
_plug_EVP_MD_CTX_free(EVP_MD_CTX * ctx,const sasl_utils_t * utils)108 static void _plug_EVP_MD_CTX_free(EVP_MD_CTX *ctx, const sasl_utils_t *utils)
109 {
110     utils->log(NULL, SASL_LOG_DEBUG, "_plug_EVP_MD_CTX_free()");
111 
112 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
113     EVP_MD_CTX_free(ctx);
114 #else
115     utils->free(ctx);
116 #endif
117 }
118 
119 /* Convert the binary data into ASCII hex */
bin2hex(unsigned char * bin,int binlen,char * hex)120 void bin2hex(unsigned char *bin, int binlen, char *hex)
121 {
122     int i;
123     unsigned char c;
124 
125     for (i = 0; i < binlen; i++) {
126 	c = (bin[i] >> 4) & 0xf;
127 	hex[i*2] = (c > 9) ? ('a' + c - 10) : ('0' + c);
128 	c = bin[i] & 0xf;
129 	hex[i*2+1] = (c > 9) ? ('a' + c - 10) : ('0' + c);
130     }
131     hex[i*2] = '\0';
132 }
133 
134 /*
135  * Hash the data using the given algorithm and fold it into 64 bits,
136  * swabbing bytes if necessary.
137  */
otp_hash(const EVP_MD * md,char * in,size_t inlen,unsigned char * out,int swab,EVP_MD_CTX * mdctx)138 static void otp_hash(const EVP_MD *md, char *in, size_t inlen,
139 		     unsigned char *out, int swab, EVP_MD_CTX *mdctx)
140 {
141     unsigned char hash[EVP_MAX_MD_SIZE];
142     unsigned int i;
143     int j;
144     unsigned hashlen;
145 
146     EVP_DigestInit(mdctx, md);
147     EVP_DigestUpdate(mdctx, in, inlen);
148     EVP_DigestFinal(mdctx, hash, &hashlen);
149 
150     /* Fold the result into 64 bits */
151     for (i = OTP_HASH_SIZE; i < hashlen; i++) {
152 	hash[i % OTP_HASH_SIZE] ^= hash[i];
153     }
154 
155     /* Swab bytes */
156     if (swab) {
157 	for (i = 0; i < OTP_HASH_SIZE;) {
158 	    for (j = swab-1; j > -swab; i++, j-=2)
159 		out[i] = hash[i+j];
160 	}
161     }
162     else
163 	memcpy(out, hash, OTP_HASH_SIZE);
164 }
165 
generate_otp(const sasl_utils_t * utils,algorithm_option_t * alg,unsigned seq,char * seed,unsigned char * secret,unsigned secret_len,unsigned char * otp)166 static int generate_otp(const sasl_utils_t *utils,
167 			algorithm_option_t *alg, unsigned seq, char *seed,
168 			unsigned char *secret, unsigned secret_len,
169                         unsigned char *otp)
170 {
171     const EVP_MD *md;
172     EVP_MD_CTX *mdctx = NULL;
173     char *key = NULL;
174     int r = SASL_OK;
175 
176     if (!(md = EVP_get_digestbyname(alg->evp_name))) {
177 	utils->seterror(utils->conn, 0,
178 			"OTP algorithm %s is not available", alg->evp_name);
179 	return SASL_FAIL;
180     }
181 
182     if ((mdctx = _plug_EVP_MD_CTX_new(utils)) == NULL) {
183 	SETERROR(utils, "cannot allocate MD CTX");
184 	r = SASL_NOMEM;
185         goto done;
186     }
187 
188     if ((key = utils->malloc(strlen(seed) + secret_len + 1)) == NULL) {
189 	SETERROR(utils, "cannot allocate OTP key");
190 	r = SASL_NOMEM;
191         goto done;
192     }
193 
194     /* initial step */
195     sprintf(key, "%s%.*s", seed, secret_len, secret);
196     otp_hash(md, key, strlen(key), otp, alg->swab, mdctx);
197 
198     /* computation step */
199     while (seq-- > 0)
200         otp_hash(md, (char *) otp, OTP_HASH_SIZE, otp, alg->swab, mdctx);
201 
202 done:
203     if (key) utils->free(key);
204     if (mdctx) _plug_EVP_MD_CTX_free(mdctx, utils);
205 
206     return r;
207 }
208 
parse_challenge(const sasl_utils_t * utils,char * chal,algorithm_option_t ** alg,unsigned * seq,char * seed,int is_init)209 static int parse_challenge(const sasl_utils_t *utils,
210 			   char *chal, algorithm_option_t **alg,
211 			   unsigned *seq, char *seed, int is_init)
212 {
213     char *c;
214     algorithm_option_t *opt;
215     int n;
216 
217     c = chal;
218 
219     /* eat leading whitespace */
220     while (*c && isspace((int) *c)) c++;
221 
222     if (!is_init) {
223 	/* check the prefix */
224 	if (!*c || strncmp(c, "otp-", 4)) {
225 	    SETERROR(utils, "not an OTP challenge");
226 	    return SASL_BADPROT;
227 	}
228 
229 	/* skip the prefix */
230 	c += 4;
231     }
232 
233     /* find the algorithm */
234     opt = algorithm_options;
235     while (opt->name) {
236 	if (!strncmp(c, opt->name, strlen(opt->name))) {
237 	    break;
238 	}
239 	opt++;
240     }
241 
242     /* didn't find the algorithm in our list */
243     if (!opt->name) {
244 	utils->seterror(utils->conn, 0, "OTP algorithm '%s' not supported", c);
245 	return SASL_BADPROT;
246     }
247 
248     /* skip algorithm name */
249     c += strlen(opt->name);
250     *alg = opt;
251 
252     /* eat whitespace */
253     if (!isspace((int) *c)) {
254 	SETERROR(utils, "no whitespace between OTP algorithm and sequence");
255 	return SASL_BADPROT;
256     }
257     while (*c && isspace((int) *c)) c++;
258 
259     /* grab the sequence */
260     if ((*seq = strtoul(c, &c, 10)) > OTP_SEQUENCE_MAX) {
261 	utils->seterror(utils->conn, 0, "sequence > %u", OTP_SEQUENCE_MAX);
262 	return SASL_BADPROT;
263     }
264 
265     /* eat whitespace */
266     if (!isspace((int) *c)) {
267 	SETERROR(utils, "no whitespace between OTP sequence and seed");
268 	return SASL_BADPROT;
269     }
270     while (*c && isspace((int) *c)) c++;
271 
272     /* grab the seed, converting to lowercase as we go */
273     n = 0;
274     while (*c && isalnum((int) *c) && (n < OTP_SEED_MAX))
275 	seed[n++] = tolower((int) *c++);
276     if (n > OTP_SEED_MAX) {
277 	utils->seterror(utils->conn, 0, "OTP seed length > %u", OTP_SEED_MAX);
278 	return SASL_BADPROT;
279     }
280     else if (n < OTP_SEED_MIN) {
281 	utils->seterror(utils->conn, 0, "OTP seed length < %u", OTP_SEED_MIN);
282 	return SASL_BADPROT;
283     }
284     seed[n] = '\0';
285 
286     if (!is_init) {
287 	/* eat whitespace */
288 	if (!isspace((int) *c)) {
289 	    SETERROR(utils, "no whitespace between OTP seed and extensions");
290 	    return SASL_BADPROT;
291 	}
292 	while (*c && isspace((int) *c)) c++;
293 
294 	/* make sure this is an extended challenge */
295 	if (strncmp(c, "ext", 3) ||
296 	    (*(c+=3) &&
297 	     !(isspace((int) *c) || (*c == ',') ||
298 	       (*c == '\r') || (*c == '\n')))) {
299 	    SETERROR(utils, "not an OTP extended challenge");
300 	    return SASL_BADPROT;
301 	}
302     }
303 
304     return SASL_OK;
305 }
306 
307 static void
otp_common_mech_free(void * global_context,const sasl_utils_t * utils)308 otp_common_mech_free(void *global_context __attribute__((unused)),
309 		     const sasl_utils_t *utils __attribute__((unused)))
310 {
311     /* Don't call EVP_cleanup(); here, as this might confuse the calling
312        application if it also uses OpenSSL */
313 }
314 
315 /*****************************  Server Section  *****************************/
316 
317 #ifdef  HAVE_OPIE
318 #include <opie.h>
319 #endif
320 
321 typedef struct server_context {
322     int state;
323 
324     char *authid;
325     int locked;				/* is the user's secret locked? */
326     algorithm_option_t *alg;
327 #ifdef HAVE_OPIE
328     struct opie opie;
329 #else
330     char *realm;
331     unsigned seq;
332     char seed[OTP_SEED_MAX+1];
333     unsigned char otp[OTP_HASH_SIZE];
334     time_t timestamp;			/* time we locked the secret */
335 #endif /* HAVE_OPIE */
336 
337     char *out_buf;
338     unsigned out_buf_len;
339 } server_context_t;
340 
otp_server_mech_new(void * glob_context,sasl_server_params_t * sparams,const char * challenge,unsigned challen,void ** conn_context)341 static int otp_server_mech_new(void *glob_context __attribute__((unused)),
342 			       sasl_server_params_t *sparams,
343 			       const char *challenge __attribute__((unused)),
344 			       unsigned challen __attribute__((unused)),
345 			       void **conn_context)
346 {
347     server_context_t *text;
348 
349     /* holds state are in */
350     text = sparams->utils->malloc(sizeof(server_context_t));
351     if (text == NULL) {
352 	MEMERROR(sparams->utils);
353 	return SASL_NOMEM;
354     }
355 
356     memset(text, 0, sizeof(server_context_t));
357 
358     text->state = 1;
359 
360     *conn_context = text;
361 
362     return SASL_OK;
363 }
364 
365 #ifdef HAVE_OPIE
366 
367 #ifndef OPIE_KEYFILE
368 #define OPIE_KEYFILE "/etc/opiekeys"
369 #endif
370 
opie_server_mech_step(void * conn_context,sasl_server_params_t * params,const char * clientin,unsigned clientinlen,const char ** serverout,unsigned * serveroutlen,sasl_out_params_t * oparams)371 static int opie_server_mech_step(void *conn_context,
372 				 sasl_server_params_t *params,
373 				 const char *clientin,
374 				 unsigned clientinlen,
375 				 const char **serverout,
376 				 unsigned *serveroutlen,
377 				 sasl_out_params_t *oparams)
378 {
379     server_context_t *text = (server_context_t *) conn_context;
380 
381     *serverout = NULL;
382     *serveroutlen = 0;
383 
384     if (text == NULL) {
385 	return SASL_BADPROT;
386     }
387 
388     switch (text->state) {
389 
390     case 1: {
391 	const char *authzid;
392 	const char *authid;
393 	size_t authid_len;
394 	unsigned lup = 0;
395 	int result;
396 
397 	/* should have received authzid NUL authid */
398 
399 	/* get authzid */
400 	authzid = clientin;
401 	while ((lup < clientinlen) && (clientin[lup] != 0)) ++lup;
402 
403 	if (lup >= clientinlen) {
404 	    SETERROR(params->utils, "Can only find OTP authzid (no authid)");
405 	    return SASL_BADPROT;
406 	}
407 
408 	/* get authid */
409 	++lup;
410 	authid = clientin + lup;
411 	while ((lup < clientinlen) && (clientin[lup] != 0)) ++lup;
412 
413 	authid_len = clientin + lup - authid;
414 
415 	if (lup != clientinlen) {
416 	    SETERROR(params->utils,
417 		     "Got more data than we were expecting in the OTP plugin\n");
418 	    return SASL_BADPROT;
419 	}
420 
421 	text->authid = params->utils->malloc(authid_len + 1);
422 	if (text->authid == NULL) {
423 	    MEMERROR(params->utils);
424 	    return SASL_NOMEM;
425 	}
426 
427 	/* we can't assume that authen is null-terminated */
428 	strncpy(text->authid, authid, authid_len);
429 	text->authid[authid_len] = '\0';
430 
431 	result = params->canon_user(params->utils->conn, text->authid, 0,
432 				    SASL_CU_AUTHID, oparams);
433 	if (result != SASL_OK) return result;
434 
435 	result = params->canon_user(params->utils->conn,
436 				    strlen(authzid) ? authzid : text->authid,
437 				    0, SASL_CU_AUTHZID, oparams);
438 	if (result != SASL_OK) return result;
439 
440 	result = _plug_buf_alloc(params->utils, &(text->out_buf),
441 				 &(text->out_buf_len), OTP_CHALLENGE_MAX+1);
442 	if (result != SASL_OK) return result;
443 
444 	/* create challenge - return sasl_continue on success */
445 	result = opiechallenge(&text->opie, text->authid, text->out_buf);
446 
447 	switch (result) {
448 	case 0:
449 	    text->locked = 1;
450 
451 	    *serverout = text->out_buf;
452 	    *serveroutlen = strlen(text->out_buf);
453 
454 	    text->state = 2;
455 	    return SASL_CONTINUE;
456 
457 	case 1:
458 	    SETERROR(params->utils, "opiechallenge: user not found or locked");
459 	    return SASL_NOUSER;
460 
461 	default:
462 	    SETERROR(params->utils,
463 		     "opiechallenge: system error (file, memory, I/O)");
464 	    return SASL_FAIL;
465 	}
466     }
467 
468     case 2: {
469 	char response[OPIE_RESPONSE_MAX+1];
470 	int result;
471 
472 	/* should have received extended response,
473 	   but we'll take anything that we can verify */
474 
475 	if (clientinlen > OPIE_RESPONSE_MAX) {
476 	    SETERROR(params->utils, "response too long");
477 	    return SASL_BADPROT;
478 	}
479 
480 	/* we can't assume that the response is null-terminated */
481 	strncpy(response, clientin, clientinlen);
482 	response[clientinlen] = '\0';
483 
484 	/* verify response */
485 	result = opieverify(&text->opie, response);
486 	text->locked = 0;
487 
488 	switch (result) {
489 	case 0:
490 	    /* set oparams */
491 	    oparams->doneflag = 1;
492 	    oparams->mech_ssf = 0;
493 	    oparams->maxoutbuf = 0;
494 	    oparams->encode_context = NULL;
495 	    oparams->encode = NULL;
496 	    oparams->decode_context = NULL;
497 	    oparams->decode = NULL;
498 	    oparams->param_version = 0;
499 
500 	    return SASL_OK;
501 
502 	case 1:
503 	    SETERROR(params->utils, "opieverify: invalid/incorrect response");
504 	    return SASL_BADAUTH;
505 
506 	default:
507 	    SETERROR(params->utils,
508 		     "opieverify: system error (file, memory, I/O)");
509 	    return SASL_FAIL;
510 	}
511     }
512 
513     default:
514 	params->utils->log(NULL, SASL_LOG_ERR,
515 			   "Invalid OTP server step %d\n", text->state);
516 	return SASL_FAIL;
517     }
518 
519     return SASL_FAIL; /* should never get here */
520 }
521 
opie_server_mech_dispose(void * conn_context,const sasl_utils_t * utils)522 static void opie_server_mech_dispose(void *conn_context,
523 				     const sasl_utils_t *utils)
524 {
525     server_context_t *text = (server_context_t *) conn_context;
526 
527     if (!text) return;
528 
529     /* if we created a challenge, but bailed before the verification of the
530        response, do a verify here to release the lock on the user key */
531     if (text->locked) opieverify(&text->opie, "");
532 
533     if (text->authid) _plug_free_string(utils, &(text->authid));
534 
535     if (text->out_buf) utils->free(text->out_buf);
536 
537     utils->free(text);
538 }
539 
opie_mech_avail(void * glob_context,sasl_server_params_t * sparams,void ** conn_context)540 static int opie_mech_avail(void *glob_context __attribute__((unused)),
541 			   sasl_server_params_t *sparams,
542 			   void **conn_context __attribute__((unused)))
543 {
544     const char *fname;
545     unsigned int len;
546 
547     sparams->utils->getopt(sparams->utils->getopt_context,
548 			   "OTP", "opiekeys", &fname, &len);
549 
550     if (!fname) fname = OPIE_KEYFILE;
551 
552     if (access(fname, R_OK|W_OK) != 0) {
553 	sparams->utils->log(NULL, SASL_LOG_ERR,
554 			    "OTP unavailable because "
555 			    "can't read/write key database %s: %m",
556 			    fname, errno);
557 	return SASL_NOMECH;
558     }
559 
560     return SASL_OK;
561 }
562 
563 static sasl_server_plug_t otp_server_plugins[] =
564 {
565     {
566 	"OTP",
567 	0,
568 	SASL_SEC_NOPLAINTEXT
569 	| SASL_SEC_NOANONYMOUS
570 	| SASL_SEC_FORWARD_SECRECY,
571 	SASL_FEAT_WANT_CLIENT_FIRST
572 	| SASL_FEAT_DONTUSE_USERPASSWD
573 	| SASL_FEAT_ALLOWS_PROXY,
574 	NULL,
575 	&otp_server_mech_new,
576 	&opie_server_mech_step,
577 	&opie_server_mech_dispose,
578 	&otp_common_mech_free,
579 	NULL,
580 	NULL,
581 	NULL,
582 	&opie_mech_avail,
583 	NULL
584     }
585 };
586 #else /* HAVE_OPIE */
587 
588 #include "otp.h"
589 
590 #define OTP_MDA_DEFAULT		"md5"
591 #define OTP_LOCK_TIMEOUT	5 * 60		/* 5 minutes */
592 
593 /* Convert the ASCII hex into binary data */
hex2bin(char * hex,unsigned char * bin,int binlen)594 int hex2bin(char *hex, unsigned char *bin, int binlen)
595 {
596     int i;
597     char *c;
598     unsigned char msn, lsn;
599 
600     memset(bin, 0, binlen);
601 
602     for (c = hex, i = 0; i < binlen; c++) {
603 	/* whitespace */
604 	if (isspace((int) *c))
605 	    continue;
606 	/* end of string, or non-hex char */
607 	if (!*c || !*(c+1) || !isxdigit((int) *c))
608 	    break;
609 
610 	msn = (*c > '9') ? tolower((int) *c) - 'a' + 10 : *c - '0';
611 	c++;
612 	lsn = (*c > '9') ? tolower((int) *c) - 'a' + 10 : *c - '0';
613 
614 	bin[i++] = (unsigned char) (msn << 4) | lsn;
615     }
616 
617     return (i < binlen) ? SASL_BADAUTH : SASL_OK;
618 }
619 
make_secret(const sasl_utils_t * utils,const char * alg,unsigned seq,char * seed,unsigned char * otp,time_t timeout,sasl_secret_t ** secret)620 static int make_secret(const sasl_utils_t *utils, const char *alg,
621                        unsigned seq, char *seed, unsigned char *otp,
622 		       time_t timeout, sasl_secret_t **secret)
623 {
624     size_t sec_len;
625     char *data;
626     char buf[2*OTP_HASH_SIZE+1];
627 
628     /*
629      * secret is stored as:
630      *
631      * <alg> \t <seq> \t <seed> \t <otp> \t <timeout> \0
632      *
633      * <timeout> is used as a "lock" when an auth is in progress
634      * we just set it to zero here (no lock)
635      */
636     sec_len = strlen(alg)+1+4+1+strlen(seed)+1+2*OTP_HASH_SIZE+1+20+1;
637     *secret = utils->malloc(sizeof(sasl_secret_t)+sec_len);
638     if (!*secret) {
639 	return SASL_NOMEM;
640     }
641 
642     (*secret)->len = (unsigned) sec_len;
643     data = (char *) (*secret)->data;
644 
645     bin2hex(otp, OTP_HASH_SIZE, buf);
646     buf[2*OTP_HASH_SIZE] = '\0';
647 
648     sprintf(data, "%s\t%04d\t%s\t%s\t%020ld",
649 	    alg, seq, seed, buf, timeout);
650 
651     return SASL_OK;
652 }
653 
parse_secret(const sasl_utils_t * utils,char * secret,size_t seclen,char * alg,unsigned * seq,char * seed,unsigned char * otp,time_t * timeout)654 static int parse_secret(const sasl_utils_t *utils,
655 			char *secret, size_t seclen,
656 			char *alg, unsigned *seq, char *seed,
657 			unsigned char *otp,
658 			time_t *timeout)
659 {
660     if (strlen(secret) < seclen) {
661         char *c;
662 
663 	/*
664 	 * old-style (binary) secret is stored as:
665 	 *
666 	 * <alg> \0 <seq> \0 <seed> \0 <otp> <timeout>
667 	 *
668 	 */
669 
670 	if (seclen < (3+1+1+1+OTP_SEED_MIN+1+OTP_HASH_SIZE+sizeof(time_t))) {
671 	    SETERROR(utils, "OTP secret too short");
672 	    return SASL_FAIL;
673 	}
674 
675 	c = secret;
676 
677 	strcpy(alg, (char*) c);
678 	c += strlen(alg)+1;
679 
680 	*seq = strtoul(c, NULL, 10);
681 	c += 5;
682 
683 	strcpy(seed, (char*) c);
684 	c += strlen(seed)+1;
685 
686 	memcpy(otp, c, OTP_HASH_SIZE);
687 	c += OTP_HASH_SIZE;
688 
689 	memcpy(timeout, c, sizeof(time_t));
690 
691 	return SASL_OK;
692     }
693 
694     else {
695 	char buf[2*OTP_HASH_SIZE+1];
696 
697 	/*
698 	 * new-style (ASCII) secret is stored as:
699 	 *
700 	 * <alg> \t <seq> \t <seed> \t <otp> \t <timeout> \0
701 	 *
702 	 */
703 
704 	if (seclen < (3+1+1+1+OTP_SEED_MIN+1+2*OTP_HASH_SIZE+1+20)) {
705 	    SETERROR(utils, "OTP secret too short");
706 	    return SASL_FAIL;
707 	}
708 
709 	sscanf(secret, "%s\t%04d\t%s\t%s\t%020ld",
710 	       alg, seq, seed, buf, timeout);
711 
712 	hex2bin(buf, otp, OTP_HASH_SIZE);
713 
714 	return SASL_OK;
715     }
716 }
717 
718 /* Compare two string pointers */
strptrcasecmp(const void * arg1,const void * arg2)719 static int strptrcasecmp(const void *arg1, const void *arg2)
720 {
721     return (strcasecmp(*((char**) arg1), *((char**) arg2)));
722 }
723 
724 /* Convert the 6 words into binary data */
word2bin(const sasl_utils_t * utils,char * words,unsigned char * bin,const EVP_MD * md,EVP_MD_CTX * mdctx)725 static int word2bin(const sasl_utils_t *utils,
726 		    char *words, unsigned char *bin, const EVP_MD *md,
727                     EVP_MD_CTX *mdctx)
728 {
729     int i, j;
730     char *c, *word, buf[OTP_RESPONSE_MAX+1];
731     void *base;
732     int nmemb;
733     unsigned long x = 0;
734     unsigned char bits[OTP_HASH_SIZE+1]; /* 1 for checksum */
735     unsigned char chksum;
736     int bit, fbyte, lbyte;
737     const char **str_ptr;
738     int alt_dict = 0;
739 
740     /* this is a destructive operation, so make a work copy */
741     strcpy(buf, words);
742     memset(bits, 0, 9);
743 
744     for (c = buf, bit = 0, i = 0; i < 6; i++, c++, bit+=11) {
745 	while (*c && isspace((int) *c)) c++;
746 	word = c;
747 	while (*c && isalpha((int) *c)) c++;
748 	if (!*c && i < 5) break;
749 	*c = '\0';
750 	if (strlen(word) < 1 || strlen(word) > 4) {
751 	    utils->log(NULL, SASL_LOG_DEBUG,
752 		       "incorrect word length '%s'", word);
753 	    return SASL_BADAUTH;
754 	}
755 
756 	/* standard dictionary */
757 	if (!alt_dict) {
758 	    if (strlen(word) < 4) {
759 		base = otp_std_dict;
760 		nmemb = OTP_4LETTER_OFFSET;
761 	    }
762 	    else {
763 		base = otp_std_dict + OTP_4LETTER_OFFSET;
764 		nmemb = OTP_STD_DICT_SIZE - OTP_4LETTER_OFFSET;
765 	    }
766 
767 	    str_ptr = (const char**) bsearch((void*) &word, base, nmemb,
768 					     sizeof(const char*),
769 					     strptrcasecmp);
770 	    if (str_ptr) {
771 		x = (unsigned long) (str_ptr - otp_std_dict);
772 	    }
773 	    else if (i == 0) {
774 		/* couldn't find first word, try alternate dictionary */
775 		alt_dict = 1;
776 	    }
777 	    else {
778 		utils->log(NULL, SASL_LOG_DEBUG,
779 			   "word '%s' not found in dictionary", word);
780 		return SASL_BADAUTH;
781 	    }
782 	}
783 
784 	/* alternate dictionary */
785 	if (alt_dict) {
786 	    unsigned char hash[EVP_MAX_MD_SIZE];
787 	    unsigned hashlen;
788 
789 	    EVP_DigestInit(mdctx, md);
790 	    EVP_DigestUpdate(mdctx, word, strlen(word));
791 	    EVP_DigestFinal(mdctx, hash, &hashlen);
792 
793 	    /* use lowest 11 bits */
794 	    x = ((hash[hashlen-2] & 0x7) << 8) | hash[hashlen-1];
795 	}
796 
797 	/* left align 11 bits on byte boundary */
798 	x <<= (8 - ((bit+11) % 8));
799 	/* first output byte containing some of our 11 bits */
800 	fbyte = bit / 8;
801 	/* last output byte containing some of our 11 bits */
802 	lbyte = (bit+11) / 8;
803 	/* populate the output bytes with the 11 bits */
804 	for (j = lbyte; j >= fbyte; j--, x >>= 8)
805 	    bits[j] |= (unsigned char) (x & 0xff);
806     }
807 
808     if (i < 6) {
809 	utils->log(NULL, SASL_LOG_DEBUG, "not enough words (%d)", i);
810 	return SASL_BADAUTH;
811     }
812 
813     /* see if the 2-bit checksum is correct */
814     for (chksum = 0, i = 0; i < 8; i++) {
815 	for (j = 0; j < 4; j++) {
816 	    chksum += ((bits[i] >> (2 * j)) & 0x3);
817 	}
818     }
819     chksum <<= 6;
820 
821     if (chksum != bits[8]) {
822 	utils->log(NULL, SASL_LOG_DEBUG, "incorrect parity");
823 	return SASL_BADAUTH;
824     }
825 
826     memcpy(bin, bits, OTP_HASH_SIZE);
827 
828     return SASL_OK;
829 }
830 
verify_response(server_context_t * text,const sasl_utils_t * utils,char * response)831 static int verify_response(server_context_t *text, const sasl_utils_t *utils,
832 			   char *response)
833 {
834     const EVP_MD *md;
835     EVP_MD_CTX *mdctx = NULL;
836     char *c;
837     int do_init = 0;
838     unsigned char cur_otp[OTP_HASH_SIZE], prev_otp[OTP_HASH_SIZE];
839     int r;
840 
841     /* find the MDA */
842     if (!(md = EVP_get_digestbyname(text->alg->evp_name))) {
843 	utils->seterror(utils->conn, 0,
844 			"OTP algorithm %s is not available",
845 			text->alg->evp_name);
846 	return SASL_FAIL;
847     }
848 
849     if ((mdctx = _plug_EVP_MD_CTX_new(utils)) == NULL) {
850 	SETERROR(utils, "cannot allocate MD CTX");
851 	return SASL_NOMEM;
852     }
853 
854     /* eat leading whitespace */
855     c = response;
856     while (isspace((int) *c)) c++;
857 
858     if (strchr(c, ':')) {
859 	if (!strncasecmp(c, OTP_HEX_TYPE, strlen(OTP_HEX_TYPE))) {
860 	    r = hex2bin(c+strlen(OTP_HEX_TYPE), cur_otp, OTP_HASH_SIZE);
861 	}
862 	else if (!strncasecmp(c, OTP_WORD_TYPE, strlen(OTP_WORD_TYPE))) {
863 	    r = word2bin(utils, c+strlen(OTP_WORD_TYPE), cur_otp, md, mdctx);
864 	}
865 	else if (!strncasecmp(c, OTP_INIT_HEX_TYPE,
866 			      strlen(OTP_INIT_HEX_TYPE))) {
867 	    do_init = 1;
868 	    r = hex2bin(c+strlen(OTP_INIT_HEX_TYPE), cur_otp, OTP_HASH_SIZE);
869 	}
870 	else if (!strncasecmp(c, OTP_INIT_WORD_TYPE,
871 			      strlen(OTP_INIT_WORD_TYPE))) {
872 	    do_init = 1;
873 	    r = word2bin(utils, c+strlen(OTP_INIT_WORD_TYPE), cur_otp, md, mdctx);
874 	}
875 	else {
876 	    SETERROR(utils, "unknown OTP extended response type");
877 	    r = SASL_BADAUTH;
878 	}
879     }
880     else {
881 	/* standard response, try word first, and then hex */
882 	r = word2bin(utils, c, cur_otp, md, mdctx);
883 	if (r != SASL_OK)
884 	    r = hex2bin(c, cur_otp, OTP_HASH_SIZE);
885     }
886 
887     if (r == SASL_OK) {
888 	/* do one more hash (previous otp) and compare to stored otp */
889 	otp_hash(md, (char *) cur_otp, OTP_HASH_SIZE,
890                  prev_otp, text->alg->swab, mdctx);
891 
892 	if (!memcmp(prev_otp, text->otp, OTP_HASH_SIZE)) {
893 	    /* update the secret with this seq/otp */
894 	    memcpy(text->otp, cur_otp, OTP_HASH_SIZE);
895 	    text->seq--;
896 	    r = SASL_OK;
897 	}
898 	else
899 	    r = SASL_BADAUTH;
900     }
901 
902     /* if this is an init- attempt, let's check it out */
903     if (r == SASL_OK && do_init) {
904 	char *new_chal = NULL, *new_resp = NULL;
905 	algorithm_option_t *alg;
906 	unsigned seq;
907 	char seed[OTP_SEED_MAX+1];
908 	unsigned char new_otp[OTP_HASH_SIZE];
909 
910 	/* find the challenge and response fields */
911 	new_chal = strchr(c+strlen(OTP_INIT_WORD_TYPE), ':');
912 	if (new_chal) {
913 	    *new_chal++ = '\0';
914 	    new_resp = strchr(new_chal, ':');
915 	    if (new_resp)
916 		*new_resp++ = '\0';
917 	}
918 
919 	if (!(new_chal && new_resp)) {
920 	    r = SASL_BADAUTH;
921             goto done;
922         }
923 
924 	if ((r = parse_challenge(utils, new_chal, &alg, &seq, seed, 1))
925 	    != SASL_OK) {
926             goto done;
927 	}
928 
929 	if (seq < 1 || !strcasecmp(seed, text->seed)) {
930 	    r = SASL_BADAUTH;
931             goto done;
932         }
933 
934 	/* find the MDA */
935 	if (!(md = EVP_get_digestbyname(alg->evp_name))) {
936 	    utils->seterror(utils->conn, 0,
937 			    "OTP algorithm %s is not available",
938 			    alg->evp_name);
939 	    r = SASL_BADAUTH;
940             goto done;
941 	}
942 
943 	if (!strncasecmp(c, OTP_INIT_HEX_TYPE, strlen(OTP_INIT_HEX_TYPE))) {
944 	    r = hex2bin(new_resp, new_otp, OTP_HASH_SIZE);
945 	}
946 	else if (!strncasecmp(c, OTP_INIT_WORD_TYPE,
947 			      strlen(OTP_INIT_WORD_TYPE))) {
948 	    r = word2bin(utils, new_resp, new_otp, md, mdctx);
949 	}
950 
951 	if (r == SASL_OK) {
952 	    /* setup for new secret */
953 	    text->alg = alg;
954 	    text->seq = seq;
955 	    strcpy(text->seed, seed);
956 	    memcpy(text->otp, new_otp, OTP_HASH_SIZE);
957 	}
958     }
959 
960   done:
961     if (mdctx) _plug_EVP_MD_CTX_free(mdctx, utils);
962 
963     return r;
964 }
965 
otp_server_mech_step1(server_context_t * text,sasl_server_params_t * params,const char * clientin,unsigned clientinlen,const char ** serverout,unsigned * serveroutlen,sasl_out_params_t * oparams)966 static int otp_server_mech_step1(server_context_t *text,
967 				 sasl_server_params_t *params,
968 				 const char *clientin,
969 				 unsigned clientinlen,
970 				 const char **serverout,
971 				 unsigned *serveroutlen,
972 				 sasl_out_params_t *oparams)
973 {
974     const char *authzid;
975     const char *authidp;
976     size_t authid_len;
977     unsigned lup = 0;
978     int result, n;
979     const char *lookup_request[] = { "*cmusaslsecretOTP",
980 				     NULL };
981     const char *store_request[] = { "cmusaslsecretOTP",
982 				    NULL };
983     struct propval auxprop_values[2];
984     char mda[10];
985     time_t timeout;
986     sasl_secret_t *sec = NULL;
987     struct propctx *propctx = NULL;
988 
989     /* should have received authzid NUL authid */
990 
991     /* get authzid */
992     authzid = clientin;
993     while ((lup < clientinlen) && (clientin[lup] != 0)) ++lup;
994 
995     if (lup >= clientinlen) {
996 	SETERROR(params->utils, "Can only find OTP authzid (no authid)");
997 	return SASL_BADPROT;
998     }
999 
1000     /* get authid */
1001     ++lup;
1002     authidp = clientin + lup;
1003     while ((lup < clientinlen) && (clientin[lup] != 0)) ++lup;
1004 
1005     authid_len = clientin + lup - authidp;
1006 
1007     if (lup != clientinlen) {
1008 	SETERROR(params->utils,
1009 		 "Got more data than we were expecting in the OTP plugin\n");
1010 	return SASL_BADPROT;
1011     }
1012 
1013     text->authid = params->utils->malloc(authid_len + 1);
1014     if (text->authid == NULL) {
1015 	MEMERROR(params->utils);
1016 	return SASL_NOMEM;
1017     }
1018 
1019     /* we can't assume that authid is null-terminated */
1020     strncpy(text->authid, authidp, authid_len);
1021     text->authid[authid_len] = '\0';
1022 
1023     n = 0;
1024     do {
1025 	/* Get user secret */
1026 	result = params->utils->prop_request(params->propctx,
1027 					     lookup_request);
1028 	if (result != SASL_OK) return result;
1029 
1030 	/* this will trigger the getting of the aux properties.
1031 	   Must use the fully qualified authid here */
1032 	result = params->canon_user(params->utils->conn, text->authid, 0,
1033 				    SASL_CU_AUTHID, oparams);
1034 	if (result != SASL_OK) return result;
1035 
1036 	result = params->canon_user(params->utils->conn,
1037 				    strlen(authzid) ? authzid : text->authid,
1038 				    0, SASL_CU_AUTHZID, oparams);
1039 	if (result != SASL_OK) return result;
1040 
1041 	result = params->utils->prop_getnames(params->propctx,
1042 					      lookup_request,
1043 					      auxprop_values);
1044 	if (result < 0 ||
1045 	    (!auxprop_values[0].name || !auxprop_values[0].values)) {
1046 	    /* We didn't find this username */
1047 	    SETERROR(params->utils, "no OTP secret in database");
1048 	    result = params->transition ? SASL_TRANS : SASL_NOUSER;
1049 	    return (result);
1050 	}
1051 
1052 	if (auxprop_values[0].name && auxprop_values[0].values) {
1053 	    result = parse_secret(params->utils,
1054 				  (char*) auxprop_values[0].values[0],
1055 				  auxprop_values[0].valsize,
1056 				  mda, &text->seq, text->seed, text->otp,
1057 				  &timeout);
1058 
1059 	    if (result != SASL_OK) return result;
1060 	} else {
1061 	    SETERROR(params->utils, "don't have an OTP secret");
1062 	    return SASL_FAIL;
1063 	}
1064 
1065 	text->timestamp = time(0);
1066     }
1067     /*
1068      * check lock timeout
1069      *
1070      * we try 10 times in 1 second intervals in order to give the other
1071      * auth attempt time to finish
1072      */
1073     while ((text->timestamp < timeout) && (n++ < 10) && !sleep(1));
1074 
1075     if (text->timestamp < timeout) {
1076 	SETERROR(params->utils,
1077 		 "simultaneous OTP authentications not permitted");
1078 	return SASL_TRYAGAIN;
1079     }
1080 
1081     /* check sequence number */
1082     if (text->seq <= 1) {
1083 	SETERROR(params->utils, "OTP has expired (sequence <= 1)");
1084 	return SASL_EXPIRED;
1085     }
1086 
1087     /* find algorithm */
1088     text->alg = algorithm_options;
1089     while (text->alg->name) {
1090 	if (!strcasecmp(text->alg->name, mda))
1091 	    break;
1092 
1093 	text->alg++;
1094     }
1095 
1096     if (!text->alg->name) {
1097 	params->utils->seterror(params->utils->conn, 0,
1098 				"unknown OTP algorithm '%s'", mda);
1099 	return SASL_FAIL;
1100     }
1101 
1102     /* remake the secret with a timeout */
1103     result = make_secret(params->utils, text->alg->name, text->seq,
1104 			 text->seed, text->otp,
1105 			 text->timestamp + OTP_LOCK_TIMEOUT, &sec);
1106     if (result != SASL_OK) {
1107 	SETERROR(params->utils, "error making OTP secret");
1108 	return result;
1109     }
1110 
1111     /* do the store */
1112     propctx = params->utils->prop_new(0);
1113     if (!propctx)
1114 	result = SASL_FAIL;
1115     if (result == SASL_OK)
1116 	result = params->utils->prop_request(propctx, store_request);
1117     if (result == SASL_OK)
1118 	result = params->utils->prop_set(propctx, "cmusaslsecretOTP",
1119 					 (char *) sec->data, sec->len);
1120     if (result == SASL_OK)
1121 	result = params->utils->auxprop_store(params->utils->conn,
1122 					      propctx, text->authid);
1123     if (propctx)
1124 	params->utils->prop_dispose(&propctx);
1125 
1126     if (sec) params->utils->free(sec);
1127 
1128     if (result != SASL_OK) {
1129 	SETERROR(params->utils, "Error putting OTP secret");
1130 	return result;
1131     }
1132 
1133     text->locked = 1;
1134 
1135     result = _plug_buf_alloc(params->utils, &(text->out_buf),
1136 			     &(text->out_buf_len), OTP_CHALLENGE_MAX+1);
1137     if (result != SASL_OK) return result;
1138 
1139     /* create challenge */
1140     sprintf(text->out_buf, "otp-%s %u %s ext",
1141 	    text->alg->name, text->seq-1, text->seed);
1142 
1143     *serverout = text->out_buf;
1144     *serveroutlen = (unsigned) strlen(text->out_buf);
1145 
1146     text->state = 2;
1147 
1148     return SASL_CONTINUE;
1149 }
1150 
1151 static int
otp_server_mech_step2(server_context_t * text,sasl_server_params_t * params,const char * clientin,unsigned clientinlen,const char ** serverout,unsigned * serveroutlen,sasl_out_params_t * oparams)1152 otp_server_mech_step2(server_context_t *text,
1153 		      sasl_server_params_t *params,
1154 		      const char *clientin,
1155 		      unsigned clientinlen,
1156 		      const char **serverout __attribute__((unused)),
1157 		      unsigned *serveroutlen __attribute__((unused)),
1158 		      sasl_out_params_t *oparams)
1159 {
1160     char response[OTP_RESPONSE_MAX+1];
1161     int result;
1162     sasl_secret_t *sec = NULL;
1163     struct propctx *propctx = NULL;
1164     const char *store_request[] = { "cmusaslsecretOTP",
1165 				     NULL };
1166 
1167     if (clientinlen > OTP_RESPONSE_MAX) {
1168 	SETERROR(params->utils, "OTP response too long");
1169 	return SASL_BADPROT;
1170     }
1171 
1172     /* we can't assume that the response is null-terminated */
1173     strncpy(response, clientin, clientinlen);
1174     response[clientinlen] = '\0';
1175 
1176     /* check timeout */
1177     if (time(0) > text->timestamp + OTP_LOCK_TIMEOUT) {
1178 	SETERROR(params->utils, "OTP: server timed out");
1179 	return SASL_UNAVAIL;
1180     }
1181 
1182     /* verify response */
1183     result = verify_response(text, params->utils, response);
1184     if (result != SASL_OK) return result;
1185 
1186     /* make the new secret */
1187     result = make_secret(params->utils, text->alg->name, text->seq,
1188 			 text->seed, text->otp, 0, &sec);
1189     if (result != SASL_OK) {
1190 	SETERROR(params->utils, "error making OTP secret");
1191     }
1192 
1193     /* do the store */
1194     propctx = params->utils->prop_new(0);
1195     if (!propctx)
1196 	result = SASL_FAIL;
1197     if (result == SASL_OK)
1198 	result = params->utils->prop_request(propctx, store_request);
1199     if (result == SASL_OK)
1200 	result = params->utils->prop_set(propctx, "cmusaslsecretOTP",
1201 					 (char *) sec->data, sec->len);
1202     if (result == SASL_OK)
1203 	result = params->utils->auxprop_store(params->utils->conn,
1204 					      propctx, text->authid);
1205     if (propctx)
1206 	params->utils->prop_dispose(&propctx);
1207 
1208     if (result) {
1209 	SETERROR(params->utils, "Error putting OTP secret");
1210     }
1211 
1212     text->locked = 0;
1213 
1214     if (sec) _plug_free_secret(params->utils, &sec);
1215 
1216     /* set oparams */
1217     oparams->doneflag = 1;
1218     oparams->mech_ssf = 0;
1219     oparams->maxoutbuf = 0;
1220     oparams->encode_context = NULL;
1221     oparams->encode = NULL;
1222     oparams->decode_context = NULL;
1223     oparams->decode = NULL;
1224     oparams->param_version = 0;
1225 
1226     return result;
1227 }
1228 
otp_server_mech_step(void * conn_context,sasl_server_params_t * params,const char * clientin,unsigned clientinlen,const char ** serverout,unsigned * serveroutlen,sasl_out_params_t * oparams)1229 static int otp_server_mech_step(void *conn_context,
1230 				sasl_server_params_t *params,
1231 				const char *clientin,
1232 				unsigned clientinlen,
1233 				const char **serverout,
1234 				unsigned *serveroutlen,
1235 				sasl_out_params_t *oparams)
1236 {
1237     server_context_t *text = (server_context_t *) conn_context;
1238 
1239     *serverout = NULL;
1240     *serveroutlen = 0;
1241 
1242     switch (text->state) {
1243 
1244     case 1:
1245 	return otp_server_mech_step1(text, params, clientin, clientinlen,
1246 				     serverout, serveroutlen, oparams);
1247 
1248     case 2:
1249 	return otp_server_mech_step2(text, params, clientin, clientinlen,
1250 				     serverout, serveroutlen, oparams);
1251 
1252     default:
1253 	params->utils->log(NULL, SASL_LOG_ERR,
1254 			   "Invalid OTP server step %d\n", text->state);
1255 	return SASL_FAIL;
1256     }
1257 
1258     return SASL_FAIL; /* should never get here */
1259 }
1260 
otp_server_mech_dispose(void * conn_context,const sasl_utils_t * utils)1261 static void otp_server_mech_dispose(void *conn_context,
1262 				    const sasl_utils_t *utils)
1263 {
1264     server_context_t *text = (server_context_t *) conn_context;
1265     sasl_secret_t *sec;
1266     struct propctx *propctx = NULL;
1267     const char *store_request[] = { "cmusaslsecretOTP",
1268 				     NULL };
1269     int r;
1270 
1271     if (!text) return;
1272 
1273     /* if we created a challenge, but bailed before the verification of the
1274        response, release the lock on the user key */
1275     if (text->locked && (time(0) < text->timestamp + OTP_LOCK_TIMEOUT)) {
1276 	r = make_secret(utils, text->alg->name, text->seq,
1277 			text->seed, text->otp, 0, &sec);
1278 	if (r != SASL_OK) {
1279 	    SETERROR(utils, "error making OTP secret");
1280 	    if (sec) utils->free(sec);
1281 	    sec = NULL;
1282 	}
1283 
1284 	/* do the store */
1285 	propctx = utils->prop_new(0);
1286 	if (!propctx)
1287 	    r = SASL_FAIL;
1288 	if (!r)
1289 	    r = utils->prop_request(propctx, store_request);
1290 	if (!r)
1291 	    r = utils->prop_set(propctx, "cmusaslsecretOTP",
1292 				(sec ? (char *) sec->data : NULL),
1293 				(sec ? sec->len : 0));
1294 	if (!r)
1295 	    r = utils->auxprop_store(utils->conn, propctx, text->authid);
1296 	if (propctx)
1297 	    utils->prop_dispose(&propctx);
1298 
1299 	if (r) {
1300 	    SETERROR(utils, "Error putting OTP secret");
1301 	}
1302 
1303 	if (sec) _plug_free_secret(utils, &sec);
1304     }
1305 
1306     if (text->authid) _plug_free_string(utils, &(text->authid));
1307     if (text->realm) _plug_free_string(utils, &(text->realm));
1308 
1309     if (text->out_buf) utils->free(text->out_buf);
1310 
1311     utils->free(text);
1312 }
1313 
otp_setpass(void * glob_context,sasl_server_params_t * sparams,const char * userstr,const char * pass,unsigned passlen,const char * oldpass,unsigned oldpasslen,unsigned flags)1314 static int otp_setpass(void *glob_context __attribute__((unused)),
1315 		       sasl_server_params_t *sparams,
1316 		       const char *userstr,
1317 		       const char *pass, unsigned passlen,
1318 		       const char *oldpass __attribute__((unused)),
1319 		       unsigned oldpasslen __attribute__((unused)),
1320 		       unsigned flags)
1321 {
1322     int r;
1323     char *user = NULL;
1324     char *user_only = NULL;
1325     char *realm = NULL;
1326     sasl_secret_t *sec;
1327     struct propctx *propctx = NULL;
1328     const char *store_request[] = { "cmusaslsecretOTP",
1329 				     NULL };
1330 
1331     /* Do we have a backend that can store properties? */
1332     if (!sparams->utils->auxprop_store ||
1333 	sparams->utils->auxprop_store(NULL, NULL, NULL) != SASL_OK) {
1334 	SETERROR(sparams->utils, "OTP: auxprop backend can't store properties");
1335 	return SASL_NOMECH;
1336     }
1337 
1338     r = _plug_parseuser(sparams->utils,
1339 			&user_only,
1340 			&realm,
1341 			sparams->user_realm,
1342 			sparams->serverFQDN,
1343 			userstr);
1344     if (r) {
1345 	SETERROR(sparams->utils, "OTP: Error parsing user");
1346 	return r;
1347     }
1348 
1349     r = _plug_make_fulluser(sparams->utils, &user, user_only, realm);
1350     if (r) {
1351        goto cleanup;
1352     }
1353 
1354     if ((flags & SASL_SET_DISABLE) || pass == NULL) {
1355 	sec = NULL;
1356     } else {
1357 	algorithm_option_t *algs;
1358 	const char *mda;
1359 	unsigned int len;
1360 	unsigned short randnum;
1361 	char seed[OTP_SEED_MAX+1];
1362 	unsigned char otp[OTP_HASH_SIZE];
1363 
1364 	sparams->utils->getopt(sparams->utils->getopt_context,
1365 			       "OTP", "otp_mda", &mda, &len);
1366 	if (!mda) mda = OTP_MDA_DEFAULT;
1367 
1368 	algs = algorithm_options;
1369 	while (algs->name) {
1370 	    if (!strcasecmp(algs->name, mda) ||
1371 		!strcasecmp(algs->evp_name, mda))
1372 		break;
1373 
1374 	    algs++;
1375 	}
1376 
1377 	if (!algs->name) {
1378 	    sparams->utils->seterror(sparams->utils->conn, 0,
1379 				     "unknown OTP algorithm '%s'", mda);
1380 	    r = SASL_FAIL;
1381 	    goto cleanup;
1382 	}
1383 
1384 	sparams->utils->rand(sparams->utils->rpool,
1385 			     (char*) &randnum, sizeof(randnum));
1386 	sprintf(seed, "%.2s%04u", sparams->serverFQDN, (randnum % 9999) + 1);
1387 
1388 	r = generate_otp(sparams->utils, algs, OTP_SEQUENCE_DEFAULT,
1389 			 seed, (unsigned char *) pass, passlen, otp);
1390 	if (r != SASL_OK) {
1391 	    /* generate_otp() takes care of error message */
1392 	    goto cleanup;
1393 	}
1394 
1395 	r = make_secret(sparams->utils, algs->name, OTP_SEQUENCE_DEFAULT,
1396 			seed, otp, 0, &sec);
1397 	if (r != SASL_OK) {
1398 	    SETERROR(sparams->utils, "error making OTP secret");
1399 	    goto cleanup;
1400 	}
1401     }
1402 
1403     /* do the store */
1404     propctx = sparams->utils->prop_new(0);
1405     if (!propctx)
1406 	r = SASL_FAIL;
1407     if (!r)
1408 	r = sparams->utils->prop_request(propctx, store_request);
1409     if (!r)
1410 	r = sparams->utils->prop_set(propctx, "cmusaslsecretOTP",
1411 				     (sec ? (char *) sec->data : NULL),
1412 				     (sec ? sec->len : 0));
1413     if (!r)
1414 	r = sparams->utils->auxprop_store(sparams->utils->conn, propctx, user);
1415     if (propctx)
1416 	sparams->utils->prop_dispose(&propctx);
1417 
1418     if (r) {
1419 	SETERROR(sparams->utils, "Error putting OTP secret");
1420 	goto cleanup;
1421     }
1422 
1423     sparams->utils->log(NULL, SASL_LOG_DEBUG, "Setpass for OTP successful\n");
1424 
1425   cleanup:
1426 
1427     if (user) 	_plug_free_string(sparams->utils, &user);
1428     if (user_only)     _plug_free_string(sparams->utils, &user_only);
1429     if (realm) 	_plug_free_string(sparams->utils, &realm);
1430     if (sec)    _plug_free_secret(sparams->utils, &sec);
1431 
1432     return r;
1433 }
1434 
otp_mech_avail(void * glob_context,sasl_server_params_t * sparams,void ** conn_context)1435 static int otp_mech_avail(void *glob_context __attribute__((unused)),
1436 	  	          sasl_server_params_t *sparams,
1437 		          void **conn_context __attribute__((unused)))
1438 {
1439     /* Do we have a backend that can store properties? */
1440     if (!sparams->utils->auxprop_store ||
1441 	sparams->utils->auxprop_store(NULL, NULL, NULL) != SASL_OK) {
1442 	sparams->utils->log(NULL,
1443 			    SASL_LOG_DEBUG,
1444 			    "OTP: auxprop backend can't store properties");
1445 	return SASL_NOMECH;
1446     }
1447 
1448     return SASL_OK;
1449 }
1450 
1451 static sasl_server_plug_t otp_server_plugins[] =
1452 {
1453     {
1454 	"OTP",				/* mech_name */
1455 	0,				/* max_ssf */
1456 	SASL_SEC_NOPLAINTEXT
1457 	| SASL_SEC_NOANONYMOUS
1458 	| SASL_SEC_FORWARD_SECRECY,	/* security_flags */
1459 	SASL_FEAT_WANT_CLIENT_FIRST
1460 	| SASL_FEAT_ALLOWS_PROXY,	/* features */
1461 	NULL,				/* glob_context */
1462 	&otp_server_mech_new,		/* mech_new */
1463 	&otp_server_mech_step,		/* mech_step */
1464 	&otp_server_mech_dispose,	/* mech_dispose */
1465 	&otp_common_mech_free,		/* mech_free */
1466 	&otp_setpass,			/* setpass */
1467 	NULL,				/* user_query */
1468 	NULL,				/* idle */
1469 	&otp_mech_avail,		/* mech avail */
1470 	NULL				/* spare */
1471     }
1472 };
1473 #endif /* HAVE_OPIE */
1474 
otp_server_plug_init(const sasl_utils_t * utils,int maxversion,int * out_version,sasl_server_plug_t ** pluglist,int * plugcount)1475 int otp_server_plug_init(const sasl_utils_t *utils,
1476 			 int maxversion,
1477 			 int *out_version,
1478 			 sasl_server_plug_t **pluglist,
1479 			 int *plugcount)
1480 {
1481     if (maxversion < SASL_SERVER_PLUG_VERSION) {
1482 	SETERROR(utils, "OTP version mismatch");
1483 	return SASL_BADVERS;
1484     }
1485 
1486     *out_version = SASL_SERVER_PLUG_VERSION;
1487     *pluglist = otp_server_plugins;
1488     *plugcount = 1;
1489 
1490     /* Add all digests */
1491     OpenSSL_add_all_digests();
1492 
1493     return SASL_OK;
1494 }
1495 
1496 /*****************************  Client Section  *****************************/
1497 
1498 typedef struct client_context {
1499     int state;
1500 
1501     sasl_secret_t *password;
1502     unsigned int free_password; /* set if we need to free password */
1503 
1504     const char *otpassword;
1505 
1506     char *out_buf;
1507     unsigned out_buf_len;
1508 
1509     char challenge[OTP_CHALLENGE_MAX+1];
1510 } client_context_t;
1511 
otp_client_mech_new(void * glob_context,sasl_client_params_t * params,void ** conn_context)1512 static int otp_client_mech_new(void *glob_context __attribute__((unused)),
1513 			       sasl_client_params_t *params,
1514 			       void **conn_context)
1515 {
1516     client_context_t *text;
1517 
1518     /* holds state are in */
1519     text = params->utils->malloc(sizeof(client_context_t));
1520     if (text == NULL) {
1521 	MEMERROR( params->utils );
1522 	return SASL_NOMEM;
1523     }
1524 
1525     memset(text, 0, sizeof(client_context_t));
1526 
1527     text->state = 1;
1528 
1529     *conn_context = text;
1530 
1531     return SASL_OK;
1532 }
1533 
otp_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)1534 static int otp_client_mech_step1(client_context_t *text,
1535 				 sasl_client_params_t *params,
1536 				 const char *serverin __attribute__((unused)),
1537 				 unsigned serverinlen __attribute__((unused)),
1538 				 sasl_interact_t **prompt_need,
1539 				 const char **clientout,
1540 				 unsigned *clientoutlen,
1541 				 sasl_out_params_t *oparams)
1542 {
1543     const char *user = NULL, *authid = NULL;
1544     int user_result = SASL_OK;
1545     int auth_result = SASL_OK;
1546     int pass_result = SASL_OK;
1547     sasl_chalprompt_t *echo_cb;
1548     void *echo_context;
1549     int result;
1550 
1551     /* check if sec layer strong enough */
1552     if (params->props.min_ssf > params->external_ssf) {
1553 	SETERROR( params->utils, "SSF requested of OTP plugin");
1554 	return SASL_TOOWEAK;
1555     }
1556 
1557     /* try to get the authid */
1558     if (oparams->authid == NULL) {
1559 	auth_result = _plug_get_authid(params->utils, &authid, prompt_need);
1560 
1561 	if ((auth_result != SASL_OK) && (auth_result != SASL_INTERACT))
1562 	    return auth_result;
1563     }
1564 
1565     /* try to get the userid */
1566     if (oparams->user == NULL) {
1567 	user_result = _plug_get_userid(params->utils, &user, prompt_need);
1568 
1569 	if ((user_result != SASL_OK) && (user_result != SASL_INTERACT))
1570 	    return user_result;
1571     }
1572 
1573     /* try to get the secret pass-phrase if we don't have a chalprompt */
1574     if ((params->utils->getcallback(params->utils->conn, SASL_CB_ECHOPROMPT,
1575 				    (sasl_callback_ft *)&echo_cb, &echo_context) == SASL_FAIL) &&
1576 	(text->password == NULL)) {
1577 	pass_result = _plug_get_password(params->utils, &text->password,
1578 					 &text->free_password, prompt_need);
1579 
1580 	if ((pass_result != SASL_OK) && (pass_result != SASL_INTERACT))
1581 	    return pass_result;
1582     }
1583 
1584     /* free prompts we got */
1585     if (prompt_need && *prompt_need) {
1586 	params->utils->free(*prompt_need);
1587 	*prompt_need = NULL;
1588     }
1589 
1590     /* if there are prompts not filled in */
1591     if ((user_result == SASL_INTERACT) || (auth_result == SASL_INTERACT) ||
1592 	(pass_result == SASL_INTERACT)) {
1593 	/* make the prompt list */
1594 	result =
1595 	    _plug_make_prompts(params->utils, prompt_need,
1596 			       user_result == SASL_INTERACT ?
1597 			       "Please enter your authorization name" : NULL,
1598 			       NULL,
1599 			       auth_result == SASL_INTERACT ?
1600 			       "Please enter your authentication name" : NULL,
1601 			       NULL,
1602 			       pass_result == SASL_INTERACT ?
1603 			       "Please enter your secret pass-phrase" : NULL,
1604 			       NULL,
1605 			       NULL, NULL, NULL,
1606 			       NULL, NULL, NULL);
1607 	if (result != SASL_OK) return result;
1608 
1609 	return SASL_INTERACT;
1610     }
1611 
1612     if (!user || !*user) {
1613 	result = params->canon_user(params->utils->conn, authid, 0,
1614 				    SASL_CU_AUTHID | SASL_CU_AUTHZID, oparams);
1615     }
1616     else {
1617 	result = params->canon_user(params->utils->conn, user, 0,
1618 				    SASL_CU_AUTHZID, oparams);
1619 	if (result != SASL_OK) return result;
1620 
1621 	result = params->canon_user(params->utils->conn, authid, 0,
1622 				    SASL_CU_AUTHID, oparams);
1623     }
1624     if (result != SASL_OK) return result;
1625 
1626     /* send authorized id NUL authentication id */
1627     *clientoutlen = oparams->ulen + 1 + oparams->alen;
1628 
1629     /* remember the extra NUL on the end for stupid clients */
1630     result = _plug_buf_alloc(params->utils, &(text->out_buf),
1631 			     &(text->out_buf_len), *clientoutlen + 1);
1632     if (result != SASL_OK) return result;
1633 
1634     memset(text->out_buf, 0, *clientoutlen + 1);
1635     memcpy(text->out_buf, oparams->user, oparams->ulen);
1636     memcpy(text->out_buf+oparams->ulen+1, oparams->authid, oparams->alen);
1637     *clientout = text->out_buf;
1638 
1639     text->state = 2;
1640 
1641     return SASL_CONTINUE;
1642 }
1643 
otp_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)1644 static int otp_client_mech_step2(client_context_t *text,
1645 				 sasl_client_params_t *params,
1646 				 const char *serverin,
1647 				 unsigned serverinlen,
1648 				 sasl_interact_t **prompt_need,
1649 				 const char **clientout,
1650 				 unsigned *clientoutlen,
1651 				 sasl_out_params_t *oparams)
1652 {
1653     int echo_result = SASL_OK;
1654     int result;
1655 
1656     if (serverinlen > OTP_CHALLENGE_MAX) {
1657 	SETERROR(params->utils, "OTP challenge too long");
1658 	return SASL_BADPROT;
1659     }
1660 
1661     /* we can't assume that challenge is null-terminated */
1662     strncpy(text->challenge, serverin, serverinlen);
1663     text->challenge[serverinlen] = '\0';
1664 
1665     /* try to get the one-time password if we don't have the secret */
1666     if ((text->password == NULL) && (text->otpassword == NULL)) {
1667 	echo_result = _plug_challenge_prompt(params->utils,
1668 					     SASL_CB_ECHOPROMPT,
1669 					     text->challenge,
1670 					     "Please enter your one-time password",
1671 					     &text->otpassword,
1672 					     prompt_need);
1673 
1674 	if ((echo_result != SASL_OK) && (echo_result != SASL_INTERACT))
1675 	    return echo_result;
1676     }
1677 
1678     /* free prompts we got */
1679     if (prompt_need && *prompt_need) {
1680 	params->utils->free(*prompt_need);
1681 	*prompt_need = NULL;
1682     }
1683 
1684     /* if there are prompts not filled in */
1685     if (echo_result == SASL_INTERACT) {
1686 	/* make the prompt list */
1687 	result =
1688 	    _plug_make_prompts(params->utils,
1689 			       prompt_need,
1690 			       NULL,
1691 			       NULL,
1692 			       NULL,
1693 			       NULL,
1694 			       NULL,
1695 			       NULL,
1696 			       text->challenge,
1697 			       "Please enter your one-time password",
1698 			       NULL,
1699 			       NULL,
1700 			       NULL,
1701 			       NULL);
1702 	if (result != SASL_OK) return result;
1703 
1704 	return SASL_INTERACT;
1705     }
1706 
1707     /* the application provided us with a one-time password so use it */
1708     if (text->otpassword) {
1709 	*clientout = text->otpassword;
1710 	*clientoutlen = (unsigned) strlen(text->otpassword);
1711     }
1712     /* generate our own response using the user's secret pass-phrase */
1713     else {
1714 	algorithm_option_t *alg;
1715 	unsigned seq;
1716 	char seed[OTP_SEED_MAX+1];
1717 	unsigned char otp[OTP_HASH_SIZE];
1718 	int init_done = 0;
1719 
1720 	/* parse challenge */
1721 	result = parse_challenge(params->utils,
1722 				 text->challenge,
1723 				 &alg,
1724 				 &seq,
1725 				 seed,
1726 				 0);
1727 	if (result != SASL_OK) return result;
1728 
1729 	if (!text->password) {
1730 	    PARAMERROR(params->utils);
1731 	    return SASL_BADPARAM;
1732 	}
1733 
1734 	if (seq < 1) {
1735 	    SETERROR(params->utils, "OTP has expired (sequence < 1)");
1736 	    return SASL_EXPIRED;
1737 	}
1738 
1739 	/* generate otp */
1740 	result = generate_otp(params->utils, alg, seq, seed,
1741 			      text->password->data, text->password->len, otp);
1742 	if (result != SASL_OK) return result;
1743 
1744 	result = _plug_buf_alloc(params->utils, &(text->out_buf),
1745 				 &(text->out_buf_len), OTP_RESPONSE_MAX+1);
1746 	if (result != SASL_OK) return result;
1747 
1748 	if (seq < OTP_SEQUENCE_REINIT) {
1749 	    unsigned short randnum;
1750 	    char new_seed[OTP_SEED_MAX+1];
1751 	    unsigned char new_otp[OTP_HASH_SIZE];
1752 
1753 	    /* try to reinitialize */
1754 
1755 	    /* make sure we have a different seed */
1756 	    do {
1757 		params->utils->rand(params->utils->rpool,
1758 				    (char*) &randnum, sizeof(randnum));
1759 		sprintf(new_seed, "%.2s%04u", params->serverFQDN,
1760 			(randnum % 9999) + 1);
1761 	    } while (!strcasecmp(seed, new_seed));
1762 
1763 	    result = generate_otp(params->utils, alg, OTP_SEQUENCE_DEFAULT,
1764 				  new_seed, text->password->data, text->password->len, new_otp);
1765 
1766 	    if (result == SASL_OK) {
1767 		/* create an init-hex response */
1768 		strcpy(text->out_buf, OTP_INIT_HEX_TYPE);
1769 		bin2hex(otp, OTP_HASH_SIZE,
1770 			text->out_buf+strlen(text->out_buf));
1771 		sprintf(text->out_buf+strlen(text->out_buf), ":%s %u %s:",
1772 			alg->name, OTP_SEQUENCE_DEFAULT, new_seed);
1773 		bin2hex(new_otp, OTP_HASH_SIZE,
1774 			text->out_buf+strlen(text->out_buf));
1775 		init_done = 1;
1776 	    }
1777 	    else {
1778 		/* just do a regular response */
1779 	    }
1780 	}
1781 
1782 	if (!init_done) {
1783 	    /* created hex response */
1784 	    strcpy(text->out_buf, OTP_HEX_TYPE);
1785 	    bin2hex(otp, OTP_HASH_SIZE, text->out_buf+strlen(text->out_buf));
1786 	}
1787 
1788 	*clientout = text->out_buf;
1789 	*clientoutlen = (unsigned) strlen(text->out_buf);
1790     }
1791 
1792     /* set oparams */
1793     oparams->doneflag = 1;
1794     oparams->mech_ssf = 0;
1795     oparams->maxoutbuf = 0;
1796     oparams->encode_context = NULL;
1797     oparams->encode = NULL;
1798     oparams->decode_context = NULL;
1799     oparams->decode = NULL;
1800     oparams->param_version = 0;
1801 
1802     return SASL_OK;
1803 }
1804 
otp_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)1805 static int otp_client_mech_step(void *conn_context,
1806 				sasl_client_params_t *params,
1807 				const char *serverin,
1808 				unsigned serverinlen,
1809 				sasl_interact_t **prompt_need,
1810 				const char **clientout,
1811 				unsigned *clientoutlen,
1812 				sasl_out_params_t *oparams)
1813 {
1814     client_context_t *text = (client_context_t *) conn_context;
1815 
1816     *clientout = NULL;
1817     *clientoutlen = 0;
1818 
1819     switch (text->state) {
1820 
1821     case 1:
1822 	return otp_client_mech_step1(text, params, serverin, serverinlen,
1823 				     prompt_need, clientout, clientoutlen,
1824 				     oparams);
1825 
1826     case 2:
1827 	return otp_client_mech_step2(text, params, serverin, serverinlen,
1828 				     prompt_need, clientout, clientoutlen,
1829 				     oparams);
1830 
1831     default:
1832 	params->utils->log(NULL, SASL_LOG_ERR,
1833 			   "Invalid OTP client step %d\n", text->state);
1834 	return SASL_FAIL;
1835     }
1836 
1837     return SASL_FAIL; /* should never get here */
1838 }
1839 
otp_client_mech_dispose(void * conn_context,const sasl_utils_t * utils)1840 static void otp_client_mech_dispose(void *conn_context,
1841 				    const sasl_utils_t *utils)
1842 {
1843     client_context_t *text = (client_context_t *) conn_context;
1844 
1845     if (!text) return;
1846 
1847     if (text->free_password) _plug_free_secret(utils, &(text->password));
1848 
1849     if (text->out_buf) utils->free(text->out_buf);
1850 
1851     utils->free(text);
1852 }
1853 
1854 static sasl_client_plug_t otp_client_plugins[] =
1855 {
1856     {
1857 	"OTP",				/* mech_name */
1858 	0,				/* max_ssf */
1859 	SASL_SEC_NOPLAINTEXT
1860 	| SASL_SEC_NOANONYMOUS
1861 	| SASL_SEC_FORWARD_SECRECY,	/* security_flags */
1862 	SASL_FEAT_WANT_CLIENT_FIRST
1863 	| SASL_FEAT_ALLOWS_PROXY,	/* features */
1864 	NULL,				/* required_prompts */
1865 	NULL,				/* glob_context */
1866 	&otp_client_mech_new,		/* mech_new */
1867 	&otp_client_mech_step,		/* mech_step */
1868 	&otp_client_mech_dispose,	/* mech_dispose */
1869 	&otp_common_mech_free,		/* mech_free */
1870 	NULL,				/* idle */
1871 	NULL,				/* spare */
1872 	NULL				/* spare */
1873     }
1874 };
1875 
otp_client_plug_init(sasl_utils_t * utils,int maxversion,int * out_version,sasl_client_plug_t ** pluglist,int * plugcount)1876 int otp_client_plug_init(sasl_utils_t *utils,
1877 			 int maxversion,
1878 			 int *out_version,
1879 			 sasl_client_plug_t **pluglist,
1880 			 int *plugcount)
1881 {
1882     if (maxversion < SASL_CLIENT_PLUG_VERSION) {
1883 	SETERROR(utils, "OTP version mismatch");
1884 	return SASL_BADVERS;
1885     }
1886 
1887     *out_version = SASL_CLIENT_PLUG_VERSION;
1888     *pluglist = otp_client_plugins;
1889     *plugcount = 1;
1890 
1891     /* Add all digests */
1892     OpenSSL_add_all_digests();
1893 
1894     return SASL_OK;
1895 }
1896