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