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