1/* PASSDSS-3DES-1 SASL plugin 2 * Ken Murchison 3 * $Id: passdss.c,v 1.5 2008/10/29 17:59:41 murch Exp $ 4 */ 5/* 6 * Copyright (c) 1998-2004 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/* 46 * Notes: 47 * 48 */ 49 50#include <config.h> 51#include <stdio.h> 52#include <ctype.h> 53#include <string.h> 54 55/* check OpenSSL version */ 56#include <openssl/opensslv.h> 57#if (OPENSSL_VERSION_NUMBER < 0x0090700f) 58#error OpenSSL 0.9.7 or later is required 59#endif 60 61/* for big number support */ 62#include <openssl/bn.h> 63 64/* for Diffie-Hellman support */ 65#include <openssl/dh.h> 66 67/* for digest and cipher support */ 68#include <openssl/evp.h> 69#include <openssl/hmac.h> 70#include <openssl/md5.h> 71#include <openssl/sha.h> 72#include <openssl/dsa.h> 73 74#include <sasl.h> 75#define MD5_H /* suppress internal MD5 */ 76#include <saslplug.h> 77 78#include "plugin_common.h" 79 80#ifdef macintosh 81#include <sasl_passdss_plugin_decl.h> 82#endif 83 84/***************************** Common Section *****************************/ 85 86static const char plugin_id[] = "$Id: passdss.c,v 1.5 2008/10/29 17:59:41 murch Exp $"; 87 88const char g[] = "2"; 89const char N[] = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF"; 90 91#define NO_LAYER_FLAG (1<<0) 92#define INTEGRITY_LAYER_FLAG (1<<1) 93#define PRIVACY_LAYER_FLAG (1<<2) 94 95#define NO_LAYER_SSF 0 96#define INTEGRITY_LAYER_SSF 1 97#define PRIVACY_LAYER_SSF 112 98 99typedef struct context { 100 int state; 101 102 char *authid; /* authentication id (server) */ 103 char *userid; /* authorization id (server) */ 104 sasl_secret_t *password; /* user secret (client) */ 105 unsigned int free_password; /* set if we need to free password */ 106 107 DH *dh; /* Diffie-Hellman parameters */ 108 109 /* copy of utils from the params structures */ 110 const sasl_utils_t *utils; 111 112 /* per-step mem management */ 113 char *out_buf; 114 unsigned out_buf_len; 115 116 /* security layer foo */ 117 unsigned char secmask; /* bitmask of enabled security layers */ 118 unsigned char padding[EVP_MAX_BLOCK_LENGTH]; /* block of NULs */ 119 120 HMAC_CTX hmac_send_ctx; 121 HMAC_CTX hmac_recv_ctx; 122 123 unsigned char send_integrity_key[4 + EVP_MAX_MD_SIZE]; /* +4 for pktnum */ 124 unsigned char recv_integrity_key[4 + EVP_MAX_MD_SIZE]; /* +4 for pktnum */ 125 unsigned char *cs_integrity_key; /* ptr to bare key in send/recv key */ 126 unsigned char *sc_integrity_key; /* ptr to bare key in send/recv key */ 127 128 EVP_CIPHER_CTX cipher_enc_ctx; 129 EVP_CIPHER_CTX cipher_dec_ctx; 130 unsigned blk_siz; 131 132 unsigned char cs_encryption_iv[EVP_MAX_MD_SIZE]; 133 unsigned char sc_encryption_iv[EVP_MAX_MD_SIZE]; 134 unsigned char cs_encryption_key[2 * EVP_MAX_MD_SIZE]; 135 unsigned char sc_encryption_key[2 * EVP_MAX_MD_SIZE]; 136 137 /* replay detection sequence numbers */ 138 uint32_t pktnum_out; 139 uint32_t pktnum_in; 140 141 /* for encoding/decoding mem management */ 142 char *encode_buf, *decode_buf, *decode_pkt_buf; 143 unsigned encode_buf_len, decode_buf_len, decode_pkt_buf_len; 144 145 /* layers buffering */ 146 decode_context_t decode_context; 147 148} context_t; 149 150 151static int passdss_encode(void *context, 152 const struct iovec *invec, 153 unsigned numiov, 154 const char **output, 155 unsigned *outputlen) 156{ 157 context_t *text = (context_t *) context; 158 unsigned long inputlen; 159 unsigned char hmac[EVP_MAX_MD_SIZE]; 160 unsigned i, hmaclen; 161 uint32_t tmpnum; 162 int ret; 163 164 if (!context || !invec || !numiov || !output || !outputlen) { 165 PARAMERROR( text->utils ); 166 return SASL_BADPARAM; 167 } 168 169 /* calculate total size of input */ 170 for (i = 0, inputlen = 0; i < numiov; i++) 171 inputlen += invec[i].iov_len; 172 173 /* allocate a buffer for the output */ 174 ret = _plug_buf_alloc(text->utils, &text->encode_buf, 175 &text->encode_buf_len, 176 4 + /* length */ 177 inputlen + /* content */ 178 EVP_MAX_MD_SIZE + /* HMAC */ 179 EVP_MAX_BLOCK_LENGTH - 1); /* padding */ 180 if (ret != SASL_OK) return ret; 181 182 *outputlen = 4; /* skip length */ 183 184 /* prepend packet number to integrity key */ 185 tmpnum = htonl(text->pktnum_out++); 186 memcpy(text->send_integrity_key, &tmpnum, 4); 187 188 /* key the HMAC */ 189 HMAC_Init_ex(&text->hmac_send_ctx, text->send_integrity_key, 190 4+SHA_DIGEST_LENGTH, EVP_sha1(), NULL); 191 192 /* operate on each iovec */ 193 for (i = 0; i < numiov; i++) { 194 /* hash the content */ 195 HMAC_Update(&text->hmac_send_ctx, invec[i].iov_base, invec[i].iov_len); 196 197 if (text->secmask & PRIVACY_LAYER_FLAG) { 198 unsigned enclen; 199 200 /* encrypt the data into the output buffer */ 201 EVP_EncryptUpdate(&text->cipher_enc_ctx, 202 text->encode_buf + *outputlen, &enclen, 203 invec[i].iov_base, invec[i].iov_len); 204 *outputlen += enclen; 205 } 206 else { 207 /* copy the raw input to the output */ 208 memcpy(text->encode_buf + *outputlen, invec[i].iov_base, 209 invec[i].iov_len); 210 *outputlen += invec[i].iov_len; 211 } 212 } 213 214 /* calculate the HMAC */ 215 HMAC_Final(&text->hmac_send_ctx, hmac, &hmaclen); 216 217 if (text->secmask & PRIVACY_LAYER_FLAG) { 218 unsigned enclen; 219 unsigned char padlen; 220 221 /* encrypt the HMAC into the output buffer */ 222 EVP_EncryptUpdate(&text->cipher_enc_ctx, 223 text->encode_buf + *outputlen, &enclen, 224 hmac, hmaclen); 225 *outputlen += enclen; 226 227 /* pad output buffer to multiple of blk_siz 228 with padlen-1 as last octet */ 229 padlen = text->blk_siz - ((inputlen + hmaclen) % text->blk_siz) - 1; 230 EVP_EncryptUpdate(&text->cipher_enc_ctx, 231 text->encode_buf + *outputlen, &enclen, 232 text->padding, padlen); 233 *outputlen += enclen; 234 EVP_EncryptUpdate(&text->cipher_enc_ctx, 235 text->encode_buf + *outputlen, &enclen, 236 &padlen, 1); 237 *outputlen += enclen; 238 239 /* encrypt the last block of data into the output buffer */ 240 EVP_EncryptFinal_ex(&text->cipher_enc_ctx, 241 text->encode_buf + *outputlen, &enclen); 242 *outputlen += enclen; 243 } 244 else { 245 /* copy the HMAC to the output */ 246 memcpy(text->encode_buf + *outputlen, hmac, hmaclen); 247 *outputlen += hmaclen; 248 } 249 250 /* prepend the length of the output */ 251 tmpnum = *outputlen - 4; 252 tmpnum = htonl(tmpnum); 253 memcpy(text->encode_buf, &tmpnum, 4); 254 255 *output = text->encode_buf; 256 257 return SASL_OK; 258} 259 260/* Decode a single PASSDSS packet */ 261static int passdss_decode_packet(void *context, 262 const char *input, 263 unsigned inputlen, 264 char **output, 265 unsigned *outputlen) 266{ 267 context_t *text = (context_t *) context; 268 uint32_t tmpnum; 269 unsigned char hmac[EVP_MAX_MD_SIZE]; 270 unsigned hmaclen; 271 int ret; 272 273 if (text->secmask & PRIVACY_LAYER_FLAG) { 274 unsigned declen, padlen; 275 276 /* allocate a buffer for the output */ 277 ret = _plug_buf_alloc(text->utils, &(text->decode_pkt_buf), 278 &(text->decode_pkt_buf_len), inputlen); 279 if (ret != SASL_OK) return ret; 280 281 /* decrypt the data into the output buffer */ 282 ret = EVP_DecryptUpdate(&text->cipher_dec_ctx, 283 text->decode_pkt_buf, &declen, 284 (char *) input, inputlen); 285 if (ret) 286 EVP_DecryptFinal_ex(&text->cipher_dec_ctx, /* should be no output */ 287 text->decode_pkt_buf + declen, &declen); 288 if (!ret) { 289 SETERROR(text->utils, "Error decrypting input"); 290 return SASL_BADPROT; 291 } 292 input = text->decode_pkt_buf; 293 294 /* trim padding */ 295 padlen = text->decode_pkt_buf[inputlen - 1] + 1; 296 inputlen -= padlen; 297 } 298 299 /* trim HMAC */ 300 inputlen -= SHA_DIGEST_LENGTH; 301 302 /* prepend packet number to integrity key */ 303 tmpnum = htonl(text->pktnum_in++); 304 memcpy(text->recv_integrity_key, &tmpnum, 4); 305 306 /* calculate the HMAC */ 307 HMAC(EVP_sha1(), text->recv_integrity_key, 4+SHA_DIGEST_LENGTH, 308 input, inputlen, hmac, &hmaclen); 309 310 /* verify HMAC */ 311 if (memcmp(hmac, input+inputlen, hmaclen)) { 312 SETERROR(text->utils, "HMAC is incorrect\n"); 313 return SASL_BADMAC; 314 } 315 316 *output = (char *) input; 317 *outputlen = inputlen; 318 319 return SASL_OK; 320} 321 322/* Decode and concatenate multiple PASSDSS packets */ 323static int passdss_decode(void *context, 324 const char *input, unsigned inputlen, 325 const char **output, unsigned *outputlen) 326{ 327 context_t *text = (context_t *) context; 328 int ret; 329 330 ret = _plug_decode(&text->decode_context, input, inputlen, 331 &text->decode_buf, &text->decode_buf_len, outputlen, 332 passdss_decode_packet, text); 333 334 *output = text->decode_buf; 335 336 return ret; 337} 338 339#define MAX_MPI_LEN 2147483643 340#define MAX_UTF8_LEN 2147483643 341 342/* 343 * Create/append to a PASSDSS buffer from the data specified by the fmt string. 344 */ 345static int MakeBuffer(const sasl_utils_t *utils, char **buf, unsigned offset, 346 unsigned *buflen, unsigned *outlen, const char *fmt, ...) 347{ 348 va_list ap; 349 char *p, *out = NULL, *lptr = NULL; 350 int r, alloclen, len = -1, argc = 0; 351 BIGNUM *mpi; 352 char *os, *str; 353 uint32_t u, nl; 354 355 /* first pass to calculate size of buffer */ 356 va_start(ap, fmt); 357 for (p = (char *) fmt, alloclen = offset; *p; p++) { 358 if (*p != '%') { 359 alloclen++; 360 continue; 361 } 362 363 /* check for length prefix ('a', 'o', 'u', and 's' only) */ 364 if (*++p == '*') { 365 /* arg is length of next arg */ 366 len = va_arg(ap, int); 367 p++; 368 } 369 else if (isdigit((int) *p)) { 370 len = 0; 371 while (isdigit((int) *p)) len = 10 * len + *p++ - '0'; 372 } 373 374 switch (*p) { 375 case 'a': 376 /* insert total length of next N args */ 377 alloclen += 4; 378 break; 379 380 case 'm': 381 /* MPI */ 382 mpi = va_arg(ap, BIGNUM *); 383 len = BN_num_bytes(mpi); 384 if (len > MAX_MPI_LEN) { 385 utils->log(NULL, SASL_LOG_ERR, 386 "String too long to create mpi string\n"); 387 r = SASL_FAIL; 388 goto done; 389 } 390 alloclen += len + 4; 391 break; 392 393 case 'o': 394 /* octet sequence (len given by prefix) */ 395 alloclen += len; 396 os = va_arg(ap, char *); 397 break; 398 399 case 's': 400 /* string */ 401 str = va_arg(ap, char *); 402 if (len == -1) len = strlen(str); 403 if (len > MAX_UTF8_LEN) { 404 utils->log(NULL, SASL_LOG_ERR, 405 "String too long to create utf8 string\n"); 406 r = SASL_FAIL; 407 goto done; 408 } 409 alloclen += len + 4; 410 break; 411 412 case 'u': 413 /* unsigned int */ 414 u = va_arg(ap, uint32_t); 415 if (len == -1) len = 4; 416 alloclen += len; 417 break; 418 419 default: 420 alloclen++; 421 break; 422 } 423 424 len = -1; 425 } 426 va_end(ap); 427 428 r = _plug_buf_alloc(utils, buf, buflen, alloclen); 429 if (r != SASL_OK) return r; 430 431 out = *buf + offset; 432 433 /* second pass to fill buffer */ 434 va_start(ap, fmt); 435 for (p = (char *) fmt; *p; p++) { 436 if (*p != '%') { 437 *out = *p; 438 out++; 439 continue; 440 } 441 442 /* check for length prefix ('a', 'o', 'u', and 's' only) */ 443 if (*++p == '*') { 444 /* arg is length of next arg */ 445 len = va_arg(ap, int); 446 p++; 447 } 448 else if (isdigit((int) *p)) { 449 len = 0; 450 while (isdigit((int) *p)) len = 10 * len + *p++ - '0'; 451 } 452 453 switch (*p) { 454 case 'a': 455 /* total length of next N args */ 456 argc = len; 457 len = -1; 458 lptr = out; 459 out += 4; 460 continue; 461 break; 462 463 case 'm': 464 /* MPI */ 465 mpi = va_arg(ap, BIGNUM *); 466 len = BN_bn2bin(mpi, out+4); 467 nl = htonl(len); 468 memcpy(out, &nl, 4); /* add 4 byte len (network order) */ 469 out += len + 4; 470 break; 471 472 case 'o': 473 /* octet sequence (len given by prefix) */ 474 os = va_arg(ap, char *); 475 memcpy(out, os, len); /* add data */ 476 out += len; 477 break; 478 479 case 's': 480 /* string (len possibly given by prefix) */ 481 str = va_arg(ap, char *); 482 /* xxx do actual utf8 conversion */ 483 if (len == -1) len = strlen(str); 484 nl = htonl(len); 485 memcpy(out, &nl, 4); /* add 4 byte len (network order) */ 486 memcpy(out+4, str, len); /* add string */ 487 out += len + 4; 488 break; 489 490 case 'u': 491 /* unsigned int */ 492 u = va_arg(ap, uint32_t); 493 nl = htonl(u); 494 if (len == -1) len = 4; 495 memcpy(out, &nl + 4 - len, len); 496 out += len; 497 break; 498 499 default: 500 *out = *p; 501 out++; 502 break; 503 } 504 505 /* see if we're done counting args */ 506 if (lptr && !--argc) { 507 len = out - lptr - 4; 508 nl = htonl(len); 509 memcpy(lptr, &nl, 4); /* insert 4 byte len (network order) */ 510 lptr = NULL; 511 } 512 513 len = -1; 514 } 515 done: 516 va_end(ap); 517 518 *outlen = out - *buf; 519 520 return r; 521} 522 523/* 524 * Extract a PASSDSS buffer into the data specified by the fmt string. 525 */ 526static int UnBuffer(const sasl_utils_t *utils, const char *buf, 527 unsigned buflen, const char *fmt, ...) 528{ 529 va_list ap; 530 char *p; 531 BIGNUM **mpi; 532 char **os, **str; 533 uint32_t *u, nl; 534 unsigned len; 535 enum { OCTET_REFERENCE, /* just point to the data (reference it) */ 536 OCTET_COPY, /* copy the data into the given buffer */ 537 OCTET_ALLOC /* alloc space for the data, then copy */ 538 } octet_flag; 539 int r = SASL_OK; 540 541 va_start(ap, fmt); 542 for (p = (char *) fmt; *p; p++) { 543 if (*p != '%') { 544 if (*buf != *p) { 545 r = SASL_BADPROT; 546 goto done; 547 } 548 buf++; 549 buflen--; 550 continue; 551 } 552 p++; 553 554 /* check for octet flags */ 555 octet_flag = OCTET_COPY; 556 if (*p == '-') { 557 octet_flag = OCTET_REFERENCE; 558 p++; 559 } 560 else if (*p == '+') { 561 octet_flag = OCTET_ALLOC; 562 p++; 563 } 564 565 /* check for length prefix ('o', 'u', and 'p' only) */ 566 len = 0; 567 if (*p == '*') { 568 /* arg is length of next arg */ 569 len = va_arg(ap, int); 570 p++; 571 } 572 else if (isdigit((int) *p)) { 573 len = 0; 574 while (isdigit((int) *p)) len = 10 * len + *p++ - '0'; 575 } 576 577 switch (*p) { 578 case 'm': 579 /* MPI */ 580 mpi = va_arg(ap, BIGNUM **); 581 582 if (buflen < 4) { 583 SETERROR(utils, "Buffer is not big enough to be PASSDSS MPI\n"); 584 r = SASL_BADPROT; 585 goto done; 586 } 587 588 /* get the length */ 589 memcpy(&nl, buf, 4); 590 len = ntohl(nl); 591 buf += 4; 592 buflen -= 4; 593 594 /* make sure it's right */ 595 if (len > buflen) { 596 SETERROR(utils, "Not enough data for this PASSDSS MPI\n"); 597 r = SASL_BADPROT; 598 goto done; 599 } 600 601 if (mpi) { 602 if (!*mpi) *mpi = BN_new(); 603 BN_init(*mpi); 604 BN_bin2bn(buf, len, *mpi); 605 } 606 break; 607 608 case 'o': 609 /* octet sequence (len given by prefix) */ 610 os = va_arg(ap, char **); 611 612 /* make sure it's right */ 613 if (len > buflen) { 614 SETERROR(utils, "Not enough data for this PASSDSS os\n"); 615 r = SASL_BADPROT; 616 goto done; 617 } 618 619 if (os) { 620 if (octet_flag == OCTET_REFERENCE) 621 *os = (char *) buf; 622 else { 623 if (octet_flag == OCTET_ALLOC && 624 (*os = (char *) utils->malloc(len)) == NULL) { 625 r = SASL_NOMEM; 626 goto done; 627 } 628 629 memcpy(*os, buf, len); 630 } 631 } 632 break; 633 634 case 'p': 635 /* padding (max len given by prefix) */ 636 637 if (buflen < len) len = buflen; 638 break; 639 640 case 's': 641 /* string */ 642 str = va_arg(ap, char **); 643 if (str) *str = NULL; 644 645 if (buflen < 4) { 646 SETERROR(utils, "Buffer is not big enough to be PASSDSS string\n"); 647 r = SASL_BADPROT; 648 goto done; 649 } 650 651 /* get the length */ 652 memcpy(&nl, buf, 4); 653 len = ntohl(nl); 654 buf += 4; 655 buflen -= 4; 656 657 /* make sure it's right */ 658 if (len > buflen) { 659 SETERROR(utils, "Not enough data for this PASSDSS string\n"); 660 r = SASL_BADPROT; 661 goto done; 662 } 663 664 if (str) { 665 *str = (char *) utils->malloc(len+1); /* +1 for NUL */ 666 if (!*str) { 667 r = SASL_NOMEM; 668 goto done; 669 } 670 671 memcpy(*str, buf, len); 672 (*str)[len] = '\0'; 673 } 674 break; 675 676 case 'u': 677 /* unsigned int */ 678 u = va_arg(ap, uint32_t*); 679 680 if (!len) len = 4; 681 if (buflen < len) { 682 SETERROR(utils, "Buffer is not big enough to be PASSDSS uint32\n"); 683 r = SASL_BADPROT; 684 goto done; 685 } 686 687 if (u) { 688 memset(u, 0, 4); 689 memcpy(u + 4 - len, buf, len); 690 *u = ntohl(*u); 691 } 692 break; 693 694 default: 695 len = 1; 696 if (*buf != *p) { 697 r = SASL_BADPROT; 698 goto done; 699 } 700 break; 701 } 702 703 buf += len; 704 buflen -= len; 705 } 706 707 if (buflen != 0) { 708 SETERROR(utils, "Extra data in PASSDSS buffer\n"); 709 r = SASL_BADPROT; 710 } 711 712 done: 713 va_end(ap); 714 715 return r; 716} 717 718#define DOHASH(out, in1, len1, in2, len2, in3, len3) \ 719 EVP_DigestInit(&mdctx, EVP_sha1()); \ 720 EVP_DigestUpdate(&mdctx, in1, len1); \ 721 EVP_DigestUpdate(&mdctx, in2, len2); \ 722 EVP_DigestUpdate(&mdctx, in3, len3); \ 723 EVP_DigestFinal(&mdctx, out, NULL) 724 725void CalcLayerParams(context_t *text, char *K, unsigned Klen, 726 char *hash, unsigned hashlen) 727{ 728 EVP_MD_CTX mdctx; 729 730 DOHASH(text->cs_encryption_iv, K, Klen, "A", 1, hash, hashlen); 731 DOHASH(text->sc_encryption_iv, K, Klen, "B", 1, hash, hashlen); 732 DOHASH(text->cs_encryption_key, K, Klen, "C", 1, hash, hashlen); 733 DOHASH(text->cs_encryption_key + hashlen, K, Klen, "", 0, 734 text->cs_encryption_key, hashlen); 735 DOHASH(text->sc_encryption_key, K, Klen, "D", 1, hash, hashlen); 736 DOHASH(text->sc_encryption_key + hashlen, K, Klen, "", 0, 737 text->sc_encryption_key, hashlen); 738 DOHASH(text->cs_integrity_key, K, Klen, "E", 1, hash, hashlen); 739 DOHASH(text->sc_integrity_key, K, Klen, "F", 1, hash, hashlen); 740} 741 742/* 743 * Dispose of a PASSDSS context (could be server or client) 744 */ 745static void passdss_common_mech_dispose(void *conn_context, 746 const sasl_utils_t *utils) 747{ 748 context_t *text = (context_t *) conn_context; 749 750 if (!text) return; 751 752 if (text->authid) utils->free(text->authid); 753 if (text->userid) utils->free(text->userid); 754 if (text->free_password) _plug_free_secret(utils, &(text->password)); 755 756 if (text->dh) DH_free(text->dh); 757 758 HMAC_CTX_cleanup(&text->hmac_send_ctx); 759 HMAC_CTX_cleanup(&text->hmac_recv_ctx); 760 761 EVP_CIPHER_CTX_cleanup(&text->cipher_enc_ctx); 762 EVP_CIPHER_CTX_cleanup(&text->cipher_dec_ctx); 763 764 _plug_decode_free(&text->decode_context); 765 766 if (text->encode_buf) utils->free(text->encode_buf); 767 if (text->decode_buf) utils->free(text->decode_buf); 768 if (text->decode_pkt_buf) utils->free(text->decode_pkt_buf); 769 if (text->out_buf) utils->free(text->out_buf); 770 771 utils->free(text); 772} 773 774/***************************** Server Section *****************************/ 775 776static int passdss_server_mech_new(void *glob_context __attribute__((unused)), 777 sasl_server_params_t *sparams, 778 const char *challenge __attribute__((unused)), 779 unsigned challen __attribute__((unused)), 780 void **conn_context) 781{ 782 context_t *text; 783 784 /* holds state are in */ 785 text = sparams->utils->malloc(sizeof(context_t)); 786 if (text == NULL) { 787 MEMERROR(sparams->utils); 788 return SASL_NOMEM; 789 } 790 791 memset(text, 0, sizeof(context_t)); 792 793 text->state = 1; 794 text->utils = sparams->utils; 795 text->cs_integrity_key = text->recv_integrity_key + 4; 796 text->sc_integrity_key = text->send_integrity_key + 4; 797 798 *conn_context = text; 799 800 return SASL_OK; 801} 802 803static int 804passdss_server_mech_step1(context_t *text, 805 sasl_server_params_t *params, 806 const char *clientin, 807 unsigned clientinlen, 808 const char **serverout, 809 unsigned *serveroutlen, 810 sasl_out_params_t *oparams __attribute__((unused))) 811{ 812 BIGNUM *X = NULL; 813 DSA *dsa = NULL; 814 unsigned char *K = NULL; 815 unsigned Klen, hashlen; 816 int need, musthave; 817 EVP_MD_CTX mdctx; 818 unsigned char hash[EVP_MAX_MD_SIZE]; 819 DSA_SIG *sig = NULL; 820 int result; 821 822 /* Expect: 823 * 824 * (1) string azname ; authorization name 825 * (2) string authname ; authentication name 826 * (3) mpint X ; Diffie-Hellman parameter X 827 */ 828 829 result = UnBuffer(params->utils, clientin, clientinlen, 830 "%s%s%m", &text->userid, &text->authid, &X); 831 if (result) { 832 params->utils->seterror(params->utils->conn, 0, 833 "Error UnBuffering input in step 1"); 834 goto cleanup; 835 } 836 837 /* Fetch DSA (XXX create one for now) */ 838 dsa = DSA_generate_parameters(1024, NULL, 0, NULL, NULL, NULL, NULL); 839 if (!dsa) { 840 result = SASL_FAIL; 841 goto cleanup; 842 } 843 DSA_generate_key(dsa); 844 845 /* Create Diffie-Hellman parameters */ 846 text->dh = DH_new(); 847 BN_hex2bn(&text->dh->p, N); 848 BN_hex2bn(&text->dh->g, g); 849 DH_generate_key(text->dh); 850 851 /* Alloc space for shared secret K as mpint */ 852 K = text->utils->malloc(DH_size(text->dh) + 4); 853 if (!K) { 854 params->utils->log(NULL, SASL_LOG_ERR, "Error allocing K\n"); 855 result = SASL_NOMEM; 856 goto cleanup; 857 } 858 859 /* Calculate DH shared secret (leave space at head for length) */ 860 Klen = DH_compute_key(K+4, X, text->dh); 861 862 /* Prepend length in network byte order (make it a mpint) */ 863 *((uint32_t *) K) = htonl(Klen); 864 Klen += 4; 865 866 /* Which layers can we support? */ 867 if (params->props.maxbufsize < 32) { 868 need = musthave = 0; 869 } else { 870 need = params->props.max_ssf - params->external_ssf; 871 musthave = params->props.min_ssf - params->external_ssf; 872 } 873 874 if (musthave <= NO_LAYER_SSF) 875 text->secmask |= NO_LAYER_FLAG; 876 if ((musthave <= INTEGRITY_LAYER_SSF) && (INTEGRITY_LAYER_SSF <= need)) 877 text->secmask |= INTEGRITY_LAYER_FLAG; 878 if ((musthave <= PRIVACY_LAYER_SSF) && (PRIVACY_LAYER_SSF <= need)) 879 text->secmask |= PRIVACY_LAYER_FLAG; 880 881 882 /* Send out: 883 * 884 * (4) uint32 pklength ; length of SSH-style DSA server public key 885 * string "ssh-dss" ; constant string "ssh-dss" (lower case) 886 * mpint p ; DSA public key parameters 887 * mpint q 888 * mpint g 889 * mpint y 890 * (5) mpint Y ; Diffie-Hellman parameter Y 891 * (6) OCTET ssecmask ; SASL security layers offered 892 * (7) 3 OCTET sbuflen ; maximum server security layer block size 893 * (8) uint32 siglength ; length of SSH-style dss signature 894 * string "ssh-dss" ; constant string "ssh-dss" (lower case) 895 * mpint r ; DSA signature parameters 896 * mpint s 897 */ 898 899 /* Items (4) - (7) */ 900 result = MakeBuffer(text->utils, &text->out_buf, 0, &text->out_buf_len, 901 serveroutlen, "%5a%s%m%m%m%m%m%1o%3u", 902 "ssh-dss", dsa->p, dsa->q, dsa->g, dsa->pub_key, 903 text->dh->pub_key, &text->secmask, 904 (params->props.maxbufsize > 0xFFFFFF) ? 0xFFFFFF : 905 params->props.maxbufsize); 906 if (result) { 907 params->utils->log(NULL, SASL_LOG_ERR, "Error making output buffer\n"); 908 goto cleanup; 909 } 910 911 /* Hash (1) - (7) and K */ 912 EVP_DigestInit(&mdctx, EVP_sha1()); 913 /* (1) - (3) */ 914 EVP_DigestUpdate(&mdctx, clientin, clientinlen); 915 /* (4) - (7) */ 916 EVP_DigestUpdate(&mdctx, text->out_buf, *serveroutlen); 917 /* K */ 918 EVP_DigestUpdate(&mdctx, K, Klen); 919 EVP_DigestFinal(&mdctx, hash, &hashlen); 920 921 /* Calculate security layer params */ 922 CalcLayerParams(text, K, Klen, hash, hashlen); 923 924 /* Start cli-hmac */ 925 HMAC_CTX_init(&text->hmac_recv_ctx); 926 HMAC_Init_ex(&text->hmac_recv_ctx, text->cs_integrity_key, 927 SHA_DIGEST_LENGTH, EVP_sha1(), NULL); 928 /* (1) - (3) */ 929 HMAC_Update(&text->hmac_recv_ctx, clientin, clientinlen); 930 /* (4) - (7) */ 931 HMAC_Update(&text->hmac_recv_ctx, text->out_buf, *serveroutlen); 932 933 /* Sign the hash */ 934 sig = DSA_do_sign(hash, hashlen, dsa); 935 if (!sig) { 936 params->utils->log(NULL, SASL_LOG_ERR, 937 "Error calculating DSS signature\n"); 938 result = SASL_FAIL; 939 goto cleanup; 940 } 941 942 /* Item (8) */ 943 result = MakeBuffer(text->utils, &text->out_buf, *serveroutlen, 944 &text->out_buf_len, serveroutlen, 945 "%3a%s%m%m", "ssh-dss", sig->r, sig->s); 946 if (result) { 947 params->utils->log(NULL, SASL_LOG_ERR, "Error making output buffer\n"); 948 goto cleanup; 949 } 950 *serverout = text->out_buf; 951 952 text->state = 2; 953 result = SASL_CONTINUE; 954 955 cleanup: 956 if (X) BN_free(X); 957 if (K) text->utils->free(K); 958 if (dsa) DSA_free(dsa); 959 if (sig) DSA_SIG_free(sig); 960 961 return result; 962} 963 964static int 965passdss_server_mech_step2(context_t *text, 966 sasl_server_params_t *params, 967 const char *clientin, 968 unsigned clientinlen, 969 const char **serverout __attribute__((unused)), 970 unsigned *serveroutlen __attribute__((unused)), 971 sasl_out_params_t *oparams) 972{ 973 char *password = NULL; 974 unsigned declen, hmaclen; 975 unsigned char *csecmask, *cli_hmac, hmac[EVP_MAX_MD_SIZE]; 976 uint32_t cbufsiz; 977 int r, result = SASL_OK; 978 979 /* Expect (3DES encrypted): 980 * 981 * (9) OCTET csecmask ; SASL security layer selection 982 * 3 OCTET cbuflen ; maximum client block size 983 * string passphrase ; the user's passphrase 984 * 20 OCTET cli-hmac ; a client HMAC-SHA-1 signature 985 */ 986 987 /* Alloc space for the decrypted input */ 988 result = _plug_buf_alloc(text->utils, &text->decode_pkt_buf, 989 &text->decode_pkt_buf_len, clientinlen); 990 if (result) { 991 params->utils->log(NULL, SASL_LOG_ERR, 992 "Error allocating decrypt buffer in step 2\n"); 993 goto cleanup; 994 } 995 996 /* Initialize decrypt cipher */ 997 EVP_CIPHER_CTX_init(&text->cipher_dec_ctx); 998 EVP_DecryptInit_ex(&text->cipher_dec_ctx, EVP_des_ede3_cbc(), NULL, 999 text->cs_encryption_key, text->cs_encryption_iv); 1000 EVP_CIPHER_CTX_set_padding(&text->cipher_dec_ctx, 0); 1001 text->blk_siz = EVP_CIPHER_CTX_block_size(&text->cipher_dec_ctx); 1002 1003 /* Decrypt the blob */ 1004 r = EVP_DecryptUpdate(&text->cipher_dec_ctx, text->decode_pkt_buf, &declen, 1005 clientin, clientinlen); 1006 if (r) 1007 r = EVP_DecryptFinal_ex(&text->cipher_dec_ctx, /* should be no output */ 1008 text->decode_pkt_buf + declen, &declen); 1009 if (!r) { 1010 params->utils->seterror(params->utils->conn, 0, 1011 "Error decrypting input in step 2"); 1012 result = SASL_BADPROT; 1013 goto cleanup; 1014 } 1015 clientin = text->decode_pkt_buf; 1016 1017 result = UnBuffer(params->utils, clientin, clientinlen, 1018 "%-1o%3u%s%-*o%*p", &csecmask, &cbufsiz, &password, 1019 SHA_DIGEST_LENGTH, &cli_hmac, text->blk_siz - 1); 1020 if (result) { 1021 params->utils->seterror(params->utils->conn, 0, 1022 "Error UnBuffering input in step 2"); 1023 goto cleanup; 1024 } 1025 1026 /* Finish cli-hmac */ 1027 /* (1) - (7) hashed in step 1 */ 1028 /* 1st 4 bytes of (9) */ 1029 HMAC_Update(&text->hmac_recv_ctx, clientin, 4); 1030 HMAC_Final(&text->hmac_recv_ctx, hmac, &hmaclen); 1031 1032 /* Verify cli-hmac */ 1033 if (memcmp(cli_hmac, hmac, hmaclen)) { 1034 params->utils->seterror(params->utils->conn, 0, 1035 "Client HMAC verification failed"); 1036 result = SASL_BADMAC; 1037 goto cleanup; 1038 } 1039 1040 /* Canonicalize authentication ID first, so that password verification 1041 * is only against the canonical id */ 1042 result = params->canon_user(params->utils->conn, 1043 text->authid, 0, SASL_CU_AUTHID, oparams); 1044 if (result != SASL_OK) { 1045 return result; 1046 } 1047 1048 /* Verify password - return sasl_ok on success */ 1049 result = params->utils->checkpass(params->utils->conn, 1050 oparams->authid, oparams->alen, 1051 password, strlen(password)); 1052 1053 if (result != SASL_OK) { 1054 params->utils->seterror(params->utils->conn, 0, 1055 "Password verification failed"); 1056 goto cleanup; 1057 } 1058 1059 /* Canonicalize and store the authorization ID */ 1060 /* We need to do this after calling verify_user just in case verify_user 1061 * needed to get auxprops itself */ 1062 result = params->canon_user(params->utils->conn, 1063 *text->userid ? text->userid : text->authid, 0, 1064 SASL_CU_AUTHZID, oparams); 1065 if (result != SASL_OK) return result; 1066 1067 /* See which layer the client selected */ 1068 text->secmask &= *csecmask; 1069 if (text->secmask & PRIVACY_LAYER_FLAG) { 1070 oparams->mech_ssf = PRIVACY_LAYER_SSF; 1071 } else if (text->secmask & INTEGRITY_LAYER_FLAG) { 1072 oparams->mech_ssf = INTEGRITY_LAYER_SSF; 1073 } else if (text->secmask & NO_LAYER_FLAG) { 1074 oparams->mech_ssf = NO_LAYER_SSF; 1075 } else { 1076 /* Mark that we tried */ 1077 oparams->mech_ssf = 2; 1078 SETERROR(params->utils, 1079 "unable to agree on layers with server"); 1080 return SASL_BADPROT; 1081 } 1082 1083 /* Set oparams */ 1084 oparams->doneflag = 1; 1085 oparams->param_version = 0; 1086 1087 if (oparams->mech_ssf > 0) { 1088 oparams->encode = &passdss_encode; 1089 oparams->decode = &passdss_decode; 1090 oparams->maxoutbuf = cbufsiz - 4 - SHA_DIGEST_LENGTH; /* -len -HMAC */ 1091 1092 HMAC_CTX_init(&text->hmac_send_ctx); 1093 1094 if (oparams->mech_ssf > 1) { 1095 oparams->maxoutbuf -= text->blk_siz-1; /* padding */ 1096 1097 /* Initialize encrypt cipher */ 1098 EVP_CIPHER_CTX_init(&text->cipher_enc_ctx); 1099 EVP_EncryptInit_ex(&text->cipher_enc_ctx, EVP_des_ede3_cbc(), NULL, 1100 text->sc_encryption_key, text->sc_encryption_iv); 1101 EVP_CIPHER_CTX_set_padding(&text->cipher_enc_ctx, 0); 1102 } 1103 1104 _plug_decode_init(&text->decode_context, text->utils, 1105 (params->props.maxbufsize > 0xFFFFFF) ? 0xFFFFFF : 1106 params->props.maxbufsize); 1107 } 1108 else { 1109 oparams->encode = NULL; 1110 oparams->decode = NULL; 1111 oparams->maxoutbuf = 0; 1112 } 1113 1114 result = SASL_OK; 1115 1116 cleanup: 1117 if (password) _plug_free_string(params->utils, &password); 1118 1119 return result; 1120} 1121 1122static int passdss_server_mech_step(void *conn_context, 1123 sasl_server_params_t *sparams, 1124 const char *clientin, 1125 unsigned clientinlen, 1126 const char **serverout, 1127 unsigned *serveroutlen, 1128 sasl_out_params_t *oparams) 1129{ 1130 context_t *text = (context_t *) conn_context; 1131 1132 if (!sparams 1133 || !serverout 1134 || !serveroutlen 1135 || !oparams) 1136 return SASL_BADPARAM; 1137 1138 sparams->utils->log(NULL, SASL_LOG_DEBUG, 1139 "PASSDSS server step %d\n", text->state); 1140 1141 *serverout = NULL; 1142 *serveroutlen = 0; 1143 1144 switch (text->state) { 1145 1146 case 1: 1147 return passdss_server_mech_step1(text, sparams, clientin, clientinlen, 1148 serverout, serveroutlen, oparams); 1149 1150 case 2: 1151 return passdss_server_mech_step2(text, sparams, clientin, clientinlen, 1152 serverout, serveroutlen, oparams); 1153 1154 default: 1155 sparams->utils->seterror(sparams->utils->conn, 0, 1156 "Invalid PASSDSS server step %d", text->state); 1157 return SASL_FAIL; 1158 } 1159 1160 return SASL_FAIL; /* should never get here */ 1161} 1162 1163static sasl_server_plug_t passdss_server_plugins[] = 1164{ 1165 { 1166 "PASSDSS-3DES-1", /* mech_name */ 1167 112, /* max_ssf */ 1168 SASL_SEC_NOPLAINTEXT 1169 | SASL_SEC_NOANONYMOUS 1170 | SASL_SEC_NOACTIVE 1171 | SASL_SEC_NODICTIONARY 1172 | SASL_SEC_FORWARD_SECRECY 1173 | SASL_SEC_PASS_CREDENTIALS 1174 | SASL_SEC_MUTUAL_AUTH, /* security_flags */ 1175 SASL_FEAT_WANT_CLIENT_FIRST 1176 | SASL_FEAT_ALLOWS_PROXY, /* features */ 1177 NULL, /* glob_context */ 1178 &passdss_server_mech_new, /* mech_new */ 1179 &passdss_server_mech_step, /* mech_step */ 1180 &passdss_common_mech_dispose, /* mech_dispose */ 1181 NULL, /* mech_free */ 1182 NULL, /* setpass */ 1183 NULL, /* user_query */ 1184 NULL, /* idle */ 1185 NULL, /* mech_avail */ 1186 NULL /* spare */ 1187 } 1188}; 1189 1190int passdss_server_plug_init(const sasl_utils_t *utils, 1191 int maxversion, 1192 int *out_version, 1193 sasl_server_plug_t **pluglist, 1194 int *plugcount) 1195{ 1196 if (maxversion < SASL_SERVER_PLUG_VERSION) { 1197 SETERROR(utils, "PASSDSS version mismatch"); 1198 return SASL_BADVERS; 1199 } 1200 1201 *out_version = SASL_SERVER_PLUG_VERSION; 1202 *pluglist = passdss_server_plugins; 1203 *plugcount = 1; 1204 1205 return SASL_OK; 1206} 1207 1208/***************************** Client Section *****************************/ 1209 1210static int passdss_client_mech_new(void *glob_context __attribute__((unused)), 1211 sasl_client_params_t *params, 1212 void **conn_context) 1213{ 1214 context_t *text; 1215 1216 /* holds state are in */ 1217 text = params->utils->malloc(sizeof(context_t)); 1218 if (text == NULL) { 1219 MEMERROR(params->utils); 1220 return SASL_NOMEM; 1221 } 1222 1223 memset(text, 0, sizeof(context_t)); 1224 1225 text->state = 1; 1226 text->utils = params->utils; 1227 text->cs_integrity_key = text->send_integrity_key + 4; 1228 text->sc_integrity_key = text->recv_integrity_key + 4; 1229 1230 *conn_context = text; 1231 1232 return SASL_OK; 1233} 1234 1235static int 1236passdss_client_mech_step1(context_t *text, 1237 sasl_client_params_t *params, 1238 const char *serverin __attribute__((unused)), 1239 unsigned serverinlen __attribute__((unused)), 1240 sasl_interact_t **prompt_need, 1241 const char **clientout, 1242 unsigned *clientoutlen, 1243 sasl_out_params_t *oparams) 1244{ 1245 const char *user = NULL, *authid = NULL; 1246 int user_result = SASL_OK; 1247 int auth_result = SASL_OK; 1248 int pass_result = SASL_OK; 1249 int result; 1250 1251 /* Expect: absolutely nothing */ 1252 if (serverinlen > 0) { 1253 SETERROR(params->utils, "Invalid input to first step of PASSDSS\n"); 1254 return SASL_BADPROT; 1255 } 1256 1257 /* check if security layer is strong enough */ 1258 if (params->props.min_ssf > PRIVACY_LAYER_SSF + params->external_ssf) { 1259 SETERROR(params->utils, 1260 "minimum ssf too strong for PASSDSS"); 1261 return SASL_TOOWEAK; 1262 } 1263 1264 /* try to get the authid */ 1265 if (oparams->authid == NULL) { 1266 auth_result = _plug_get_authid(params->utils, &authid, prompt_need); 1267 1268 if ((auth_result != SASL_OK) && (auth_result != SASL_INTERACT)) 1269 return auth_result; 1270 } 1271 1272 /* try to get the userid */ 1273 if (oparams->user == NULL) { 1274 user_result = _plug_get_userid(params->utils, &user, prompt_need); 1275 1276 if ((user_result != SASL_OK) && (user_result != SASL_INTERACT)) 1277 return user_result; 1278 } 1279 1280 /* try to get the password */ 1281 if (text->password == NULL) { 1282 pass_result = _plug_get_password(params->utils, &text->password, 1283 &text->free_password, prompt_need); 1284 1285 if ((pass_result != SASL_OK) && (pass_result != SASL_INTERACT)) 1286 return pass_result; 1287 } 1288 1289 /* free prompts we got */ 1290 if (prompt_need && *prompt_need) { 1291 params->utils->free(*prompt_need); 1292 *prompt_need = NULL; 1293 } 1294 1295 /* if there are prompts not filled in */ 1296 if ((user_result == SASL_INTERACT) || (auth_result == SASL_INTERACT) || 1297 (pass_result == SASL_INTERACT)) { 1298 /* make the prompt list */ 1299 result = 1300 _plug_make_prompts(params->utils, prompt_need, 1301 user_result == SASL_INTERACT ? 1302 "Please enter your authorization name" : NULL, 1303 NULL, 1304 auth_result == SASL_INTERACT ? 1305 "Please enter your authentication name" : NULL, 1306 NULL, 1307 pass_result == SASL_INTERACT ? 1308 "Please enter your password" : NULL, NULL, 1309 NULL, NULL, NULL, 1310 NULL, NULL, NULL); 1311 if (result != SASL_OK) goto cleanup; 1312 1313 return SASL_INTERACT; 1314 } 1315 1316 if (!text->password) { 1317 PARAMERROR(params->utils); 1318 return SASL_BADPARAM; 1319 } 1320 1321 if (!user || !*user) { 1322 result = params->canon_user(params->utils->conn, authid, 0, 1323 SASL_CU_AUTHID | SASL_CU_AUTHZID, oparams); 1324 } 1325 else { 1326 result = params->canon_user(params->utils->conn, user, 0, 1327 SASL_CU_AUTHZID, oparams); 1328 if (result != SASL_OK) goto cleanup; 1329 1330 result = params->canon_user(params->utils->conn, authid, 0, 1331 SASL_CU_AUTHID, oparams); 1332 } 1333 if (result != SASL_OK) goto cleanup; 1334 1335 /* create Diffie-Hellman parameters */ 1336 text->dh = DH_new(); 1337 BN_hex2bn(&text->dh->p, N); 1338 BN_hex2bn(&text->dh->g, g); 1339 DH_generate_key(text->dh); 1340 1341 1342 /* Send out: 1343 * 1344 * (1) string azname ; authorization name 1345 * (2) string authname ; authentication name 1346 * (3) mpint X ; Diffie-Hellman parameter X 1347 */ 1348 1349 result = MakeBuffer(text->utils, &text->out_buf, 0, &text->out_buf_len, 1350 clientoutlen, "%s%s%m", 1351 (user && *user) ? (char *) oparams->user : "", 1352 (char *) oparams->authid, text->dh->pub_key); 1353 if (result) { 1354 params->utils->log(NULL, SASL_LOG_ERR, "Error making output buffer\n"); 1355 goto cleanup; 1356 } 1357 *clientout = text->out_buf; 1358 1359 text->state = 2; 1360 result = SASL_CONTINUE; 1361 1362 cleanup: 1363 1364 return result; 1365} 1366 1367static int 1368passdss_client_mech_step2(context_t *text, 1369 sasl_client_params_t *params, 1370 const char *serverin, 1371 unsigned serverinlen, 1372 sasl_interact_t **prompt_need __attribute__((unused)), 1373 const char **clientout, 1374 unsigned *clientoutlen, 1375 sasl_out_params_t *oparams) 1376{ 1377 DSA *dsa = DSA_new(); 1378 DSA_SIG *sig = DSA_SIG_new(); 1379 BIGNUM *Y = NULL; 1380 uint32_t siglen; 1381 unsigned char *K = NULL; 1382 unsigned Klen, hashlen, enclen; 1383 unsigned char *ssecmask; 1384 uint32_t sbufsiz; 1385 EVP_MD_CTX mdctx; 1386 unsigned char hash[EVP_MAX_MD_SIZE]; 1387 int need, musthave; 1388 int result, r; 1389 1390 /* Expect: 1391 * 1392 * (4) uint32 pklength ; length of SSH-style DSA server public key 1393 * string "ssh-dss" ; constant string "ssh-dss" (lower case) 1394 * mpint p ; DSA public key parameters 1395 * mpint q 1396 * mpint g 1397 * mpint y 1398 * (5) mpint Y ; Diffie-Hellman parameter Y 1399 * (6) OCTET ssecmask ; SASL security layers offered 1400 * (7) 3 OCTET sbuflen ; maximum server security layer block size 1401 * (8) uint32 siglength ; length of SSH-style dss signature 1402 * string "ssh-dss" ; constant string "ssh-dss" (lower case) 1403 * mpint r ; DSA signature parameters 1404 * mpint s 1405 */ 1406 1407 result = UnBuffer(params->utils, serverin, serverinlen, 1408 "%u%3p\7ssh-dss%m%m%m%m%m%-1o%3u%u%3p\7ssh-dss%m%m", 1409 NULL, &dsa->p, &dsa->q, &dsa->g, &dsa->pub_key, 1410 &Y, &ssecmask, &sbufsiz, &siglen, &sig->r, &sig->s); 1411 if (result) { 1412 params->utils->seterror(params->utils->conn, 0, 1413 "Error UnBuffering input in step 2"); 1414 goto cleanup; 1415 } 1416 1417 /* XXX Validate server DSA public key */ 1418 1419 /* Alloc space for shared secret K as mpint */ 1420 K = text->utils->malloc(DH_size(text->dh) + 4); 1421 if (!K) { 1422 params->utils->log(NULL, SASL_LOG_ERR, "Error allocing K\n"); 1423 result = SASL_NOMEM; 1424 goto cleanup; 1425 } 1426 1427 /* Calculate DH shared secret (leave space at head for length) */ 1428 Klen = DH_compute_key(K+4, Y, text->dh); 1429 1430 /* Prepend length in network byte order (make it a mpint) */ 1431 *((uint32_t *) K) = htonl(Klen); 1432 Klen += 4; 1433 1434 /* Hash (1) - (7) and K */ 1435 EVP_DigestInit(&mdctx, EVP_sha1()); 1436 /* (1) - (3) (output from step 1 still in buffer) */ 1437 EVP_DigestUpdate(&mdctx, text->out_buf, text->out_buf_len); 1438 /* (4) - (7) */ 1439 EVP_DigestUpdate(&mdctx, serverin, serverinlen - siglen - 4); 1440 /* K */ 1441 EVP_DigestUpdate(&mdctx, K, Klen); 1442 EVP_DigestFinal(&mdctx, hash, &hashlen); 1443 1444 /* Verify signature on the hash */ 1445 result = DSA_do_verify(hash, hashlen, sig, dsa); 1446 if (result != 1) { 1447 params->utils->log(NULL, SASL_LOG_ERR, 1448 (result == 0) ? "Incorrect DSS signature\n" : 1449 "Error verifying DSS signature\n"); 1450 result = (result == 0) ? SASL_BADPROT : SASL_FAIL; 1451 goto cleanup; 1452 } 1453 1454 /* Calculate security layer params */ 1455 CalcLayerParams(text, K, Klen, hash, hashlen); 1456 1457 /* Initialize encrypt cipher */ 1458 EVP_CIPHER_CTX_init(&text->cipher_enc_ctx); 1459 EVP_EncryptInit_ex(&text->cipher_enc_ctx, EVP_des_ede3_cbc(), NULL, 1460 text->cs_encryption_key, text->cs_encryption_iv); 1461 EVP_CIPHER_CTX_set_padding(&text->cipher_enc_ctx, 0); 1462 text->blk_siz = EVP_CIPHER_CTX_block_size(&text->cipher_enc_ctx); 1463 1464 /* pick a layer */ 1465 if (params->props.maxbufsize < 32) { 1466 need = musthave = 0; 1467 } else { 1468 need = params->props.max_ssf - params->external_ssf; 1469 musthave = params->props.min_ssf - params->external_ssf; 1470 } 1471 1472 if ((*ssecmask & PRIVACY_LAYER_FLAG) && 1473 (need >= PRIVACY_LAYER_SSF) && (musthave <= PRIVACY_LAYER_SSF)) { 1474 text->secmask = PRIVACY_LAYER_FLAG; 1475 oparams->mech_ssf = PRIVACY_LAYER_SSF; 1476 } else if ((*ssecmask & INTEGRITY_LAYER_FLAG) && 1477 (need >= INTEGRITY_LAYER_SSF) && 1478 (musthave <= INTEGRITY_LAYER_SSF)) { 1479 text->secmask =INTEGRITY_LAYER_FLAG; 1480 oparams->mech_ssf = INTEGRITY_LAYER_SSF; 1481 } else if ((*ssecmask & NO_LAYER_FLAG) && (musthave <= NO_LAYER_SSF)) { 1482 text->secmask = NO_LAYER_FLAG; 1483 oparams->mech_ssf = NO_LAYER_SSF; 1484 } else { 1485 /* Mark that we tried */ 1486 oparams->mech_ssf = 2; 1487 SETERROR(params->utils, 1488 "unable to agree on layers with server"); 1489 return SASL_BADPROT; 1490 } 1491 1492 /* Start cli-hmac */ 1493 HMAC_CTX_init(&text->hmac_send_ctx); 1494 HMAC_Init_ex(&text->hmac_send_ctx, text->cs_integrity_key, 1495 SHA_DIGEST_LENGTH, EVP_sha1(), NULL); 1496 /* (1) - (3) (output from step 1 still in buffer) */ 1497 HMAC_Update(&text->hmac_send_ctx, text->out_buf, text->out_buf_len); 1498 /* (4) - (7) */ 1499 HMAC_Update(&text->hmac_send_ctx, serverin, serverinlen - siglen - 4); 1500 1501 1502 /* Send out (3DES encrypted): 1503 * 1504 * (9) OCTET csecmask ; SASL security layer selection 1505 * 3 OCTET cbuflen ; maximum client block size 1506 * string passphrase ; the user's passphrase 1507 * 20 OCTET cli-hmac ; a client HMAC-SHA-1 signature 1508 */ 1509 1510 result = MakeBuffer(text->utils, &text->out_buf, 0, 1511 &text->out_buf_len, clientoutlen, "%1o%3u%*s", 1512 &text->secmask, 1513 (params->props.maxbufsize > 0xFFFFFF) ? 0xFFFFFF : 1514 params->props.maxbufsize, 1515 text->password->len, text->password->data); 1516 if (result) { 1517 params->utils->log(NULL, SASL_LOG_ERR, "Error making output buffer\n"); 1518 goto cleanup; 1519 } 1520 1521 /* Finish cli-hmac */ 1522 /* 1st 4 bytes of (9) */ 1523 HMAC_Update(&text->hmac_send_ctx, text->out_buf, 4); 1524 HMAC_Final(&text->hmac_send_ctx, hash, &hashlen); 1525 1526 /* Add HMAC and pad to fill no more than current block */ 1527 result = MakeBuffer(text->utils, &text->out_buf, *clientoutlen, 1528 &text->out_buf_len, clientoutlen, "%*o%*o", 1529 hashlen, hash, text->blk_siz - 1, text->padding); 1530 if (result) { 1531 params->utils->log(NULL, SASL_LOG_ERR, "Error making output buffer\n"); 1532 goto cleanup; 1533 } 1534 1535 /* Alloc space for the encrypted output */ 1536 result = _plug_buf_alloc(text->utils, &text->encode_buf, 1537 &text->encode_buf_len, *clientoutlen); 1538 if (result) { 1539 params->utils->log(NULL, SASL_LOG_ERR, 1540 "Error allocating encrypt buffer in step 2\n"); 1541 goto cleanup; 1542 } 1543 1544 /* Encrypt (9) (here we calculate the exact number of full blocks) */ 1545 r = EVP_EncryptUpdate(&text->cipher_enc_ctx, text->encode_buf, 1546 clientoutlen, text->out_buf, 1547 text->blk_siz * (*clientoutlen / text->blk_siz)); 1548 if (r) 1549 r = EVP_EncryptFinal_ex(&text->cipher_enc_ctx, /* should be no output */ 1550 text->encode_buf + *clientoutlen, &enclen); 1551 if (!r) { 1552 params->utils->seterror(params->utils->conn, 0, 1553 "Error encrypting output in step 2"); 1554 result = SASL_FAIL; 1555 goto cleanup; 1556 } 1557 *clientout = text->encode_buf; 1558 1559 /* Set oparams */ 1560 oparams->doneflag = 1; 1561 oparams->param_version = 0; 1562 1563 if (oparams->mech_ssf > 0) { 1564 oparams->encode = &passdss_encode; 1565 oparams->decode = &passdss_decode; 1566 oparams->maxoutbuf = sbufsiz - 4 - SHA_DIGEST_LENGTH; /* -len -HMAC */ 1567 1568 HMAC_CTX_init(&text->hmac_recv_ctx); 1569 1570 if (oparams->mech_ssf > 1) { 1571 oparams->maxoutbuf -= text->blk_siz-1; /* padding */ 1572 1573 /* Initialize decrypt cipher */ 1574 EVP_CIPHER_CTX_init(&text->cipher_dec_ctx); 1575 EVP_DecryptInit_ex(&text->cipher_dec_ctx, EVP_des_ede3_cbc(), NULL, 1576 text->sc_encryption_key, text->sc_encryption_iv); 1577 EVP_CIPHER_CTX_set_padding(&text->cipher_dec_ctx, 0); 1578 } 1579 1580 _plug_decode_init(&text->decode_context, text->utils, 1581 (params->props.maxbufsize > 0xFFFFFF) ? 0xFFFFFF : 1582 params->props.maxbufsize); 1583 } 1584 else { 1585 oparams->encode = NULL; 1586 oparams->decode = NULL; 1587 oparams->maxoutbuf = 0; 1588 } 1589 1590 result = SASL_OK; 1591 1592 cleanup: 1593 if (Y) BN_free(Y); 1594 if (K) text->utils->free(K); 1595 if (dsa) DSA_free(dsa); 1596 if (sig) DSA_SIG_free(sig); 1597 1598 return result; 1599} 1600 1601static int passdss_client_mech_step(void *conn_context, 1602 sasl_client_params_t *params, 1603 const char *serverin, 1604 unsigned serverinlen, 1605 sasl_interact_t **prompt_need, 1606 const char **clientout, 1607 unsigned *clientoutlen, 1608 sasl_out_params_t *oparams) 1609{ 1610 context_t *text = (context_t *) conn_context; 1611 1612 params->utils->log(NULL, SASL_LOG_DEBUG, 1613 "PASSDSS client step %d\n", text->state); 1614 1615 *clientout = NULL; 1616 *clientoutlen = 0; 1617 1618 switch (text->state) { 1619 1620 case 1: 1621 return passdss_client_mech_step1(text, params, serverin, serverinlen, 1622 prompt_need, clientout, clientoutlen, 1623 oparams); 1624 1625 case 2: 1626 return passdss_client_mech_step2(text, params, serverin, serverinlen, 1627 prompt_need, clientout, clientoutlen, 1628 oparams); 1629 1630 default: 1631 params->utils->log(NULL, SASL_LOG_ERR, 1632 "Invalid PASSDSS client step %d\n", text->state); 1633 return SASL_FAIL; 1634 } 1635 1636 return SASL_FAIL; /* should never get here */ 1637} 1638 1639 1640static sasl_client_plug_t passdss_client_plugins[] = 1641{ 1642 { 1643 "PASSDSS-3DES-1", /* mech_name */ 1644 112, /* max_ssf */ 1645 SASL_SEC_NOPLAINTEXT 1646 | SASL_SEC_NOANONYMOUS 1647 | SASL_SEC_NOACTIVE 1648 | SASL_SEC_NODICTIONARY 1649 | SASL_SEC_FORWARD_SECRECY 1650 | SASL_SEC_PASS_CREDENTIALS 1651 | SASL_SEC_MUTUAL_AUTH, /* security_flags */ 1652 SASL_FEAT_WANT_CLIENT_FIRST 1653 | SASL_FEAT_ALLOWS_PROXY, /* features */ 1654 NULL, /* required_prompts */ 1655 NULL, /* glob_context */ 1656 &passdss_client_mech_new, /* mech_new */ 1657 &passdss_client_mech_step, /* mech_step */ 1658 &passdss_common_mech_dispose, /* mech_dispose */ 1659 NULL, /* mech_free */ 1660 NULL, /* idle */ 1661 NULL, /* spare */ 1662 NULL /* spare */ 1663 } 1664}; 1665 1666int passdss_client_plug_init(sasl_utils_t *utils, 1667 int maxversion, 1668 int *out_version, 1669 sasl_client_plug_t **pluglist, 1670 int *plugcount) 1671{ 1672 if (maxversion < SASL_CLIENT_PLUG_VERSION) { 1673 SETERROR(utils, "PASSDSS version mismatch"); 1674 return SASL_BADVERS; 1675 } 1676 1677 *out_version = SASL_CLIENT_PLUG_VERSION; 1678 *pluglist = passdss_client_plugins; 1679 *plugcount = 1; 1680 1681 return SASL_OK; 1682} 1683