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