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