GSS_S_PROMPTING_NEEDED is a bit
[cyrus-sasl.git] / plugins / otp.c
1 /* OTP SASL plugin
2  * Ken Murchison
3  * $Id: otp.c,v 1.36 2004/06/23 18:43:37 rjs3 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.36 2004/06/23 18:43:37 rjs3 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
86 typedef 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
92 static 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 */
100 void 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  */
118 static 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
145 static 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
177 static 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
275 static void
276 otp_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
288 typedef 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
308 static 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
338 static 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
485 static 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
503 static 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
526 static 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 */
556 int 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
582 static 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(data, "%s\t%04d\t%s\t%s\t%020ld",
611             alg, seq, seed, buf, timeout);
612     
613     return SASL_OK;
614 }
615
616 static 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(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 */
681 static 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 */
687 static 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
793 static 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
913 static 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
1100 static int
1101 otp_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
1179 static 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
1211 static 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
1264 static 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
1384 static 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
1398 static 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
1422 int 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
1445 typedef 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
1457 static 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
1479 static 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
1589 static 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
1740 static 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
1775 static 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
1789 static 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
1811 int 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 }