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