1/* DIGEST-MD5 SASL plugin 2 * Ken Murchison 3 * Rob Siemborski 4 * Tim Martin 5 * Alexey Melnikov 6 * $Id: digestmd5.c,v 1.9 2006/02/03 22:33:14 snsimon Exp $ 7 */ 8/* 9 * Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in 20 * the documentation and/or other materials provided with the 21 * distribution. 22 * 23 * 3. The name "Carnegie Mellon University" must not be used to 24 * endorse or promote products derived from this software without 25 * prior written permission. For permission or any other legal 26 * details, please contact 27 * Office of Technology Transfer 28 * Carnegie Mellon University 29 * 5000 Forbes Avenue 30 * Pittsburgh, PA 15213-3890 31 * (412) 268-4387, fax: (412) 268-7395 32 * tech-transfer@andrew.cmu.edu 33 * 34 * 4. Redistributions of any form whatsoever must retain the following 35 * acknowledgment: 36 * "This product includes software developed by Computing Services 37 * at Carnegie Mellon University (http://www.cmu.edu/computing/)." 38 * 39 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO 40 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 41 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE 42 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 43 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 44 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 45 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 46 */ 47 48#include <config.h> 49 50#include <stdlib.h> 51#include <stdio.h> 52#include <string.h> 53#ifndef macintosh 54#include <sys/types.h> 55#include <sys/stat.h> 56#endif 57#include <fcntl.h> 58#include <ctype.h> 59 60/* DES support */ 61#ifdef WITH_DES 62# ifdef WITH_SSL_DES 63# include <openssl/des.h> 64# include <openssl/opensslv.h> 65# if (OPENSSL_VERSION_NUMBER >= 0x0090700f) && \ 66 !defined(OPENSSL_ENABLE_OLD_DES_SUPPORT) 67# define des_cblock DES_cblock 68# define des_key_schedule DES_key_schedule 69# define des_key_sched(k,ks) \ 70 DES_key_sched((k),&(ks)) 71# define des_cbc_encrypt(i,o,l,k,iv,e) \ 72 DES_cbc_encrypt((i),(o),(l),&(k),(iv),(e)) 73# define des_ede2_cbc_encrypt(i,o,l,k1,k2,iv,e) \ 74 DES_ede2_cbc_encrypt((i),(o),(l),&(k1),&(k2),(iv),(e)) 75# endif /* OpenSSL 0.9.7+ w/o old DES support */ 76# else /* system DES library */ 77#ifdef HAVE_DES_H 78# include <des.h> 79#endif 80# endif 81#endif /* WITH_DES */ 82 83#ifdef WIN32 84# include <winsock2.h> 85#else /* Unix */ 86# include <netinet/in.h> 87#endif /* WIN32 */ 88 89#include <sasl.h> 90#include <saslplug.h> 91 92#include "plugin_common.h" 93 94#ifndef WIN32 95extern int strcasecmp(const char *s1, const char *s2); 96#endif /* end WIN32 */ 97 98#ifdef macintosh 99#include <sasl_md5_plugin_decl.h> 100#endif 101 102/* external definitions */ 103 104#ifdef sun 105/* gotta define gethostname ourselves on suns */ 106extern int gethostname(char *, int); 107#endif 108 109#define bool int 110 111#ifndef TRUE 112#define TRUE (1) 113#define FALSE (0) 114#endif 115 116/* MAX_UIN32_DIV_10 * 10 + MAX_UIN32_MOD_10 == 2^32-1 == 4294967295 */ 117#define MAX_UIN32_DIV_10 429496729 118#define MAX_UIN32_MOD_10 5 119 120#define DEFAULT_BUFSIZE 0xFFFF 121#define MAX_SASL_BUFSIZE 0xFFFFFF 122 123/***************************** Common Section *****************************/ 124 125//static const char plugin_id[] = "$Id: digestmd5.c,v 1.9 2006/02/03 22:33:14 snsimon Exp $"; 126 127/* Definitions */ 128#define NONCE_SIZE (32) /* arbitrary */ 129 130/* Layer Flags */ 131#define DIGEST_NOLAYER (1) 132#define DIGEST_INTEGRITY (2) 133#define DIGEST_PRIVACY (4) 134 135/* defines */ 136#define HASHLEN 16 137typedef unsigned char HASH[HASHLEN + 1]; 138#define HASHHEXLEN 32 139typedef unsigned char HASHHEX[HASHHEXLEN + 1]; 140 141#define MAC_SIZE 10 142#define MAC_OFFS 2 143 144const char *SEALING_CLIENT_SERVER="Digest H(A1) to client-to-server sealing key magic constant"; 145const char *SEALING_SERVER_CLIENT="Digest H(A1) to server-to-client sealing key magic constant"; 146 147const char *SIGNING_CLIENT_SERVER="Digest session key to client-to-server signing key magic constant"; 148const char *SIGNING_SERVER_CLIENT="Digest session key to server-to-client signing key magic constant"; 149 150#define HT (9) 151#define CR (13) 152#define LF (10) 153#define SP (32) 154#define DEL (127) 155 156#define NEED_ESCAPING "\"\\" 157 158#define REALM_CHAL_PREFIX "Available realms:" 159 160static char *quote (char *str); 161 162struct context; 163 164/* function definitions for cipher encode/decode */ 165typedef int cipher_function_t(struct context *, 166 const char *, 167 unsigned, 168 unsigned char[], 169 char *, 170 unsigned *); 171 172typedef int cipher_init_t(struct context *, unsigned char [16], 173 unsigned char [16]); 174typedef void cipher_free_t(struct context *); 175 176enum Context_type { SERVER = 0, CLIENT = 1 }; 177 178typedef struct cipher_context cipher_context_t; 179 180/* cached auth info used for fast reauth */ 181typedef struct reauth_entry { 182 char *authid; 183 char *realm; 184 unsigned char *nonce; 185 unsigned int nonce_count; 186 unsigned char *cnonce; 187 188 union { 189 struct { 190 time_t timestamp; 191 } s; /* server stuff */ 192 193 struct { 194 char *serverFQDN; 195 int protection; 196 struct digest_cipher *cipher; 197 unsigned long server_maxbuf; 198 199 /* for HTTP mode (RFC 2617) only */ 200 char *algorithm; 201 unsigned char *opaque; 202 } c; /* client stuff */ 203 } u; 204} reauth_entry_t; 205 206typedef struct reauth_cache { 207 /* static stuff */ 208 enum Context_type i_am; /* are we the client or server? */ 209 time_t timeout; 210 void *mutex; 211 unsigned size; 212 213 reauth_entry_t *e; /* fixed-size hash table of entries */ 214} reauth_cache_t; 215 216/* global context for reauth use */ 217typedef struct digest_glob_context { 218 reauth_cache_t *reauth; 219} digest_glob_context_t; 220 221/* context that stores info */ 222typedef struct context { 223 int state; /* state in the authentication we are in */ 224 enum Context_type i_am; /* are we the client or server? */ 225 int http_mode; /* use RFC 2617 compatible protocol? */ 226 227 reauth_cache_t *reauth; 228 229 char *authid; 230 char *realm; 231 unsigned char *nonce; 232 unsigned int nonce_count; 233 unsigned char *cnonce; 234 235 /* only used by the client */ 236 char ** realms; 237 int realm_cnt; 238 239 char *response_value; 240 241 unsigned int seqnum; 242 unsigned int rec_seqnum; /* for checking integrity */ 243 244 HASH Ki_send; 245 HASH Ki_receive; 246 247 HASH HA1; /* Kcc or Kcs */ 248 249 /* copy of utils from the params structures */ 250 const sasl_utils_t *utils; 251 252 /* For general use */ 253 char *out_buf; 254 unsigned out_buf_len; 255 256 /* for encoding/decoding */ 257 buffer_info_t *enc_in_buf; 258 char *encode_buf, *decode_buf, *decode_packet_buf; 259 unsigned encode_buf_len, decode_buf_len, decode_packet_buf_len; 260 261 decode_context_t decode_context; 262 263 /* if privacy mode is used use these functions for encode and decode */ 264 cipher_function_t *cipher_enc; 265 cipher_function_t *cipher_dec; 266 cipher_init_t *cipher_init; 267 cipher_free_t *cipher_free; 268 struct cipher_context *cipher_enc_context; 269 struct cipher_context *cipher_dec_context; 270} context_t; 271 272struct digest_cipher { 273 char *name; 274 sasl_ssf_t ssf; 275 int n; /* bits to make privacy key */ 276 int flag; /* a bitmask to make things easier for us */ 277 278 cipher_function_t *cipher_enc; 279 cipher_function_t *cipher_dec; 280 cipher_init_t *cipher_init; 281 cipher_free_t *cipher_free; 282}; 283#if 0 284static const unsigned char *COLON = ":"; 285#else 286static const unsigned char COLON[] = { ':', '\0' }; 287#endif 288/* Hashes a string to produce an unsigned short */ 289static unsigned hash(const char *str) 290{ 291 unsigned val = 0; 292 int i; 293 294 while (str && *str) { 295 i = (int) *str; 296 val ^= i; 297 val <<= 1; 298 str++; 299 } 300 301 return val; 302} 303 304static void CvtHex(HASH Bin, HASHHEX Hex) 305{ 306 unsigned short i; 307 unsigned char j; 308 309 for (i = 0; i < HASHLEN; i++) { 310 j = (Bin[i] >> 4) & 0xf; 311 if (j <= 9) 312 Hex[i * 2] = (j + '0'); 313 else 314 Hex[i * 2] = (j + 'a' - 10); 315 j = Bin[i] & 0xf; 316 if (j <= 9) 317 Hex[i * 2 + 1] = (j + '0'); 318 else 319 Hex[i * 2 + 1] = (j + 'a' - 10); 320 } 321 Hex[HASHHEXLEN] = '\0'; 322} 323 324/* 325 * calculate request-digest/response-digest as per HTTP Digest spec 326 */ 327void 328DigestCalcResponse(const sasl_utils_t * utils, 329 HASHHEX HA1, /* HEX(H(A1)) */ 330 unsigned char *pszNonce, /* nonce from server */ 331 unsigned int pszNonceCount, /* 8 hex digits */ 332 unsigned char *pszCNonce, /* client nonce */ 333 unsigned char *pszQop, /* qop-value: "", "auth", 334 * "auth-int" */ 335 unsigned char *pszDigestUri, /* requested URL */ 336 unsigned char *pszMethod, 337 HASHHEX HEntity, /* H(entity body) if qop="auth-int" */ 338 HASHHEX Response /* request-digest or response-digest */ 339 ) 340{ 341 MD5_CTX Md5Ctx; 342 HASH HA2; 343 HASH RespHash; 344 HASHHEX HA2Hex; 345 unsigned char ncvalue[10]; 346 347 /* calculate H(A2) */ 348 utils->MD5Init(&Md5Ctx); 349 350 /* APPLE */ 351 #if DEBUG 352 utils->seterror(utils->conn,0, "xxx pszNonce %s", pszNonce?pszNonce:"NULL"); 353 utils->seterror(utils->conn,0, "xxx pszNonceCount %d", pszNonceCount?pszNonceCount:0); 354 utils->seterror(utils->conn,0, "xxx pszCNonce %s", pszCNonce?pszCNonce:"NULL"); 355 utils->seterror(utils->conn,0, "xxx pszQop %s", pszQop?pszQop:"NULL"); 356 utils->seterror(utils->conn,0, "xxx pszDigestUri %s", pszDigestUri?pszDigestUri:"NULL"); 357 utils->seterror(utils->conn,0, "xxx method %s", pszMethod?pszMethod:"NULL"); 358 #endif 359 360 if (pszMethod != NULL) { 361 utils->MD5Update(&Md5Ctx, pszMethod, (unsigned) strlen((char *) pszMethod)); 362 } 363 utils->MD5Update(&Md5Ctx, (unsigned char *) COLON, 1); 364 365 /* utils->MD5Update(&Md5Ctx, (unsigned char *) "AUTHENTICATE:", 13); */ 366 utils->MD5Update(&Md5Ctx, pszDigestUri, (unsigned) strlen((char *) pszDigestUri)); 367 if (strcasecmp((char *) pszQop, "auth") != 0) { 368 /* append ":00000000000000000000000000000000" */ 369 utils->MD5Update(&Md5Ctx, COLON, 1); 370 utils->MD5Update(&Md5Ctx, HEntity, HASHHEXLEN); 371 } 372 utils->MD5Final(HA2, &Md5Ctx); 373 CvtHex(HA2, HA2Hex); 374 375 /* calculate response */ 376 utils->MD5Init(&Md5Ctx); 377 utils->MD5Update(&Md5Ctx, HA1, HASHHEXLEN); 378 utils->MD5Update(&Md5Ctx, COLON, 1); 379 utils->MD5Update(&Md5Ctx, pszNonce, (unsigned) strlen((char *) pszNonce)); 380 utils->MD5Update(&Md5Ctx, COLON, 1); 381 if (*pszQop) { 382 sprintf((char *)ncvalue, "%08x", pszNonceCount); 383 utils->MD5Update(&Md5Ctx, ncvalue, (unsigned) strlen((char *)ncvalue)); 384 utils->MD5Update(&Md5Ctx, COLON, 1); 385 utils->MD5Update(&Md5Ctx, pszCNonce, (unsigned) strlen((char *) pszCNonce)); 386 utils->MD5Update(&Md5Ctx, COLON, 1); 387 utils->MD5Update(&Md5Ctx, pszQop, (unsigned) strlen((char *) pszQop)); 388 utils->MD5Update(&Md5Ctx, COLON, 1); 389 } 390 utils->MD5Update(&Md5Ctx, HA2Hex, HASHHEXLEN); 391 utils->MD5Final(RespHash, &Md5Ctx); 392 CvtHex(RespHash, Response); 393} 394 395static bool UTF8_In_8859_1(const unsigned char *base, size_t len) 396{ 397 const unsigned char *scan, *end; 398 399 end = base + len; 400 for (scan = base; scan < end; ++scan) { 401 if (*scan > 0xC3) 402 break; /* abort if outside 8859-1 */ 403 if (*scan >= 0xC0 && *scan <= 0xC3) { 404 if (++scan == end || *scan < 0x80 || *scan > 0xBF) 405 break; 406 } 407 } 408 409 /* if scan >= end, then this is a 8859-1 string. */ 410 return (scan >= end); 411} 412 413/* 414 * if the string is entirely in the 8859-1 subset of UTF-8, then translate to 415 * 8859-1 prior to MD5 416 */ 417static void MD5_UTF8_8859_1(const sasl_utils_t * utils, 418 MD5_CTX * ctx, 419 bool In_ISO_8859_1, 420 const unsigned char *base, 421 int len) 422{ 423 const unsigned char *scan, *end; 424 unsigned char cbuf; 425 426 end = base + len; 427 428 /* if we found a character outside 8859-1, don't alter string */ 429 if (!In_ISO_8859_1) { 430 utils->MD5Update(ctx, base, len); 431 return; 432 } 433 /* convert to 8859-1 prior to applying hash */ 434 do { 435 for (scan = base; scan < end && *scan < 0xC0; ++scan); 436 if (scan != base) 437 utils->MD5Update(ctx, base, (unsigned) (scan - base)); 438 if (scan + 1 >= end) 439 break; 440 cbuf = ((scan[0] & 0x3) << 6) | (scan[1] & 0x3f); 441 utils->MD5Update(ctx, &cbuf, 1); 442 base = scan + 2; 443 } 444 while (base < end); 445} 446 447/** 448 * Returns true if it mangled the username. 449 */ 450static bool DigestCalcSecret(const sasl_utils_t * utils, 451 unsigned char *pszUserName, 452 unsigned char *pszRealm, 453 unsigned char *Password, 454 int PasswordLen, 455 bool Ignore_8859, 456 HASH HA1) 457{ 458 bool In_8859_1; 459 bool Any_8859_1 = FALSE; 460 MD5_CTX Md5Ctx; 461 462 /* Chris Newman clarified that the following text in DIGEST-MD5 spec 463 is bogus: "if name and password are both in ISO 8859-1 charset" 464 We shoud use code example instead */ 465 466 utils->MD5Init(&Md5Ctx); 467 468 /* We have to convert UTF-8 to ISO-8859-1 if possible */ 469 if (Ignore_8859 == FALSE) { 470 In_8859_1 = UTF8_In_8859_1(pszUserName, strlen((char *) pszUserName)); 471 MD5_UTF8_8859_1(utils, &Md5Ctx, In_8859_1, 472 pszUserName, (unsigned) strlen((char *) pszUserName)); 473 Any_8859_1 |= In_8859_1; 474 } else { 475 utils->MD5Update(&Md5Ctx, pszUserName, (unsigned) strlen((char *) pszUserName)); 476 } 477 478 utils->MD5Update(&Md5Ctx, COLON, 1); 479 480 /* a NULL realm is equivalent to the empty string */ 481 if (pszRealm != NULL && pszRealm[0] != '\0') { 482 if (Ignore_8859 == FALSE) { 483 /* We have to convert UTF-8 to ISO-8859-1 if possible */ 484 In_8859_1 = UTF8_In_8859_1(pszRealm, strlen((char *) pszRealm)); 485 MD5_UTF8_8859_1(utils, &Md5Ctx, In_8859_1, 486 pszRealm, (unsigned) strlen((char *) pszRealm)); 487 Any_8859_1 |= In_8859_1; 488 } else { 489 utils->MD5Update(&Md5Ctx, pszRealm, (unsigned) strlen((char *) pszRealm)); 490 } 491 } 492 493 utils->MD5Update(&Md5Ctx, COLON, 1); 494 495 if (Ignore_8859 == FALSE) { 496 /* We have to convert UTF-8 to ISO-8859-1 if possible */ 497 In_8859_1 = UTF8_In_8859_1(Password, PasswordLen); 498 MD5_UTF8_8859_1(utils, &Md5Ctx, In_8859_1, 499 Password, PasswordLen); 500 Any_8859_1 |= In_8859_1; 501 } else { 502 utils->MD5Update(&Md5Ctx, Password, PasswordLen); 503 } 504 utils->MD5Final(HA1, &Md5Ctx); 505 506 return Any_8859_1; 507} 508 509static unsigned char *create_nonce(const sasl_utils_t * utils) 510{ 511 unsigned char *base64buf; 512 int base64len; 513 514 char *ret = (char *) utils->malloc(NONCE_SIZE); 515 if (ret == NULL) 516 return NULL; 517 518 utils->rand(utils->rpool, (char *) ret, NONCE_SIZE); 519 520 /* base 64 encode it so it has valid chars */ 521 base64len = (NONCE_SIZE * 4 / 3) + (NONCE_SIZE % 3 ? 4 : 0); 522 523 base64buf = (unsigned char *) utils->malloc(base64len + 1); 524 if (base64buf == NULL) { 525 utils->seterror(utils->conn, 0, "Unable to allocate final buffer"); 526 return NULL; 527 } 528 529 /* 530 * Returns SASL_OK on success, SASL_BUFOVER if result won't fit 531 */ 532 if (utils->encode64(ret, NONCE_SIZE, 533 (char *) base64buf, base64len, NULL) != SASL_OK) { 534 utils->free(ret); 535 return NULL; 536 } 537 utils->free(ret); 538 539 return base64buf; 540} 541 542static int add_to_challenge(const sasl_utils_t *utils, 543 char **str, unsigned *buflen, unsigned *curlen, 544 char *name, 545 unsigned char *value, 546 bool need_quotes) 547{ 548 size_t namesize = strlen(name); 549 size_t valuesize = strlen((char *) value); 550 unsigned newlen; 551 int ret; 552 553 newlen = (unsigned) (*curlen + 1 + namesize + 2 + valuesize + 2); 554 ret = _plug_buf_alloc(utils, str, buflen, newlen); 555 if(ret != SASL_OK) return ret; 556 557 if (*curlen > 0) { 558 strcat(*str, ","); 559 strcat(*str, name); 560 } else { 561 strcpy(*str, name); 562 } 563 564 if (need_quotes) { 565 strcat(*str, "=\""); 566 567 /* Check if the value needs quoting */ 568 if (strpbrk ((char *)value, NEED_ESCAPING) != NULL) { 569 char * quoted = quote ((char *) value); 570 valuesize = strlen(quoted); 571 /* As the quoted string is bigger, make sure we have enough 572 space now */ 573 ret = _plug_buf_alloc(utils, str, buflen, newlen); 574 if (ret == SASL_OK) { 575 strcat(*str, quoted); 576 free (quoted); 577 } else { 578 free (quoted); 579 return ret; 580 } 581 } else { 582 strcat(*str, (char *) value); 583 } 584 strcat(*str, "\""); 585 } else { 586 strcat(*str, "="); 587 strcat(*str, (char *) value); 588 } 589 590 *curlen = newlen; 591 return SASL_OK; 592} 593 594static int is_lws_char (char c) 595{ 596 return (c == ' ' || c == HT || c == CR || c == LF); 597} 598 599static char *skip_lws (char *s) 600{ 601 if (!s) return NULL; 602 603 /* skipping spaces: */ 604 while (is_lws_char(s[0])) { 605 if (s[0] == '\0') break; 606 s++; 607 } 608 609 return s; 610} 611 612/* Same as skip_lws, but do this right to left */ 613/* skip LWSP at the end of the value (if any), skip_r_lws returns pointer to 614 the first LWSP character, NUL (if there were none) or NULL if the value 615 is entirely from LWSP characters */ 616static char *skip_r_lws (char *s) 617{ 618 char *end; 619 size_t len; 620 621 if (!s) return NULL; 622 623 len = strlen(s); 624 if (len == 0) return NULL; 625 626 /* the last character before terminating NUL */ 627 end = s + len - 1; 628 629 /* skipping spaces: */ 630 while (end > s && (end[0] == ' ' || end[0] == HT || end[0] == CR || end[0] == LF)) { 631 end--; 632 } 633 634 /* If all string from spaces, return NULL */ 635 if (end == s && (end[0] == ' ' || end[0] == HT || end[0] == CR || end[0] == LF)) { 636 return NULL; 637 } else { 638 return (end + 1); 639 } 640} 641 642static char *skip_token (char *s, int caseinsensitive) 643{ 644 if(!s) return NULL; 645 646 while (s[0]>SP) { 647 if (s[0]==DEL || s[0]=='(' || s[0]==')' || s[0]=='<' || s[0]=='>' || 648 s[0]=='@' || s[0]==',' || s[0]==';' || s[0]==':' || s[0]=='\\' || 649 s[0]=='\'' || s[0]=='/' || s[0]=='[' || s[0]==']' || s[0]== '?' || 650 s[0]=='=' || s[0]== '{' || s[0]== '}') { 651 if (caseinsensitive == 1) { 652 if (!isupper((unsigned char) s[0])) 653 break; 654 } else { 655 break; 656 } 657 } 658 s++; 659 } 660 return s; 661} 662 663/* Convert a string to 32 bit unsigned integer. 664 Any number of trailing spaces is allowed, but not a string 665 entirely comprised of spaces */ 666static bool str2ul32 (char *str, unsigned long * value) 667{ 668 unsigned int n; 669 char c; 670 671 if (str == NULL) { 672 return (FALSE); 673 } 674 675 *value = 0; 676 677 str = skip_lws (str); 678 if (str[0] == '\0') { 679 return (FALSE); 680 } 681 682 n = 0; 683 while (str[0] != '\0') { 684 c = str[0]; 685 if (!isdigit((int)c)) { 686 return (FALSE); 687 } 688 689/* Will overflow after adding additional digit */ 690 if (n > MAX_UIN32_DIV_10) { 691 return (FALSE); 692 } else if (n == MAX_UIN32_DIV_10 && ((unsigned) (c - '0') > MAX_UIN32_MOD_10)) { 693 return (FALSE); 694 } 695 696 n = n * 10 + (unsigned) (c - '0'); 697 str++; 698 } 699 700 *value = n; 701 return (TRUE); 702} 703 704/* NULL - error (unbalanced quotes), 705 otherwise pointer to the first character after the value. 706 The function performs work in place. */ 707static char *unquote (char *qstr) 708{ 709 char *endvalue; 710 int escaped = 0; 711 char *outptr; 712 713 if(!qstr) return NULL; 714 715 if (qstr[0] == '"') { 716 qstr++; 717 outptr = qstr; 718 719 for (endvalue = qstr; endvalue[0] != '\0'; endvalue++, outptr++) { 720 if (escaped) { 721 outptr[0] = endvalue[0]; 722 escaped = 0; 723 } 724 else if (endvalue[0] == '\\') { 725 escaped = 1; 726 outptr--; /* Will be incremented at the end of the loop */ 727 } 728 else if (endvalue[0] == '"') { 729 break; 730 } 731 else { 732 outptr[0] = endvalue[0]; 733 } 734 } 735 736 if (endvalue[0] != '"') { 737 return NULL; 738 } 739 740 while (outptr <= endvalue) { 741 outptr[0] = '\0'; 742 outptr++; 743 } 744 endvalue++; 745 } 746 else { /* not qouted value (token) */ 747 /* qstr already contains output */ 748 endvalue = skip_token(qstr,0); 749 }; 750 751 return endvalue; 752} 753 754/* Unlike unquote, this function returns an allocated quoted copy */ 755static char *quote (char *str) 756{ 757 char *p; 758 char *outp; 759 char *result; 760 int num_to_escape; /* How many characters need escaping */ 761 762 if (!str) return NULL; 763 764 num_to_escape = 0; 765 p = strpbrk (str, NEED_ESCAPING); 766 while (p != NULL) { 767 num_to_escape++; 768 p = strpbrk (p + 1, NEED_ESCAPING); 769 } 770 771 if (num_to_escape == 0) { 772 return (strdup (str)); 773 } 774 775 result = malloc (strlen(str) + num_to_escape + 1); 776 for (p = str, outp = result; *p; p++) { 777 if (*p == '"' || *p == '\\') { 778 *outp = '\\'; 779 outp++; 780 } 781 *outp = *p; 782 outp++; 783 } 784 785 *outp = '\0'; 786 787 return (result); 788} 789 790static void get_pair(char **in, char **name, char **value) 791{ 792 char *endpair; 793 char *curp = *in; 794 *name = NULL; 795 *value = NULL; 796 797 if (curp == NULL) return; 798 799 while (curp[0] != '\0') { 800 /* skipping spaces: */ 801 curp = skip_lws(curp); 802 803 /* 'LWS "," LWS "," ...' is allowed by the DIGEST-MD5 ABNF */ 804 if (curp[0] == ',') { 805 curp++; 806 } else { 807 break; 808 } 809 } 810 811 if (curp[0] == '\0') { 812 /* End of the string is not an error */ 813 *name = ""; 814 return; 815 } 816 817 *name = curp; 818 819 curp = skip_token(curp,1); 820 821 /* strip wierd chars */ 822 if (curp[0] != '=' && curp[0] != '\0') { 823 *curp++ = '\0'; 824 }; 825 826 curp = skip_lws(curp); 827 828 if (curp[0] != '=') { /* No '=' sign */ 829 *name = NULL; 830 return; 831 } 832 833 curp[0] = '\0'; 834 curp++; 835 836 curp = skip_lws(curp); 837 838 *value = (curp[0] == '"') ? curp+1 : curp; 839 840 endpair = unquote (curp); 841 if (endpair == NULL) { /* Unbalanced quotes */ 842 *name = NULL; 843 *value = NULL; 844 return; 845 } 846 847 /* An optional LWS is allowed after the value. Skip it. */ 848 if (is_lws_char (endpair[0])) { 849 /* Remove the trailing LWS from the value */ 850 *endpair++ = '\0'; 851 endpair = skip_lws(endpair); 852 } 853 854 /* syntax check: MUST be '\0' or ',' */ 855 if (endpair[0] == ',') { 856 endpair[0] = '\0'; 857 endpair++; /* skipping <,> */ 858 } else if (endpair[0] != '\0') { 859 *name = NULL; 860 *value = NULL; 861 return; 862 } 863 864 *in = endpair; 865} 866 867#ifdef WITH_DES 868struct des_context_s { 869 des_key_schedule keysched; /* key schedule for des initialization */ 870 des_cblock ivec; /* initial vector for encoding */ 871 des_key_schedule keysched2; /* key schedule for 3des initialization */ 872}; 873 874typedef struct des_context_s des_context_t; 875 876/* slide the first 7 bytes of 'inbuf' into the high seven bits of the 877 first 8 bytes of 'keybuf'. 'keybuf' better be 8 bytes long or longer. */ 878static void slidebits(unsigned char *keybuf, unsigned char *inbuf) 879{ 880 keybuf[0] = inbuf[0]; 881 keybuf[1] = (inbuf[0]<<7) | (inbuf[1]>>1); 882 keybuf[2] = (inbuf[1]<<6) | (inbuf[2]>>2); 883 keybuf[3] = (inbuf[2]<<5) | (inbuf[3]>>3); 884 keybuf[4] = (inbuf[3]<<4) | (inbuf[4]>>4); 885 keybuf[5] = (inbuf[4]<<3) | (inbuf[5]>>5); 886 keybuf[6] = (inbuf[5]<<2) | (inbuf[6]>>6); 887 keybuf[7] = (inbuf[6]<<1); 888} 889 890/****************************** 891 * 892 * 3DES functions 893 * 894 *****************************/ 895 896static int dec_3des(context_t *text, 897 const char *input, 898 unsigned inputlen, 899 unsigned char digest[16] __attribute__((unused)), 900 char *output, 901 unsigned *outputlen) 902{ 903 des_context_t *c = (des_context_t *) text->cipher_dec_context; 904 int padding, p; 905 906 des_ede2_cbc_encrypt((void *) input, 907 (void *) output, 908 inputlen, 909 c->keysched, 910 c->keysched2, 911 &c->ivec, 912 DES_DECRYPT); 913 914 /* now chop off the padding */ 915 padding = output[inputlen - 11]; 916 if (padding < 1 || padding > 8) { 917 /* invalid padding length */ 918 return SASL_FAIL; 919 } 920 /* verify all padding is correct */ 921 for (p = 1; p <= padding; p++) { 922 if (output[inputlen - 10 - p] != padding) { 923 return SASL_FAIL; 924 } 925 } 926 927 /* chop off the padding */ 928 *outputlen = inputlen - padding - 10; 929 930 return SASL_OK; 931} 932 933static int enc_3des(context_t *text, 934 const char *input, 935 unsigned inputlen, 936 unsigned char digest[16], 937 char *output, 938 unsigned *outputlen) 939{ 940 des_context_t *c = (des_context_t *) text->cipher_enc_context; 941 int len; 942 int paddinglen; 943 944 /* determine padding length */ 945 paddinglen = 8 - ((inputlen + 10) % 8); 946 947 /* now construct the full stuff to be ciphered */ 948 memcpy(output, input, inputlen); /* text */ 949 memset(output+inputlen, paddinglen, paddinglen);/* pad */ 950 memcpy(output+inputlen+paddinglen, digest, 10); /* hmac */ 951 952 len=inputlen+paddinglen+10; 953 954 des_ede2_cbc_encrypt((void *) output, 955 (void *) output, 956 len, 957 c->keysched, 958 c->keysched2, 959 &c->ivec, 960 DES_ENCRYPT); 961 962 *outputlen=len; 963 964 return SASL_OK; 965} 966 967static int init_3des(context_t *text, 968 unsigned char enckey[16], 969 unsigned char deckey[16]) 970{ 971 des_context_t *c; 972 unsigned char keybuf[8]; 973 974 /* allocate enc & dec context */ 975 c = (des_context_t *) text->utils->malloc(2 * sizeof(des_context_t)); 976 if (c == NULL) return SASL_NOMEM; 977 978 /* setup enc context */ 979 slidebits(keybuf, enckey); 980 if (des_key_sched((des_cblock *) keybuf, c->keysched) < 0) 981 return SASL_FAIL; 982 983 slidebits(keybuf, enckey + 7); 984 if (des_key_sched((des_cblock *) keybuf, c->keysched2) < 0) 985 return SASL_FAIL; 986 memcpy(c->ivec, ((char *) enckey) + 8, 8); 987 988 text->cipher_enc_context = (cipher_context_t *) c; 989 990 /* setup dec context */ 991 c++; 992 slidebits(keybuf, deckey); 993 if (des_key_sched((des_cblock *) keybuf, c->keysched) < 0) 994 return SASL_FAIL; 995 996 slidebits(keybuf, deckey + 7); 997 if (des_key_sched((des_cblock *) keybuf, c->keysched2) < 0) 998 return SASL_FAIL; 999 1000 memcpy(c->ivec, ((char *) deckey) + 8, 8); 1001 1002 text->cipher_dec_context = (cipher_context_t *) c; 1003 1004 return SASL_OK; 1005} 1006 1007 1008/****************************** 1009 * 1010 * DES functions 1011 * 1012 *****************************/ 1013 1014static int dec_des(context_t *text, 1015 const char *input, 1016 unsigned inputlen, 1017 unsigned char digest[16] __attribute__((unused)), 1018 char *output, 1019 unsigned *outputlen) 1020{ 1021 des_context_t *c = (des_context_t *) text->cipher_dec_context; 1022 int p, padding = 0; 1023 1024 des_cbc_encrypt((void *) input, 1025 (void *) output, 1026 inputlen, 1027 c->keysched, 1028 &c->ivec, 1029 DES_DECRYPT); 1030 1031 /* Update the ivec (des_cbc_encrypt implementations tend to be broken in 1032 this way) */ 1033 memcpy(c->ivec, input + (inputlen - 8), 8); 1034 1035 /* now chop off the padding */ 1036 padding = output[inputlen - 11]; 1037 if (padding < 1 || padding > 8) { 1038 /* invalid padding length */ 1039 return SASL_FAIL; 1040 } 1041 /* verify all padding is correct */ 1042 for (p = 1; p <= padding; p++) { 1043 if (output[inputlen - 10 - p] != padding) { 1044 return SASL_FAIL; 1045 } 1046 } 1047 1048 /* chop off the padding */ 1049 *outputlen = inputlen - padding - 10; 1050 1051 return SASL_OK; 1052} 1053 1054static int enc_des(context_t *text, 1055 const char *input, 1056 unsigned inputlen, 1057 unsigned char digest[16], 1058 char *output, 1059 unsigned *outputlen) 1060{ 1061 des_context_t *c = (des_context_t *) text->cipher_enc_context; 1062 int len; 1063 int paddinglen; 1064 1065 /* determine padding length */ 1066 paddinglen = 8 - ((inputlen+10) % 8); 1067 1068 /* now construct the full stuff to be ciphered */ 1069 memcpy(output, input, inputlen); /* text */ 1070 memset(output+inputlen, paddinglen, paddinglen);/* pad */ 1071 memcpy(output+inputlen+paddinglen, digest, 10); /* hmac */ 1072 1073 len = inputlen + paddinglen + 10; 1074 1075 des_cbc_encrypt((void *) output, 1076 (void *) output, 1077 len, 1078 c->keysched, 1079 &c->ivec, 1080 DES_ENCRYPT); 1081 1082 /* Update the ivec (des_cbc_encrypt implementations tend to be broken in 1083 this way) */ 1084 memcpy(c->ivec, output + (len - 8), 8); 1085 1086 *outputlen = len; 1087 1088 return SASL_OK; 1089} 1090 1091static int init_des(context_t *text, 1092 unsigned char enckey[16], 1093 unsigned char deckey[16]) 1094{ 1095 des_context_t *c; 1096 unsigned char keybuf[8]; 1097 1098 /* allocate enc context */ 1099 c = (des_context_t *) text->utils->malloc(2 * sizeof(des_context_t)); 1100 if (c == NULL) return SASL_NOMEM; 1101 1102 /* setup enc context */ 1103 slidebits(keybuf, enckey); 1104 des_key_sched((des_cblock *) keybuf, c->keysched); 1105 1106 memcpy(c->ivec, ((char *) enckey) + 8, 8); 1107 1108 text->cipher_enc_context = (cipher_context_t *) c; 1109 1110 /* setup dec context */ 1111 c++; 1112 slidebits(keybuf, deckey); 1113 des_key_sched((des_cblock *) keybuf, c->keysched); 1114 1115 memcpy(c->ivec, ((char *) deckey) + 8, 8); 1116 1117 text->cipher_dec_context = (cipher_context_t *) c; 1118 1119 return SASL_OK; 1120} 1121 1122static void free_des(context_t *text) 1123{ 1124 /* free des contextss. only cipher_enc_context needs to be free'd, 1125 since cipher_dec_context was allocated at the same time. */ 1126 if (text->cipher_enc_context) text->utils->free(text->cipher_enc_context); 1127} 1128 1129#endif /* WITH_DES */ 1130 1131#ifdef WITH_RC4 1132/* quick generic implementation of RC4 */ 1133struct rc4_context_s { 1134 unsigned char sbox[256]; 1135 int i, j; 1136}; 1137 1138typedef struct rc4_context_s rc4_context_t; 1139 1140static void rc4_init(rc4_context_t *text, 1141 const unsigned char *key, 1142 unsigned keylen) 1143{ 1144 int i, j; 1145 1146 /* fill in linearly s0=0 s1=1... */ 1147 for (i=0;i<256;i++) 1148 text->sbox[i]=i; 1149 1150 j=0; 1151 for (i = 0; i < 256; i++) { 1152 unsigned char tmp; 1153 /* j = (j + Si + Ki) mod 256 */ 1154 j = (j + text->sbox[i] + key[i % keylen]) % 256; 1155 1156 /* swap Si and Sj */ 1157 tmp = text->sbox[i]; 1158 text->sbox[i] = text->sbox[j]; 1159 text->sbox[j] = tmp; 1160 } 1161 1162 /* counters initialized to 0 */ 1163 text->i = 0; 1164 text->j = 0; 1165} 1166 1167static void rc4_encrypt(rc4_context_t *text, 1168 const char *input, 1169 char *output, 1170 unsigned len) 1171{ 1172 int tmp; 1173 int i = text->i; 1174 int j = text->j; 1175 int t; 1176 int K; 1177 const char *input_end = input + len; 1178 1179 while (input < input_end) { 1180 i = (i + 1) % 256; 1181 1182 j = (j + text->sbox[i]) % 256; 1183 1184 /* swap Si and Sj */ 1185 tmp = text->sbox[i]; 1186 text->sbox[i] = text->sbox[j]; 1187 text->sbox[j] = tmp; 1188 1189 t = (text->sbox[i] + text->sbox[j]) % 256; 1190 1191 K = text->sbox[t]; 1192 1193 /* byte K is Xor'ed with plaintext */ 1194 *output++ = *input++ ^ K; 1195 } 1196 1197 text->i = i; 1198 text->j = j; 1199} 1200 1201static void rc4_decrypt(rc4_context_t *text, 1202 const char *input, 1203 char *output, 1204 unsigned len) 1205{ 1206 int tmp; 1207 int i = text->i; 1208 int j = text->j; 1209 int t; 1210 int K; 1211 const char *input_end = input + len; 1212 1213 while (input < input_end) { 1214 i = (i + 1) % 256; 1215 1216 j = (j + text->sbox[i]) % 256; 1217 1218 /* swap Si and Sj */ 1219 tmp = text->sbox[i]; 1220 text->sbox[i] = text->sbox[j]; 1221 text->sbox[j] = tmp; 1222 1223 t = (text->sbox[i] + text->sbox[j]) % 256; 1224 1225 K = text->sbox[t]; 1226 1227 /* byte K is Xor'ed with plaintext */ 1228 *output++ = *input++ ^ K; 1229 } 1230 1231 text->i = i; 1232 text->j = j; 1233} 1234 1235static void free_rc4(context_t *text) 1236{ 1237 /* free rc4 context structures */ 1238 1239 if(text->cipher_enc_context) text->utils->free(text->cipher_enc_context); 1240 if(text->cipher_dec_context) text->utils->free(text->cipher_dec_context); 1241} 1242 1243static int init_rc4(context_t *text, 1244 unsigned char enckey[16], 1245 unsigned char deckey[16]) 1246{ 1247 /* allocate rc4 context structures */ 1248 text->cipher_enc_context= 1249 (cipher_context_t *) text->utils->malloc(sizeof(rc4_context_t)); 1250 if (text->cipher_enc_context == NULL) return SASL_NOMEM; 1251 1252 text->cipher_dec_context= 1253 (cipher_context_t *) text->utils->malloc(sizeof(rc4_context_t)); 1254 if (text->cipher_dec_context == NULL) return SASL_NOMEM; 1255 1256 /* initialize them */ 1257 rc4_init((rc4_context_t *) text->cipher_enc_context, 1258 (const unsigned char *) enckey, 16); 1259 rc4_init((rc4_context_t *) text->cipher_dec_context, 1260 (const unsigned char *) deckey, 16); 1261 1262 return SASL_OK; 1263} 1264 1265static int dec_rc4(context_t *text, 1266 const char *input, 1267 unsigned inputlen, 1268 unsigned char digest[16] __attribute__((unused)), 1269 char *output, 1270 unsigned *outputlen) 1271{ 1272 /* decrypt the text part & HMAC */ 1273 rc4_decrypt((rc4_context_t *) text->cipher_dec_context, 1274 input, output, inputlen); 1275 1276 /* no padding so we just subtract the HMAC to get the text length */ 1277 *outputlen = inputlen - 10; 1278 1279 return SASL_OK; 1280} 1281 1282static int enc_rc4(context_t *text, 1283 const char *input, 1284 unsigned inputlen, 1285 unsigned char digest[16], 1286 char *output, 1287 unsigned *outputlen) 1288{ 1289 /* pad is zero */ 1290 *outputlen = inputlen+10; 1291 1292 /* encrypt the text part */ 1293 rc4_encrypt((rc4_context_t *) text->cipher_enc_context, 1294 input, 1295 output, 1296 inputlen); 1297 1298 /* encrypt the HMAC part */ 1299 rc4_encrypt((rc4_context_t *) text->cipher_enc_context, 1300 (const char *) digest, 1301 (output)+inputlen, 10); 1302 1303 return SASL_OK; 1304} 1305 1306#endif /* WITH_RC4 */ 1307 1308struct digest_cipher available_ciphers[] = 1309{ 1310#ifdef WITH_RC4 1311 { "rc4-40", 40, 5, 0x01, &enc_rc4, &dec_rc4, &init_rc4, &free_rc4 }, 1312 { "rc4-56", 56, 7, 0x02, &enc_rc4, &dec_rc4, &init_rc4, &free_rc4 }, 1313 { "rc4", 128, 16, 0x04, &enc_rc4, &dec_rc4, &init_rc4, &free_rc4 }, 1314#endif 1315#ifdef WITH_DES 1316 { "des", 55, 16, 0x08, &enc_des, &dec_des, &init_des, &free_des }, 1317 { "3des", 112, 16, 0x10, &enc_3des, &dec_3des, &init_3des, &free_des }, 1318#endif 1319 { NULL, 0, 0, 0, NULL, NULL, NULL, NULL } 1320}; 1321 1322static int create_layer_keys(context_t *text, 1323 const sasl_utils_t *utils, 1324 HASH key, int keylen, 1325 unsigned char enckey[16], 1326 unsigned char deckey[16]) 1327{ 1328 MD5_CTX Md5Ctx; 1329 1330 utils->log(utils->conn, SASL_LOG_DEBUG, 1331 "DIGEST-MD5 create_layer_keys()"); 1332 1333 utils->MD5Init(&Md5Ctx); 1334 utils->MD5Update(&Md5Ctx, key, keylen); 1335 if (text->i_am == SERVER) { 1336 utils->MD5Update(&Md5Ctx, (const unsigned char *) SEALING_SERVER_CLIENT, 1337 (unsigned) strlen(SEALING_SERVER_CLIENT)); 1338 } else { 1339 utils->MD5Update(&Md5Ctx, (const unsigned char *) SEALING_CLIENT_SERVER, 1340 (unsigned) strlen(SEALING_CLIENT_SERVER)); 1341 } 1342 utils->MD5Final(enckey, &Md5Ctx); 1343 1344 utils->MD5Init(&Md5Ctx); 1345 utils->MD5Update(&Md5Ctx, key, keylen); 1346 if (text->i_am != SERVER) { 1347 utils->MD5Update(&Md5Ctx, (const unsigned char *) SEALING_SERVER_CLIENT, 1348 (unsigned) strlen(SEALING_SERVER_CLIENT)); 1349 } else { 1350 utils->MD5Update(&Md5Ctx, (const unsigned char *) SEALING_CLIENT_SERVER, 1351 (unsigned) strlen(SEALING_CLIENT_SERVER)); 1352 } 1353 utils->MD5Final(deckey, &Md5Ctx); 1354 1355 /* create integrity keys */ 1356 /* sending */ 1357 utils->MD5Init(&Md5Ctx); 1358 utils->MD5Update(&Md5Ctx, text->HA1, HASHLEN); 1359 if (text->i_am == SERVER) { 1360 utils->MD5Update(&Md5Ctx, (const unsigned char *)SIGNING_SERVER_CLIENT, 1361 (unsigned) strlen(SIGNING_SERVER_CLIENT)); 1362 } else { 1363 utils->MD5Update(&Md5Ctx, (const unsigned char *)SIGNING_CLIENT_SERVER, 1364 (unsigned) strlen(SIGNING_CLIENT_SERVER)); 1365 } 1366 utils->MD5Final(text->Ki_send, &Md5Ctx); 1367 1368 /* receiving */ 1369 utils->MD5Init(&Md5Ctx); 1370 utils->MD5Update(&Md5Ctx, text->HA1, HASHLEN); 1371 if (text->i_am != SERVER) { 1372 utils->MD5Update(&Md5Ctx, (const unsigned char *)SIGNING_SERVER_CLIENT, 1373 (unsigned) strlen(SIGNING_SERVER_CLIENT)); 1374 } else { 1375 utils->MD5Update(&Md5Ctx, (const unsigned char *)SIGNING_CLIENT_SERVER, 1376 (unsigned) strlen(SIGNING_CLIENT_SERVER)); 1377 } 1378 utils->MD5Final(text->Ki_receive, &Md5Ctx); 1379 1380 return SASL_OK; 1381} 1382 1383static const unsigned short version = 1; 1384 1385/* 1386 * privacy: 1387 * len, CIPHER(Kc, {msg, pag, HMAC(ki, {SeqNum, msg})[0..9]}), x0001, SeqNum 1388 * 1389 * integrity: 1390 * len, HMAC(ki, {SeqNum, msg})[0..9], x0001, SeqNum 1391 */ 1392static int digestmd5_encode(void *context, 1393 const struct iovec *invec, 1394 unsigned numiov, 1395 const char **output, 1396 unsigned *outputlen) 1397{ 1398 context_t *text = (context_t *) context; 1399 int tmp; 1400 unsigned int tmpnum; 1401 unsigned short int tmpshort; 1402 int ret; 1403 char *out; 1404 struct buffer_info *inblob, bufinfo; 1405 1406 if(!context || !invec || !numiov || !output || !outputlen) { 1407 PARAMERROR(text->utils); 1408 return SASL_BADPARAM; 1409 } 1410 1411 if (numiov > 1) { 1412 ret = _plug_iovec_to_buf(text->utils, invec, numiov, &text->enc_in_buf); 1413 if (ret != SASL_OK) return ret; 1414 inblob = text->enc_in_buf; 1415 } else { 1416 /* avoid the data copy */ 1417 bufinfo.data = invec[0].iov_base; 1418 bufinfo.curlen = invec[0].iov_len; 1419 inblob = &bufinfo; 1420 } 1421 1422 /* make sure the output buffer is big enough for this blob */ 1423 ret = _plug_buf_alloc(text->utils, &(text->encode_buf), 1424 &(text->encode_buf_len), 1425 (4 + /* for length */ 1426 inblob->curlen + /* for content */ 1427 10 + /* for MAC */ 1428 8 + /* maximum pad */ 1429 6)); /* for ver and seqnum */ 1430 if(ret != SASL_OK) return ret; 1431 1432 /* skip by the length for now */ 1433 out = (text->encode_buf)+4; 1434 1435 /* construct (seqnum, msg) 1436 * 1437 * Use the output buffer so that the message text is already in place 1438 * for an integrity-only layer. 1439 */ 1440 tmpnum = htonl(text->seqnum); 1441 memcpy(text->encode_buf, &tmpnum, 4); 1442 memcpy(text->encode_buf + 4, inblob->data, inblob->curlen); 1443 1444 if (text->cipher_enc) { 1445 unsigned char digest[16]; 1446 1447 /* HMAC(ki, (seqnum, msg) ) */ 1448 text->utils->hmac_md5((const unsigned char *) text->encode_buf, 1449 inblob->curlen + 4, 1450 text->Ki_send, HASHLEN, digest); 1451 1452 /* calculate the encrypted part */ 1453 text->cipher_enc(text, inblob->data, inblob->curlen, 1454 digest, out, outputlen); 1455 out+=(*outputlen); 1456 } 1457 else { 1458 /* HMAC(ki, (seqnum, msg) ) -- put directly into output buffer */ 1459 text->utils->hmac_md5((const unsigned char *) text->encode_buf, 1460 inblob->curlen + 4, 1461 text->Ki_send, HASHLEN, 1462 (unsigned char *) text->encode_buf + 1463 inblob->curlen + 4); 1464 1465 *outputlen = inblob->curlen + 10; /* for message + CMAC */ 1466 out+=inblob->curlen + 10; 1467 } 1468 1469 /* copy in version */ 1470 tmpshort = htons(version); 1471 memcpy(out, &tmpshort, 2); /* 2 bytes = version */ 1472 1473 out+=2; 1474 (*outputlen)+=2; /* for version */ 1475 1476 /* put in seqnum */ 1477 tmpnum = htonl(text->seqnum); 1478 memcpy(out, &tmpnum, 4); /* 4 bytes = seq # */ 1479 1480 (*outputlen)+=4; /* for seqnum */ 1481 1482 /* put the 1st 4 bytes in */ 1483 tmp=htonl(*outputlen); 1484 memcpy(text->encode_buf, &tmp, 4); 1485 1486 (*outputlen)+=4; 1487 1488 *output = text->encode_buf; 1489 text->seqnum++; 1490 1491 return SASL_OK; 1492} 1493 1494static int digestmd5_decode_packet(void *context, 1495 const char *input, 1496 unsigned inputlen, 1497 char **output, 1498 unsigned *outputlen) 1499{ 1500 context_t *text = (context_t *) context; 1501 int result; 1502 unsigned char *digest; 1503 int tmpnum; 1504 int lup; 1505 unsigned short ver; 1506 unsigned int seqnum; 1507 unsigned char checkdigest[16]; 1508 1509 if (inputlen < 16) { 1510 text->utils->seterror(text->utils->conn, 0, "DIGEST-MD5 SASL packets must be at least 16 bytes long"); 1511 return SASL_FAIL; 1512 } 1513 1514 /* check the version number */ 1515 memcpy(&ver, input+inputlen-6, 2); 1516 ver = ntohs(ver); 1517 if (ver != version) { 1518 text->utils->seterror(text->utils->conn, 0, "Wrong Version"); 1519 return SASL_FAIL; 1520 } 1521 1522 /* check the sequence number */ 1523 memcpy(&seqnum, input+inputlen-4, 4); 1524 seqnum = ntohl(seqnum); 1525 1526 if (seqnum != text->rec_seqnum) { 1527 text->utils->seterror(text->utils->conn, 0, 1528 "Incorrect Sequence Number: received %u, expected %u", 1529 seqnum, 1530 text->rec_seqnum); 1531 return SASL_FAIL; 1532 } 1533 1534 /* allocate a buffer large enough for the output */ 1535 result = _plug_buf_alloc(text->utils, &text->decode_packet_buf, 1536 &text->decode_packet_buf_len, 1537 inputlen /* length of message */ 1538 - 6 /* skip ver and seqnum */ 1539 + 4); /* prepend seqnum */ 1540 if (result != SASL_OK) return result; 1541 1542 /* construct (seqnum, msg) */ 1543 tmpnum = htonl(text->rec_seqnum); 1544 memcpy(text->decode_packet_buf, &tmpnum, 4); 1545 1546 text->rec_seqnum++; /* now increment it */ 1547 1548 *output = text->decode_packet_buf + 4; /* skip seqnum */ 1549 1550 if (text->cipher_dec) { 1551 /* decrypt message & HMAC into output buffer */ 1552 result = text->cipher_dec(text, input, inputlen-6, NULL, 1553 *output, outputlen); 1554 if (result != SASL_OK) return result; 1555 } 1556 else { 1557 /* copy message & HMAC into output buffer */ 1558 memcpy(*output, input, inputlen - 6); 1559 *outputlen = inputlen - 16; /* -16 to skip HMAC, ver and seqnum */ 1560 } 1561 digest = (unsigned char *) *output + (inputlen - 16); 1562 1563 /* check the CMAC */ 1564 1565 /* HMAC(ki, (seqnum, msg) ) */ 1566 text->utils->hmac_md5((const unsigned char *) text->decode_packet_buf, 1567 (*outputlen) + 4, 1568 text->Ki_receive, HASHLEN, checkdigest); 1569 1570 /* now check it */ 1571 // APPLE: secure constant-time comparison 1572 int mismatch = 0; 1573 for (lup = 0; lup < 10; lup++) 1574 mismatch |= checkdigest[lup] ^ digest[lup]; 1575 1576 if (mismatch) { 1577 text->utils->seterror(text->utils->conn, 0, 1578 "CMAC doesn't match!"); 1579 return SASL_FAIL; 1580 } 1581 1582 return SASL_OK; 1583} 1584 1585static int digestmd5_decode(void *context, 1586 const char *input, unsigned inputlen, 1587 const char **output, unsigned *outputlen) 1588{ 1589 context_t *text = (context_t *) context; 1590 int ret; 1591 1592 ret = _plug_decode(&text->decode_context, input, inputlen, 1593 &text->decode_buf, &text->decode_buf_len, outputlen, 1594 digestmd5_decode_packet, text); 1595 1596 *output = text->decode_buf; 1597 1598 return ret; 1599} 1600 1601static void digestmd5_common_mech_dispose(void *conn_context, 1602 const sasl_utils_t *utils) 1603{ 1604 context_t *text = (context_t *) conn_context; 1605 int lup; 1606 1607 if (!text || !utils) return; 1608 1609 utils->log(utils->conn, SASL_LOG_DEBUG, 1610 "DIGEST-MD5 common mech dispose"); 1611 1612 if (text->authid) utils->free(text->authid); 1613 if (text->realm) utils->free(text->realm); 1614 1615 if (text->realms) { 1616 /* need to free all the realms */ 1617 for (lup = 0; lup < text->realm_cnt; lup++) 1618 utils->free (text->realms[lup]); 1619 1620 utils->free(text->realms); 1621 } 1622 1623 if (text->nonce) utils->free(text->nonce); 1624 if (text->cnonce) utils->free(text->cnonce); 1625 1626 if (text->cipher_free) text->cipher_free(text); 1627 1628 /* free the stuff in the context */ 1629 if (text->response_value) utils->free(text->response_value); 1630 1631 _plug_decode_free(&text->decode_context); 1632 if (text->encode_buf) utils->free(text->encode_buf); 1633 if (text->decode_buf) utils->free(text->decode_buf); 1634 if (text->decode_packet_buf) utils->free(text->decode_packet_buf); 1635 if (text->out_buf) utils->free(text->out_buf); 1636 1637 if (text->enc_in_buf) { 1638 if (text->enc_in_buf->data) utils->free(text->enc_in_buf->data); 1639 utils->free(text->enc_in_buf); 1640 } 1641 1642 utils->free(conn_context); 1643} 1644 1645static void clear_reauth_entry(reauth_entry_t *reauth, enum Context_type type, 1646 const sasl_utils_t *utils) 1647{ 1648 if (!reauth) return; 1649 1650 if (reauth->authid) utils->free(reauth->authid); 1651 if (reauth->realm) utils->free(reauth->realm); 1652 if (reauth->nonce) utils->free(reauth->nonce); 1653 if (reauth->cnonce) utils->free(reauth->cnonce); 1654 1655 if (type == CLIENT) { 1656 if (reauth->u.c.serverFQDN) utils->free(reauth->u.c.serverFQDN); 1657 } 1658 1659 memset(reauth, 0, sizeof(reauth_entry_t)); 1660} 1661 1662static void digestmd5_common_mech_free(void *glob_context, 1663 const sasl_utils_t *utils) 1664{ 1665 digest_glob_context_t *my_glob_context = 1666 (digest_glob_context_t *) glob_context; 1667 reauth_cache_t *reauth_cache = my_glob_context->reauth; 1668 size_t n; 1669 1670 utils->log(utils->conn, SASL_LOG_DEBUG, 1671 "DIGEST-MD5 common mech free"); 1672 1673 /* Prevent anybody else from freeing this as well */ 1674 my_glob_context->reauth = NULL; 1675 1676 if (!reauth_cache) return; 1677 1678 for (n = 0; n < reauth_cache->size; n++) { 1679 clear_reauth_entry(&reauth_cache->e[n], reauth_cache->i_am, utils); 1680 } 1681 if (reauth_cache->e) utils->free(reauth_cache->e); 1682 1683 if (reauth_cache->mutex) { 1684 utils->mutex_free(reauth_cache->mutex); 1685 reauth_cache->mutex = NULL; 1686 } 1687 1688 utils->free(reauth_cache); 1689} 1690 1691/***************************** Server Section *****************************/ 1692 1693typedef struct server_context { 1694 context_t common; 1695 1696 time_t timestamp; 1697 int stale; /* last nonce is stale */ 1698 sasl_ssf_t limitssf, requiressf; /* application defined bounds */ 1699} server_context_t; 1700 1701static digest_glob_context_t server_glob_context; 1702 1703static void DigestCalcHA1FromSecret(context_t * text, 1704 const sasl_utils_t * utils, 1705 HASH HA1, 1706 unsigned char *authorization_id, 1707 unsigned char *pszNonce, 1708 unsigned char *pszCNonce, 1709 HASHHEX SessionKey) 1710{ 1711 MD5_CTX Md5Ctx; 1712 1713 /* calculate session key */ 1714 utils->MD5Init(&Md5Ctx); 1715 if (text->http_mode) { 1716 /* per RFC 2617 Errata ID 1649 */ 1717 HASHHEX HA1Hex; 1718 1719 CvtHex(HA1, HA1Hex); 1720 utils->MD5Update(&Md5Ctx, HA1Hex, HASHHEXLEN); 1721 } 1722 else { 1723 /* per RFC 2831 */ 1724 utils->MD5Update(&Md5Ctx, HA1, HASHLEN); 1725 } 1726 utils->MD5Update(&Md5Ctx, COLON, 1); 1727 utils->MD5Update(&Md5Ctx, pszNonce, (unsigned) strlen((char *) pszNonce)); 1728 utils->MD5Update(&Md5Ctx, COLON, 1); 1729 utils->MD5Update(&Md5Ctx, pszCNonce, (unsigned) strlen((char *) pszCNonce)); 1730 if (authorization_id != NULL) { 1731 utils->MD5Update(&Md5Ctx, COLON, 1); 1732 utils->MD5Update(&Md5Ctx, authorization_id, 1733 (unsigned) strlen((char *) authorization_id)); 1734 } 1735 utils->MD5Final(HA1, &Md5Ctx); 1736 1737 CvtHex(HA1, SessionKey); 1738 1739 1740 /* save HA1 because we need it to make the privacy and integrity keys */ 1741 memcpy(text->HA1, HA1, sizeof(HASH)); 1742} 1743 1744static char *create_response(context_t * text, 1745 const sasl_utils_t * utils, 1746 unsigned char *nonce, 1747 unsigned int ncvalue, 1748 unsigned char *cnonce, 1749 char *qop, 1750 const sasl_http_request_t *request, 1751 HASH Secret, 1752 char *authorization_id, 1753 char **response_value) 1754{ 1755 HASHHEX SessionKey; 1756 HASH EntityHash; 1757 HASHHEX HEntity; 1758 HASHHEX Response; 1759 char *result; 1760 1761 if (qop == NULL) qop = "auth"; 1762 1763 DigestCalcHA1FromSecret(text, 1764 utils, 1765 Secret, 1766 (unsigned char *) authorization_id, 1767 nonce, 1768 cnonce, 1769 SessionKey); 1770 1771 if (text->http_mode) { 1772 /* per RFC 2617 */ 1773 MD5_CTX Md5Ctx; 1774 1775 utils->MD5Init(&Md5Ctx); 1776 utils->MD5Update(&Md5Ctx, request->entity, request->elen); 1777 utils->MD5Final(EntityHash, &Md5Ctx); 1778 } 1779 else { 1780 /* per RFC 2831 */ 1781 memset(EntityHash, 0, HASHLEN); 1782 } 1783 CvtHex(EntityHash, HEntity); 1784 1785 /* Calculate response for comparison with client's response */ 1786 DigestCalcResponse(utils, 1787 SessionKey,/* HEX(H(A1)) */ 1788 nonce, /* nonce from server */ 1789 ncvalue, /* 8 hex digits */ 1790 cnonce, /* client nonce */ 1791 (unsigned char *) qop, /* qop-value: "", "auth", 1792 * "auth-int" */ 1793 (unsigned char *) request->uri, /* requested URL */ 1794 (unsigned char *) request->method, 1795 HEntity, /* H(entity body) if qop="auth-int" */ 1796 Response /* request-digest or response-digest */ 1797 ); 1798 1799 result = utils->malloc(HASHHEXLEN + 1); 1800 memcpy(result, Response, HASHHEXLEN); 1801 result[HASHHEXLEN] = 0; 1802 1803 /* Calculate response value for mutual auth with the client (NO Method) */ 1804 if (response_value != NULL) { 1805 char * new_response_value; 1806 1807 DigestCalcResponse(utils, 1808 SessionKey, /* HEX(H(A1)) */ 1809 nonce, /* nonce from server */ 1810 ncvalue, /* 8 hex digits */ 1811 cnonce, /* client nonce */ 1812 (unsigned char *) qop, /* qop-value: "", "auth", 1813 * "auth-int" */ 1814 (unsigned char *) request->uri, /* requested URL */ 1815 NULL, 1816 HEntity, /* H(entity body) if qop="auth-int" */ 1817 Response /* request-digest or response-digest */ 1818 ); 1819 1820 new_response_value = utils->realloc(*response_value, HASHHEXLEN + 1); 1821 if (new_response_value == NULL) { 1822 free (*response_value); 1823 *response_value = NULL; 1824 return NULL; 1825 } 1826 *response_value = new_response_value; 1827 1828 memcpy(*response_value, Response, HASHHEXLEN); 1829 (*response_value)[HASHHEXLEN] = 0; 1830 } 1831 return result; 1832} 1833 1834static int get_server_realm(sasl_server_params_t * params, char **realm) 1835{ 1836 /* look at user realm first */ 1837 if (params->user_realm != NULL) { 1838 if(params->user_realm[0] != '\0') { 1839 *realm = (char *) params->user_realm; 1840 } else { 1841 /* Catch improperly converted apps */ 1842 params->utils->seterror(params->utils->conn, 0, 1843 "user_realm is an empty string!"); 1844 return SASL_BADPARAM; 1845 } 1846 } else if (params->serverFQDN != NULL) { 1847 *realm = (char *) params->serverFQDN; 1848 } else { 1849 params->utils->seterror(params->utils->conn, 0, 1850 "no way to obtain domain"); 1851 return SASL_FAIL; 1852 } 1853 1854 return SASL_OK; 1855} 1856 1857/* 1858 * Convert hex string to int 1859 */ 1860static int htoi(unsigned char *hexin, unsigned int *res) 1861{ 1862 size_t lup, inlen; 1863 inlen = strlen((char *) hexin); 1864 1865 *res = 0; 1866 for (lup = 0; lup < inlen; lup++) { 1867 switch (hexin[lup]) { 1868 case '0': 1869 case '1': 1870 case '2': 1871 case '3': 1872 case '4': 1873 case '5': 1874 case '6': 1875 case '7': 1876 case '8': 1877 case '9': 1878 *res = (*res << 4) + (hexin[lup] - '0'); 1879 break; 1880 1881 case 'a': 1882 case 'b': 1883 case 'c': 1884 case 'd': 1885 case 'e': 1886 case 'f': 1887 *res = (*res << 4) + (hexin[lup] - 'a' + 10); 1888 break; 1889 1890 case 'A': 1891 case 'B': 1892 case 'C': 1893 case 'D': 1894 case 'E': 1895 case 'F': 1896 *res = (*res << 4) + (hexin[lup] - 'A' + 10); 1897 break; 1898 1899 default: 1900 return SASL_BADPARAM; 1901 } 1902 1903 } 1904 1905 return SASL_OK; 1906} 1907 1908static int digestmd5_server_mech_new(void *glob_context, 1909 sasl_server_params_t * sparams, 1910 const char *challenge __attribute__((unused)), 1911 unsigned challen __attribute__((unused)), 1912 void **conn_context) 1913{ 1914 context_t *text; 1915 1916 /* holds state are in -- allocate server size */ 1917 text = sparams->utils->malloc(sizeof(server_context_t)); 1918 if (text == NULL) 1919 return SASL_NOMEM; 1920 memset(text, 0, sizeof(server_context_t)); 1921 1922 text->state = 1; 1923 text->i_am = SERVER; 1924 text->http_mode = (sparams->flags & SASL_NEED_HTTP); 1925 text->reauth = ((digest_glob_context_t *) glob_context)->reauth; 1926 1927 *conn_context = text; 1928 return SASL_OK; 1929} 1930 1931static int 1932digestmd5_server_mech_step1(server_context_t *stext, 1933 sasl_server_params_t *sparams, 1934 const char *clientin __attribute__((unused)), 1935 unsigned clientinlen __attribute__((unused)), 1936 const char **serverout, 1937 unsigned *serveroutlen, 1938 sasl_out_params_t * oparams __attribute__((unused))) 1939{ 1940 context_t *text = (context_t *) stext; 1941 int result; 1942 char *realm; 1943 unsigned char *nonce; 1944 char *charset = "utf-8"; 1945 char qop[1024], cipheropts[1024]; 1946 struct digest_cipher *cipher; 1947 unsigned resplen; 1948 int added_conf = 0; 1949 char maxbufstr[64]; 1950 1951 sparams->utils->log(sparams->utils->conn, SASL_LOG_DEBUG, 1952 "DIGEST-MD5 server step 1"); 1953 1954 /* get realm */ 1955 result = get_server_realm(sparams, &realm); 1956 if(result != SASL_OK) return result; 1957 1958 /* what options should we offer the client? */ 1959 qop[0] = '\0'; 1960 cipheropts[0] = '\0'; 1961 if (stext->requiressf == 0) { 1962 if (*qop) strcat(qop, ","); 1963 strcat(qop, "auth"); 1964 } 1965 if (stext->requiressf <= 1 && stext->limitssf >= 1) { 1966 if (*qop) strcat(qop, ","); 1967 strcat(qop, "auth-int"); 1968 } 1969 1970 cipher = available_ciphers; 1971 while (cipher->name) { 1972 /* do we allow this particular cipher? */ 1973 if (stext->requiressf <= cipher->ssf && 1974 stext->limitssf >= cipher->ssf) { 1975 if (!added_conf) { 1976 if (*qop) strcat(qop, ","); 1977 strcat(qop, "auth-conf"); 1978 added_conf = 1; 1979 } 1980 if (*cipheropts) strcat(cipheropts, ","); 1981 strcat(cipheropts, cipher->name); 1982 } 1983 cipher++; 1984 } 1985 1986 if (*qop == '\0') { 1987 /* we didn't allow anything?!? we'll return SASL_TOOWEAK, since 1988 that's close enough */ 1989 return SASL_TOOWEAK; 1990 } 1991 1992 /* 1993 * digest-challenge = 1#( realm | nonce | qop-options | stale | maxbuf | 1994 * charset | cipher-opts | auth-param ) 1995 */ 1996 1997 nonce = create_nonce(sparams->utils); 1998 if (nonce == NULL) { 1999 SETERROR(sparams->utils, "internal erorr: failed creating a nonce"); 2000 return SASL_FAIL; 2001 } 2002 2003 resplen = 0; 2004 text->out_buf = NULL; 2005 text->out_buf_len = 0; 2006 if (add_to_challenge(sparams->utils, 2007 &text->out_buf, &text->out_buf_len, &resplen, 2008 "nonce", (unsigned char *) nonce, 2009 TRUE) != SASL_OK) { 2010 SETERROR(sparams->utils, "internal error: add_to_challenge failed"); 2011 return SASL_FAIL; 2012 } 2013 2014 /* add to challenge; if we chose not to specify a realm, we won't 2015 * send one to the client */ 2016 if (realm && add_to_challenge(sparams->utils, 2017 &text->out_buf, &text->out_buf_len, &resplen, 2018 "realm", (unsigned char *) realm, 2019 TRUE) != SASL_OK) { 2020 SETERROR(sparams->utils, "internal error: add_to_challenge failed"); 2021 return SASL_FAIL; 2022 } 2023 /* 2024 * qop-options A quoted string of one or more tokens indicating the 2025 * "quality of protection" values supported by the server. The value 2026 * "auth" indicates authentication; the value "auth-int" indicates 2027 * authentication with integrity protection; the value "auth-conf" 2028 * indicates authentication with integrity protection and encryption. 2029 */ 2030 2031 /* add qop to challenge */ 2032 if (add_to_challenge(sparams->utils, 2033 &text->out_buf, &text->out_buf_len, &resplen, 2034 "qop", 2035 (unsigned char *) qop, TRUE) != SASL_OK) { 2036 SETERROR(sparams->utils, "internal error: add_to_challenge 3 failed"); 2037 return SASL_FAIL; 2038 } 2039 2040 /* 2041 * Cipheropts - list of ciphers server supports 2042 */ 2043 /* add cipher-opts to challenge; only add if there are some */ 2044 if (strcmp(cipheropts,"")!=0) 2045 { 2046 if (add_to_challenge(sparams->utils, 2047 &text->out_buf, &text->out_buf_len, &resplen, 2048 "cipher", (unsigned char *) cipheropts, 2049 TRUE) != SASL_OK) { 2050 SETERROR(sparams->utils, 2051 "internal error: add_to_challenge 4 failed"); 2052 return SASL_FAIL; 2053 } 2054 } 2055 2056 /* "stale" is true if a reauth failed because of a nonce timeout */ 2057 if (stext->stale && 2058 add_to_challenge(sparams->utils, 2059 &text->out_buf, &text->out_buf_len, &resplen, 2060 "stale", (unsigned char *) "true", FALSE) != SASL_OK) { 2061 SETERROR(sparams->utils, "internal error: add_to_challenge failed"); 2062 return SASL_FAIL; 2063 } 2064 2065 /* 2066 * maxbuf A number indicating the size of the largest buffer the server 2067 * is able to receive when using "auth-int". If this directive is 2068 * missing, the default value is 65536. This directive may appear at most 2069 * once; if multiple instances are present, the client should abort the 2070 * authentication exchange. 2071 */ 2072 if(sparams->props.maxbufsize) { 2073 snprintf(maxbufstr, sizeof(maxbufstr), "%u", 2074 sparams->props.maxbufsize); 2075 if (add_to_challenge(sparams->utils, 2076 &text->out_buf, &text->out_buf_len, &resplen, 2077 "maxbuf", 2078 (unsigned char *) maxbufstr, FALSE) != SASL_OK) { 2079 SETERROR(sparams->utils, 2080 "internal error: add_to_challenge 5 failed"); 2081 return SASL_FAIL; 2082 } 2083 } 2084 2085 if (add_to_challenge(sparams->utils, 2086 &text->out_buf, &text->out_buf_len, &resplen, 2087 "charset", 2088 (unsigned char *) charset, FALSE) != SASL_OK) { 2089 SETERROR(sparams->utils, "internal error: add_to_challenge 6 failed"); 2090 return SASL_FAIL; 2091 } 2092 2093 2094 /* 2095 * algorithm 2096 * This directive is required for backwards compatibility with HTTP 2097 * Digest, which supports other algorithms. This directive is 2098 * required and MUST appear exactly once; if not present, or if multiple 2099 * instances are present, the client should abort the authentication 2100 * exchange. 2101 * 2102 * algorithm = "algorithm" "=" "md5-sess" 2103 */ 2104 2105 if (add_to_challenge(sparams->utils, 2106 &text->out_buf, &text->out_buf_len, &resplen, 2107 "algorithm", 2108 (unsigned char *) "md5-sess", FALSE)!=SASL_OK) { 2109 SETERROR(sparams->utils, "internal error: add_to_challenge 7 failed"); 2110 return SASL_FAIL; 2111 } 2112 2113 /* 2114 * The size of a digest-challenge MUST be less than 2048 bytes!!! 2115 */ 2116 if (*serveroutlen > 2048) { 2117 SETERROR(sparams->utils, 2118 "internal error: challenge larger than 2048 bytes"); 2119 return SASL_FAIL; 2120 } 2121 2122 text->authid = NULL; 2123 if (_plug_strdup(sparams->utils, realm, &text->realm, NULL) != SASL_OK) { 2124 SETERROR(sparams->utils, 2125 "internal error: out of memory when saving realm"); 2126 return SASL_FAIL; 2127 } 2128 2129 if (text->http_mode && 2130 sparams->utils->mutex_lock(text->reauth->mutex) == SASL_OK) { /* LOCK */ 2131 2132 /* Create an initial cache entry for non-persistent HTTP connections */ 2133 unsigned val = hash((char *) nonce) % text->reauth->size; 2134 2135 clear_reauth_entry(&text->reauth->e[val], SERVER, sparams->utils); 2136 text->reauth->e[val].authid = NULL; 2137 text->reauth->e[val].realm = text->realm; text->realm = NULL; 2138 text->reauth->e[val].nonce = nonce; 2139 text->reauth->e[val].nonce_count = 1; 2140 text->reauth->e[val].cnonce = NULL; 2141 text->reauth->e[val].u.s.timestamp = time(0); 2142 2143 sparams->utils->mutex_unlock(text->reauth->mutex); /* UNLOCK */ 2144 } 2145 else { 2146 text->nonce = nonce; 2147 text->nonce_count = 1; 2148 text->cnonce = NULL; 2149 stext->timestamp = time(0); 2150 } 2151 2152 *serveroutlen = (unsigned) strlen(text->out_buf); 2153 *serverout = text->out_buf; 2154 2155 text->state = 2; 2156 2157 return SASL_CONTINUE; 2158} 2159 2160static int digestmd5_server_mech_step2(server_context_t *stext, 2161 sasl_server_params_t *sparams, 2162 const char *clientin, 2163 unsigned clientinlen, 2164 const char **serverout, 2165 unsigned *serveroutlen, 2166 sasl_out_params_t * oparams) 2167{ 2168 context_t *text = (context_t *) stext; 2169 /* verify digest */ 2170 sasl_secret_t *sec = NULL; 2171 int result; 2172 char *serverresponse = NULL; 2173 char *username = NULL; 2174 char *authorization_id = NULL; 2175 char *realm = NULL; 2176 unsigned char *nonce = NULL, *cnonce = NULL; 2177 unsigned int noncecount = 0; 2178 char *qop = NULL; 2179 char *digesturi = NULL; 2180 sasl_http_request_t rfc2831_request; 2181 const sasl_http_request_t *request; 2182 char *response = NULL; 2183 2184 /* setting the default value (65536) */ 2185 unsigned long client_maxbuf = 65536; 2186 int maxbuf_count = 0; /* How many maxbuf instances was found */ 2187 2188 char *charset = NULL; 2189 char *cipher = NULL; 2190 unsigned int n = 0; 2191 2192 HASH Secret; 2193 HASH SecretBogus; 2194 bool Try_8859_1 = FALSE; 2195 int client_ignores_realm = 0; 2196 char *full_username = NULL; 2197 char *internal_username = NULL; 2198 int canon_flags; 2199 2200 /* password prop_request */ 2201 const char *password_request[] = { SASL_AUX_PASSWORD, 2202 "*cmusaslsecretDIGEST-MD5", 2203 NULL }; 2204 size_t len; 2205 struct propval auxprop_values[2]; 2206 2207 /* can we mess with clientin? copy it to be safe */ 2208 char *in_start = NULL; 2209 char *in = NULL; 2210 cipher_free_t *old_cipher_free = NULL; 2211 2212 sparams->utils->log(sparams->utils->conn, SASL_LOG_DEBUG, 2213 "DIGEST-MD5 server step 2"); 2214 2215 if (clientinlen == 0) { 2216 SETERROR(sparams->utils, "input expected in DIGEST-MD5, step 2"); 2217 result = SASL_BADAUTH; 2218 goto FreeAllMem; 2219 } 2220 2221 if (text->http_mode) { 2222 /* per RFC 2617 (HTTP Request as set by calling application) */ 2223 request = sparams->http_request; 2224 if (!request) { 2225 SETERROR(sparams->utils, 2226 "missing HTTP request in DIGEST-MD5, step 2"); 2227 result = SASL_BADPARAM; 2228 goto FreeAllMem; 2229 } 2230 } 2231 else { 2232 /* per RFC 2831 */ 2233 rfc2831_request.method = "AUTHENTICATE"; 2234 rfc2831_request.uri = NULL; /* to be filled in below from response */ 2235 rfc2831_request.entity = NULL; 2236 rfc2831_request.elen = 0; 2237 rfc2831_request.non_persist = 0; 2238 request = &rfc2831_request; 2239 } 2240 2241 in = sparams->utils->malloc(clientinlen + 1); 2242 2243 memcpy(in, clientin, clientinlen); 2244 in[clientinlen] = 0; 2245 2246 in_start = in; 2247 2248 2249 /* parse what we got */ 2250 while (in[0] != '\0') { 2251 char *name = NULL, *value = NULL; 2252 get_pair(&in, &name, &value); 2253 2254 if (name == NULL) { 2255 SETERROR(sparams->utils, 2256 "Parse error"); 2257 result = SASL_BADAUTH; 2258 goto FreeAllMem; 2259 } 2260 2261 if (*name == '\0') { 2262 break; 2263 } 2264 2265 /* Extracting parameters */ 2266 2267 /* 2268 * digest-response = 1#( username | realm | nonce | cnonce | 2269 * nonce-count | qop | digest-uri | response | maxbuf | charset | 2270 * cipher | auth-param ) 2271 */ 2272 2273 if (strcasecmp(name, "username") == 0) { 2274 _plug_strdup(sparams->utils, value, &username, NULL); 2275 } else if (strcasecmp(name, "authzid") == 0) { 2276 _plug_strdup(sparams->utils, value, &authorization_id, NULL); 2277 } else if (strcasecmp(name, "cnonce") == 0) { 2278 _plug_strdup(sparams->utils, value, (char **) &cnonce, NULL); 2279 } else if (strcasecmp(name, "nc") == 0) { 2280 if (htoi((unsigned char *) value, &noncecount) != SASL_OK) { 2281 SETERROR(sparams->utils, 2282 "error converting hex to int"); 2283 result = SASL_BADAUTH; 2284 goto FreeAllMem; 2285 } 2286 } else if (strcasecmp(name, "realm") == 0) { 2287 if (realm) { 2288 SETERROR(sparams->utils, 2289 "duplicate realm: authentication aborted"); 2290 result = SASL_FAIL; 2291 goto FreeAllMem; 2292 } 2293 _plug_strdup(sparams->utils, value, &realm, NULL); 2294 } else if (strcasecmp(name, "nonce") == 0) { 2295 _plug_strdup(sparams->utils, value, (char **) &nonce, NULL); 2296 } else if (strcasecmp(name, "qop") == 0) { 2297 if (qop) { 2298 SETERROR(sparams->utils, 2299 "duplicate qop: authentication aborted"); 2300 result = SASL_FAIL; 2301 goto FreeAllMem; 2302 } 2303 _plug_strdup(sparams->utils, value, &qop, NULL); 2304 } else if (strcasecmp(name, "digest-uri") == 0 || /* per RFC 2831 */ 2305 (text->http_mode && 2306 strcasecmp(name, "uri") == 0)) { /* per RFC 2617 */ 2307 size_t service_len; 2308 2309 if (digesturi) { 2310 SETERROR(sparams->utils, 2311 "duplicate digest-uri: authentication aborted"); 2312 result = SASL_FAIL; 2313 goto FreeAllMem; 2314 } 2315 2316 _plug_strdup(sparams->utils, value, &digesturi, NULL); 2317 2318 if (text->http_mode && request && request->uri) { 2319 /* Verify digest-uri matches HTTP request (per RFC 2617) */ 2320 if (strcmp(digesturi, request->uri)) { 2321 result = SASL_BADAUTH; 2322 SETERROR(sparams->utils, 2323 "bad digest-uri: doesn't match HTTP request"); 2324 goto FreeAllMem; 2325 } 2326 } 2327 else { 2328 /* Verify digest-uri format (per RFC 2831): 2329 * 2330 * digest-uri-value = serv-type "/" host [ "/" serv-name ] 2331 */ 2332 2333 /* make sure it's the service that we're expecting */ 2334 service_len = strlen(sparams->service); 2335 if (strncasecmp(digesturi, sparams->service, service_len) || 2336 digesturi[service_len] != '/') { 2337 result = SASL_BADAUTH; 2338 SETERROR(sparams->utils, 2339 "bad digest-uri: doesn't match service"); 2340 goto FreeAllMem; 2341 } 2342 2343 /* xxx we don't verify the hostname component */ 2344 2345 rfc2831_request.uri = digesturi; 2346 } 2347 2348 } else if (strcasecmp(name, "response") == 0) { 2349 _plug_strdup(sparams->utils, value, &response, NULL); 2350 } else if (strcasecmp(name, "cipher") == 0) { 2351 _plug_strdup(sparams->utils, value, &cipher, NULL); 2352 } else if (strcasecmp(name, "maxbuf") == 0) { 2353 maxbuf_count++; 2354 if (maxbuf_count != 1) { 2355 result = SASL_BADAUTH; 2356 SETERROR(sparams->utils, 2357 "duplicate maxbuf: authentication aborted"); 2358 goto FreeAllMem; 2359 } else if (str2ul32 (value, &client_maxbuf) == FALSE) { 2360 result = SASL_BADAUTH; 2361 SETERROR(sparams->utils, "invalid maxbuf parameter"); 2362 goto FreeAllMem; 2363 } else { 2364 if (client_maxbuf <= 16) { 2365 result = SASL_BADAUTH; 2366 SETERROR(sparams->utils, 2367 "maxbuf parameter too small"); 2368 goto FreeAllMem; 2369 } 2370 2371 if (client_maxbuf > MAX_SASL_BUFSIZE) { 2372 result = SASL_BADAUTH; 2373 SETERROR(sparams->utils, 2374 "maxbuf parameter too big"); 2375 goto FreeAllMem; 2376 } 2377 } 2378 } else if (strcasecmp(name, "charset") == 0) { 2379 if (strcasecmp(value, "utf-8") != 0) { 2380 SETERROR(sparams->utils, "client doesn't support UTF-8"); 2381 result = SASL_FAIL; 2382 goto FreeAllMem; 2383 } 2384 _plug_strdup(sparams->utils, value, &charset, NULL); 2385 } else if (strcasecmp(name,"algorithm") == 0) { 2386 /* per RFC 2831: algorithm MUST be ignored if received */ 2387 if (text->http_mode && strcasecmp(value, "md5-sess") != 0) { 2388 /* per RFC 2617: algorithm MUST match that sent in challenge */ 2389 SETERROR(sparams->utils, "'algorithm' isn't 'md5-sess'"); 2390 result = SASL_FAIL; 2391 goto FreeAllMem; 2392 } 2393 } else { 2394 sparams->utils->log(sparams->utils->conn, SASL_LOG_DEBUG, 2395 "DIGEST-MD5 unrecognized pair %s/%s: ignoring", 2396 name, value); 2397 } 2398 } 2399 2400 /* 2401 * username = "username" "=" <"> username-value <"> 2402 * username-value = qdstr-val 2403 * cnonce = "cnonce" "=" <"> cnonce-value <"> 2404 * cnonce-value = qdstr-val 2405 * nonce-count = "nc" "=" nc-value 2406 * nc-value = 8LHEX 2407 * qop = "qop" "=" qop-value 2408 * digest-uri = "digest-uri" "=" digest-uri-value 2409 * digest-uri-value = serv-type "/" host [ "/" serv-name ] 2410 * serv-type = 1*ALPHA 2411 * host = 1*( ALPHA | DIGIT | "-" | "." ) 2412 * service = host 2413 * response = "response" "=" <"> response-value <"> 2414 * response-value = 32LHEX 2415 * LHEX = "0" | "1" | "2" | "3" | "4" | "5" | 2416 * "6" | "7" | "8" | "9" | "a" | "b" | "c" | "d" | "e" | "f" 2417 * cipher = "cipher" "=" cipher-value 2418 */ 2419 /* Verifing that all required parameters were received */ 2420 /* APPLE: remove all extraneous parentheses around equality comparisons */ 2421 if (username == NULL) { 2422 SETERROR(sparams->utils, "required parameters missing: username"); 2423 result = SASL_BADAUTH; 2424 goto FreeAllMem; 2425 } 2426 if (nonce == NULL) { 2427 SETERROR(sparams->utils, "required parameters missing: nonce"); 2428 result = SASL_BADAUTH; 2429 goto FreeAllMem; 2430 } 2431 if (noncecount == 0) { 2432 SETERROR(sparams->utils, "required parameters missing: noncecount"); 2433 result = SASL_BADAUTH; 2434 goto FreeAllMem; 2435 } 2436 if (cnonce == NULL) { 2437 SETERROR(sparams->utils, "required parameters missing: cnonce"); 2438 result = SASL_BADAUTH; 2439 goto FreeAllMem; 2440 } 2441 if (digesturi == NULL) { 2442 SETERROR(sparams->utils, "required parameters missing: digesturi"); 2443 result = SASL_BADAUTH; 2444 goto FreeAllMem; 2445 } 2446 if (response == NULL) { 2447 SETERROR(sparams->utils, "required parameters missing: response"); 2448 result = SASL_BADAUTH; 2449 goto FreeAllMem; 2450 } 2451 2452 if (realm == NULL) { 2453 /* From 2831bis: 2454 If the directive is missing, "realm-value" will set to 2455 the empty string when computing A1. */ 2456 _plug_strdup(sparams->utils, "", &realm, NULL); 2457 sparams->utils->log(sparams->utils->conn, SASL_LOG_DEBUG, 2458 "The client didn't send a realm, assuming empty string."); 2459#if 0 2460 if (text->realm[0] != '\0') { 2461 SETERROR(sparams->utils, 2462 "realm changed: authentication aborted"); 2463 result = SASL_BADAUTH; 2464 goto FreeAllMem; 2465 } 2466#endif 2467 } 2468 2469 if (!text->nonce) { 2470 unsigned val = hash((char *) nonce) % text->reauth->size; 2471 2472 /* reauth attempt or continuation of HTTP Digest on a 2473 non-persistent connection, see if we have any info for this nonce */ 2474 if (sparams->utils->mutex_lock(text->reauth->mutex) == SASL_OK) { /* LOCK */ 2475 if (text->reauth->e[val].realm && 2476 !strcmp(realm, text->reauth->e[val].realm) && 2477 ((text->reauth->e[val].nonce_count == 1) || 2478 (text->reauth->e[val].authid && 2479 !strcmp(username, text->reauth->e[val].authid)))) { 2480 2481 _plug_strdup(sparams->utils, text->reauth->e[val].realm, 2482 &text->realm, NULL); 2483 _plug_strdup(sparams->utils, (char *) text->reauth->e[val].nonce, 2484 (char **) &text->nonce, NULL); 2485 text->nonce_count = text->reauth->e[val].nonce_count; 2486#if 0 /* XXX Neither RFC 2617 nor RFC 2831 state that the cnonce 2487 needs to remain constant for subsequent authentication to work */ 2488 _plug_strdup(sparams->utils, (char *) text->reauth->e[val].cnonce, 2489 (char **) &text->cnonce, NULL); 2490#endif 2491 stext->timestamp = text->reauth->e[val].u.s.timestamp; 2492 } 2493 sparams->utils->mutex_unlock(text->reauth->mutex); /* UNLOCK */ 2494 } 2495 2496 if (!text->nonce) { 2497 /* we don't have any reauth info */ 2498 sparams->utils->log(sparams->utils->conn, SASL_LOG_DEBUG, 2499 "No reauth info for '%s' found", nonce); 2500 2501 /* we will continue processing the response to determine 2502 if the client knows the password and return stale accordingly */ 2503 } 2504 } 2505 2506 /* Sanity check the parameters */ 2507 if (text->nonce) { 2508 /* CLAIM: realm is not NULL below */ 2509 if (text->realm == NULL) { 2510 sparams->utils->log(sparams->utils->conn, SASL_LOG_DEBUG, 2511 "The client specifies a realm when the server hasn't provided one. Using client's realm."); 2512 _plug_strdup(sparams->utils, realm, &text->realm, NULL); 2513 } else if ((strcmp(realm, text->realm) != 0) && 2514 /* XXX - Not sure why the check for text->realm not being empty is needed, 2515 as it should always be non-empty */ 2516 (text->realm[0] != 0)) { 2517 2518 client_ignores_realm = 1; 2519 sparams->utils->log(sparams->utils->conn, SASL_LOG_DEBUG, 2520 "The client tries to override server provided realm"); 2521 if (text->realm) sparams->utils->free(text->realm); 2522 _plug_strdup(sparams->utils, realm, &text->realm, NULL); 2523 } 2524 2525 if (strcmp((char *) nonce, (char *) text->nonce) != 0) { 2526 SETERROR(sparams->utils, 2527 "nonce changed: authentication aborted"); 2528 result = SASL_BADAUTH; 2529 goto FreeAllMem; 2530 } 2531#if 0 /* XXX Possible replay attack, but we will continue processing 2532 * the response to determine if the client knows the password and 2533 return stale accordingly */ 2534 if (noncecount != text->nonce_count) { 2535 SETERROR(sparams->utils, 2536 "incorrect nonce-count: authentication aborted"); 2537 result = SASL_BADAUTH; 2538 goto FreeAllMem; 2539 } 2540#endif 2541#if 0 /* XXX Neither RFC 2617 nor RFC 2831 state that the cnonce 2542 needs to remain constant for subsequent authentication to work */ 2543 if (text->cnonce && strcmp((char *) cnonce, (char *) text->cnonce) != 0) { 2544 SETERROR(sparams->utils, 2545 "cnonce changed: authentication aborted"); 2546 result = SASL_BADAUTH; 2547 goto FreeAllMem; 2548 } 2549#endif 2550 } 2551 2552 result = sparams->utils->prop_request(sparams->propctx, password_request); 2553 if(result != SASL_OK) { 2554 SETERROR(sparams->utils, "unable to obtain user password"); 2555 goto FreeAllMem; 2556 } 2557 2558 /* this will trigger the getting of the aux properties */ 2559 /* Note that if we don't have an authorization id, we don't use it... */ 2560 2561 if (client_ignores_realm) { 2562 if (strlen(text->realm) == 0) { 2563 /* Don't put @ at the end of the username, if the realm is empty */ 2564 _plug_strdup(sparams->utils, username, &full_username, NULL); 2565 } else { 2566 full_username = (char *) sparams->utils->malloc(strlen(username) + 2567 strlen(text->realm) + 2); 2568 full_username[0] = '\0'; 2569 sprintf (full_username, "%s@%s", username, text->realm); 2570 } 2571 internal_username = full_username; 2572 } else { 2573 internal_username = username; 2574 } 2575 2576 canon_flags = SASL_CU_AUTHID; 2577 if (!authorization_id || !*authorization_id) { 2578 canon_flags |= SASL_CU_AUTHZID; 2579 } 2580 2581 result = sparams->canon_user(sparams->utils->conn, 2582 internal_username, 2583 0, 2584 canon_flags, 2585 oparams); 2586 if (result != SASL_OK) { 2587 SETERROR(sparams->utils, "unable to canonify user and get auxprops"); 2588 goto FreeAllMem; 2589 } 2590 2591 if (authorization_id != NULL && *authorization_id != '\0') { 2592 result = sparams->canon_user(sparams->utils->conn, 2593 authorization_id, 0, SASL_CU_AUTHZID, 2594 oparams); 2595 } 2596 if (result != SASL_OK) { 2597 SETERROR(sparams->utils, "unable to canonify authorization ID"); 2598 goto FreeAllMem; 2599 } 2600 2601 result = sparams->utils->prop_getnames(sparams->propctx, password_request, 2602 auxprop_values); 2603 if (result < 0 || 2604 ((!auxprop_values[0].name || !auxprop_values[0].values) && 2605 (!auxprop_values[1].name || !auxprop_values[1].values))) { 2606 /* We didn't find this username */ 2607 sparams->utils->seterror(sparams->utils->conn, 0, 2608 "no secret in database"); 2609 result = sparams->transition ? SASL_TRANS : SASL_NOUSER; 2610 goto FreeAllMem; 2611 } 2612 2613 if (auxprop_values[0].name && auxprop_values[0].values) { 2614 len = strlen(auxprop_values[0].values[0]); 2615 if (len == 0) { 2616 sparams->utils->seterror(sparams->utils->conn,0, 2617 "empty secret"); 2618 result = SASL_FAIL; 2619 goto FreeAllMem; 2620 } 2621 2622 sec = sparams->utils->malloc(sizeof(sasl_secret_t) + len); 2623 if (!sec) { 2624 SETERROR(sparams->utils, "unable to allocate secret"); 2625 result = SASL_FAIL; 2626 goto FreeAllMem; 2627 } 2628 2629 sec->len = (unsigned) len; 2630 strncpy((char *) sec->data, auxprop_values[0].values[0], len + 1); 2631 2632 /* 2633 * Verifying response obtained from client 2634 * 2635 * H_URP = H({ username-value,":",realm-value,":",passwd}) sec->data 2636 * contains H_URP 2637 */ 2638 2639 /* Calculate the secret from the plaintext password */ 2640 { 2641 /* 2642 * Secret = { H( { username-value, ":", realm-value, ":", passwd } ) } 2643 * 2644 * (used to build A1) 2645 */ 2646 2647 Try_8859_1 = DigestCalcSecret(sparams->utils, 2648 (unsigned char *) username, 2649 (unsigned char *) realm, 2650 sec->data, 2651 sec->len, 2652 FALSE, 2653 Secret); 2654 Secret[HASHLEN] = '\0'; 2655 } 2656 if (Try_8859_1) { 2657 /* 2658 * Secret = { H( { username-value, ":", realm-value, ":", passwd } ) } 2659 * 2660 * (used to build A1) 2661 */ 2662 2663 DigestCalcSecret(sparams->utils, 2664 (unsigned char *) username, 2665 (unsigned char *) realm, 2666 sec->data, 2667 sec->len, 2668 TRUE, 2669 SecretBogus); 2670 SecretBogus[HASHLEN] = '\0'; 2671 } 2672 2673 /* We're done with sec now. Let's get rid of it */ 2674 _plug_free_secret(sparams->utils, &sec); 2675 } else if (auxprop_values[1].name && auxprop_values[1].values) { 2676 /* NB: This will most likely fail for clients that 2677 choose to ignore server-advertised realm */ 2678 memcpy(Secret, auxprop_values[1].values[0], HASHLEN); 2679 Secret[HASHLEN] = '\0'; 2680 } else { 2681 sparams->utils->seterror(sparams->utils->conn, 0, 2682 "Have neither type of secret"); 2683 return SASL_FAIL; 2684 } 2685 2686 /* erase the plaintext password */ 2687 sparams->utils->prop_erase(sparams->propctx, password_request[0]); 2688 2689 /* defaulting qop to "auth" if not specified */ 2690 if (qop == NULL) { 2691 _plug_strdup(sparams->utils, "auth", &qop, NULL); 2692 } 2693 2694 if (oparams->mech_ssf > 1) { 2695 /* Remember the old cipher free function (if any). 2696 It will be called later, once we are absolutely 2697 sure that authentication was successful. */ 2698 old_cipher_free = text->cipher_free; 2699 /* free the old cipher context first */ 2700 } 2701 2702 /* check which layer/cipher to use */ 2703 if ((!strcasecmp(qop, "auth-conf")) && (cipher != NULL)) { 2704 /* see what cipher was requested */ 2705 struct digest_cipher *cptr; 2706 2707 cptr = available_ciphers; 2708 while (cptr->name) { 2709 /* find the cipher requested & make sure it's one we're happy 2710 with by policy */ 2711 if (!strcasecmp(cipher, cptr->name) && 2712 stext->requiressf <= cptr->ssf && 2713 stext->limitssf >= cptr->ssf) { 2714 /* found it! */ 2715 break; 2716 } 2717 cptr++; 2718 } 2719 2720 if (cptr->name) { 2721 text->cipher_enc = cptr->cipher_enc; 2722 text->cipher_dec = cptr->cipher_dec; 2723 text->cipher_init = cptr->cipher_init; 2724 text->cipher_free = cptr->cipher_free; 2725 oparams->mech_ssf = cptr->ssf; 2726 n = cptr->n; 2727 } else { 2728 /* erg? client requested something we didn't advertise! */ 2729 sparams->utils->log(sparams->utils->conn, SASL_LOG_WARN, 2730 "protocol violation: client requested invalid cipher"); 2731 SETERROR(sparams->utils, "client requested invalid cipher"); 2732 /* Mark that we attempted security layer negotiation */ 2733 oparams->mech_ssf = 2; 2734 result = SASL_FAIL; 2735 goto FreeAllMem; 2736 } 2737 2738 oparams->encode=&digestmd5_encode; 2739 oparams->decode=&digestmd5_decode; 2740 } else if (!strcasecmp(qop, "auth-int") && 2741 stext->requiressf <= 1 && stext->limitssf >= 1) { 2742 oparams->encode = &digestmd5_encode; 2743 oparams->decode = &digestmd5_decode; 2744 oparams->mech_ssf = 1; 2745 } else if (!strcasecmp(qop, "auth") && stext->requiressf == 0) { 2746 oparams->encode = NULL; 2747 oparams->decode = NULL; 2748 oparams->mech_ssf = 0; 2749 } else { 2750 SETERROR(sparams->utils, 2751 "protocol violation: client requested invalid qop"); 2752 result = SASL_FAIL; 2753 goto FreeAllMem; 2754 } 2755 2756 serverresponse = create_response(text, 2757 sparams->utils, 2758 nonce, 2759 noncecount, 2760 cnonce, 2761 qop, 2762 request, 2763 Secret, 2764 authorization_id, 2765 &text->response_value); 2766 2767 if (serverresponse == NULL) { 2768 SETERROR(sparams->utils, "internal error: unable to create response"); 2769 result = SASL_NOMEM; 2770 goto FreeAllMem; 2771 } 2772 2773 /* if ok verified */ 2774 if (strcmp(serverresponse, response) != 0) { 2775 if (Try_8859_1) { 2776 2777 serverresponse = create_response(text, 2778 sparams->utils, 2779 nonce, 2780 noncecount, 2781 cnonce, 2782 qop, 2783 request, 2784 SecretBogus, 2785 authorization_id, 2786 &text->response_value); 2787 2788 if (serverresponse == NULL) { 2789 SETERROR(sparams->utils, "internal error: unable to create response"); 2790 result = SASL_NOMEM; 2791 goto FreeAllMem; 2792 } 2793 2794 /* if ok verified */ 2795 if (strcmp(serverresponse, response) != 0) { 2796 SETERROR(sparams->utils, 2797 "client response doesn't match what we generated (tried bogus)"); 2798 result = SASL_BADAUTH; 2799 2800 goto FreeAllMem; 2801 } 2802 2803 } else { 2804 SETERROR(sparams->utils, 2805 "client response doesn't match what we generated"); 2806 result = SASL_BADAUTH; 2807 2808 goto FreeAllMem; 2809 } 2810 } 2811 2812 /* see if our nonce expired */ 2813 if (!text->nonce || 2814 (noncecount != text->nonce_count) || 2815 (text->reauth->timeout && 2816 time(0) - stext->timestamp > text->reauth->timeout)) { 2817 if (!text->nonce) SETERROR(sparams->utils, "no cached server nonce"); 2818 else if (noncecount != text->nonce_count) 2819 SETERROR(sparams->utils, "incorrect nonce-count"); 2820 else SETERROR(sparams->utils, "server nonce expired"); 2821 stext->stale = 1; 2822 result = SASL_BADAUTH; 2823 2824 goto FreeAllMem; 2825 } 2826 2827 /* 2828 * nothing more to do; authenticated set oparams information 2829 */ 2830 oparams->doneflag = 1; 2831 oparams->maxoutbuf = client_maxbuf - 4; 2832 if (oparams->mech_ssf > 1) { 2833 /* MAC block (privacy) */ 2834 oparams->maxoutbuf -= 25; 2835 } else if(oparams->mech_ssf == 1) { 2836 /* MAC block (integrity) */ 2837 oparams->maxoutbuf -= 16; 2838 } 2839 2840 oparams->param_version = 0; 2841 2842 text->seqnum = 0; /* for integrity/privacy */ 2843 text->rec_seqnum = 0; /* for integrity/privacy */ 2844 text->utils = sparams->utils; 2845 2846 /* Free the old security layer, if any */ 2847 if (old_cipher_free) old_cipher_free(text); 2848 2849 /* used by layers */ 2850 _plug_decode_init(&text->decode_context, text->utils, 2851 sparams->props.maxbufsize ? sparams->props.maxbufsize : 2852 DEFAULT_BUFSIZE); 2853 2854 if (oparams->mech_ssf > 0) { 2855 unsigned char enckey[16]; 2856 unsigned char deckey[16]; 2857 2858 create_layer_keys(text, sparams->utils,text->HA1,n,enckey,deckey); 2859 2860 /* initialize cipher if need be */ 2861 if (text->cipher_init) { 2862 if (text->cipher_init(text, enckey, deckey) != SASL_OK) { 2863 sparams->utils->seterror(sparams->utils->conn, 0, 2864 "couldn't init cipher"); 2865 } 2866 } 2867 } 2868 2869 /* 2870 * The server receives and validates the "digest-response". The server 2871 * checks that the nonce-count is "00000001". If it supports subsequent 2872 * authentication, it saves the value of the nonce and the nonce-count. 2873 */ 2874 2875 /* 2876 * The "username-value", "realm-value" and "passwd" are encoded according 2877 * to the value of the "charset" directive. If "charset=UTF-8" is 2878 * present, and all the characters of either "username-value" or "passwd" 2879 * are in the ISO 8859-1 character set, then it must be converted to 2880 * UTF-8 before being hashed. A sample implementation of this conversion 2881 * is in section 8. 2882 */ 2883 2884 /* add to challenge */ 2885 { 2886 unsigned resplen = 0; 2887 2888 if (add_to_challenge(sparams->utils, 2889 &text->out_buf, &text->out_buf_len, &resplen, 2890 "rspauth", (unsigned char *) text->response_value, 2891 text->http_mode ? TRUE : FALSE) != SASL_OK) { 2892 SETERROR(sparams->utils, "internal error: add_to_challenge failed"); 2893 result = SASL_FAIL; 2894 goto FreeAllMem; 2895 } 2896 2897 if (text->http_mode) { 2898 /* per RFC 2617 */ 2899 char ncvalue[10]; 2900 2901 if (add_to_challenge(sparams->utils, 2902 &text->out_buf, &text->out_buf_len, &resplen, 2903 "cnonce", cnonce, TRUE) != SASL_OK) { 2904 result = SASL_FAIL; 2905 goto FreeAllMem; 2906 } 2907 snprintf(ncvalue, sizeof(ncvalue), "%08x", text->nonce_count); 2908 if (add_to_challenge(sparams->utils, 2909 &text->out_buf, &text->out_buf_len, &resplen, 2910 "nc", (unsigned char *) ncvalue, FALSE) != SASL_OK) { 2911 result = SASL_FAIL; 2912 goto FreeAllMem; 2913 } 2914 if (add_to_challenge(sparams->utils, 2915 &text->out_buf, &text->out_buf_len, &resplen, 2916 "qop", (unsigned char *) qop, TRUE) != SASL_OK) { 2917 result = SASL_FAIL; 2918 goto FreeAllMem; 2919 } 2920 } 2921 2922 /* self check */ 2923 if (strlen(text->out_buf) > 2048) { 2924 result = SASL_FAIL; 2925 goto FreeAllMem; 2926 } 2927 } 2928 2929 *serveroutlen = (unsigned) strlen(text->out_buf); 2930 *serverout = text->out_buf; 2931 2932 result = SASL_OK; 2933 2934 FreeAllMem: 2935 if (clientinlen > 0 && 2936 text->reauth->timeout && 2937 sparams->utils->mutex_lock(text->reauth->mutex) == SASL_OK) { /* LOCK */ 2938 2939 /* Look for an entry for the nonce value */ 2940 unsigned val = hash((char *) nonce) % text->reauth->size; 2941 2942 switch (result) { 2943 case SASL_OK: 2944 /* successful auth, setup for future reauth */ 2945 if (text->nonce_count == 1) { 2946 /* successful initial auth, create new entry */ 2947 clear_reauth_entry(&text->reauth->e[val], SERVER, sparams->utils); 2948 text->reauth->e[val].authid = username; username = NULL; 2949 text->reauth->e[val].realm = text->realm; text->realm = NULL; 2950 text->reauth->e[val].nonce = text->nonce; text->nonce = NULL; 2951 text->reauth->e[val].cnonce = cnonce; cnonce = NULL; 2952 } 2953 if (text->nonce_count < text->reauth->e[val].nonce_count) { 2954 /* paranoia. prevent replay attacks */ 2955 clear_reauth_entry(&text->reauth->e[val], SERVER, sparams->utils); 2956 } 2957 else { 2958 text->reauth->e[val].nonce_count = ++text->nonce_count; 2959 text->reauth->e[val].u.s.timestamp = time(0); 2960 } 2961 break; 2962 default: 2963 if (text->nonce_count > 1) { 2964 /* failed reauth, clear entry */ 2965 clear_reauth_entry(&text->reauth->e[val], SERVER, sparams->utils); 2966 } 2967 else { 2968 /* failed initial auth, leave existing cache */ 2969 } 2970 } 2971 sparams->utils->mutex_unlock(text->reauth->mutex); /* UNLOCK */ 2972 } 2973 2974 /* free everything */ 2975 if (in_start) sparams->utils->free (in_start); 2976 2977 if (full_username != NULL) 2978 sparams->utils->free (full_username); 2979 if (username != NULL) 2980 sparams->utils->free (username); 2981 if (authorization_id != NULL) 2982 sparams->utils->free (authorization_id); 2983 if (realm != NULL) 2984 sparams->utils->free (realm); 2985 if (nonce != NULL) 2986 sparams->utils->free (nonce); 2987 if (cnonce != NULL) 2988 sparams->utils->free (cnonce); 2989 if (response != NULL) 2990 sparams->utils->free (response); 2991 if (cipher != NULL) 2992 sparams->utils->free (cipher); 2993 if (serverresponse != NULL) 2994 sparams->utils->free(serverresponse); 2995 if (charset != NULL) 2996 sparams->utils->free (charset); 2997 if (digesturi != NULL) 2998 sparams->utils->free (digesturi); 2999 if (qop!=NULL) 3000 sparams->utils->free (qop); 3001 if (sec) 3002 _plug_free_secret(sparams->utils, &sec); 3003 3004 return result; 3005} 3006 3007static int digestmd5_server_mech_step(void *conn_context, 3008 sasl_server_params_t *sparams, 3009 const char *clientin, 3010 unsigned clientinlen, 3011 const char **serverout, 3012 unsigned *serveroutlen, 3013 sasl_out_params_t *oparams) 3014{ 3015 context_t *text = (context_t *) conn_context; 3016 server_context_t *stext = (server_context_t *) conn_context; 3017 3018 *serverout = NULL; 3019 *serveroutlen = 0; 3020 3021 if (clientinlen > 4096) return SASL_BADPROT; 3022 3023 if (text == NULL) { 3024 return SASL_BADPROT; 3025 } 3026 3027 switch (text->state) { 3028 3029 case 1: 3030 /* setup SSF limits */ 3031 if (!text->http_mode && /* HTTP Digest doesn't need buffer */ 3032 !sparams->props.maxbufsize) { 3033 stext->limitssf = 0; 3034 stext->requiressf = 0; 3035 } else { 3036 if (sparams->props.max_ssf < sparams->external_ssf) { 3037 stext->limitssf = 0; 3038 } else { 3039 stext->limitssf = 3040 sparams->props.max_ssf - sparams->external_ssf; 3041 } 3042 if (sparams->props.min_ssf < sparams->external_ssf) { 3043 stext->requiressf = 0; 3044 } else { 3045 stext->requiressf = 3046 sparams->props.min_ssf - sparams->external_ssf; 3047 } 3048 } 3049 3050 if (clientin && text->reauth->timeout) { 3051 /* here's where we attempt fast reauth if possible */ 3052 if (digestmd5_server_mech_step2(stext, sparams, 3053 clientin, clientinlen, 3054 serverout, serveroutlen, 3055 oparams) == SASL_OK) { 3056 return SASL_OK; 3057 } 3058 3059 sparams->utils->log(NULL, SASL_LOG_WARN, 3060 "DIGEST-MD5 reauth failed\n"); 3061 3062 /* re-initialize everything for a fresh start */ 3063 memset(oparams, 0, sizeof(sasl_out_params_t)); 3064 if (text->nonce) sparams->utils->free(text->nonce); 3065 if (text->realm) sparams->utils->free(text->realm); 3066 text->nonce = NULL; text->realm = NULL; /* APPLE */ 3067 3068 /* fall through and issue challenge */ 3069 } 3070 3071 return digestmd5_server_mech_step1(stext, sparams, 3072 clientin, clientinlen, 3073 serverout, serveroutlen, oparams); 3074 3075 case 2: 3076 return digestmd5_server_mech_step2(stext, sparams, 3077 clientin, clientinlen, 3078 serverout, serveroutlen, oparams); 3079 3080 default: 3081 sparams->utils->log(NULL, SASL_LOG_ERR, 3082 "Invalid DIGEST-MD5 server step %d\n", text->state); 3083 return SASL_FAIL; 3084 } 3085 3086 return SASL_FAIL; /* should never get here */ 3087} 3088 3089static void digestmd5_server_mech_dispose(void *conn_context, 3090 const sasl_utils_t *utils) 3091{ 3092 server_context_t *stext = (server_context_t *) conn_context; 3093 3094 if (!stext || !utils) return; 3095 3096 digestmd5_common_mech_dispose(conn_context, utils); 3097} 3098 3099static sasl_server_plug_t digestmd5_server_plugins[] = 3100{ 3101 { 3102 "DIGEST-MD5", /* mech_name */ 3103#ifdef WITH_RC4 3104 128, /* max_ssf */ 3105#elif defined(WITH_DES) 3106 112, 3107#else 3108 1, 3109#endif 3110 SASL_SEC_NOPLAINTEXT 3111 | SASL_SEC_NOANONYMOUS 3112 | SASL_SEC_MUTUAL_AUTH, /* security_flags */ 3113 SASL_FEAT_ALLOWS_PROXY 3114 | SASL_FEAT_SUPPORTS_HTTP, /* features */ 3115 &server_glob_context, /* glob_context */ 3116 &digestmd5_server_mech_new, /* mech_new */ 3117 &digestmd5_server_mech_step, /* mech_step */ 3118 &digestmd5_server_mech_dispose, /* mech_dispose */ 3119 &digestmd5_common_mech_free, /* mech_free */ 3120 NULL, /* setpass */ 3121 NULL, /* user_query */ 3122 NULL, /* idle */ 3123 NULL, /* mech avail */ 3124 NULL /* spare */ 3125 } 3126}; 3127 3128int digestmd5_server_plug_init(sasl_utils_t *utils, 3129 int maxversion, 3130 int *out_version, 3131 sasl_server_plug_t **pluglist, 3132 int *plugcount) 3133{ 3134 reauth_cache_t *reauth_cache; 3135 const char *timeout = NULL; 3136 unsigned int len; 3137 3138 if (maxversion < SASL_SERVER_PLUG_VERSION) { 3139 return SASL_BADVERS; 3140 } 3141 3142 /* reauth cache */ 3143 reauth_cache = utils->malloc(sizeof(reauth_cache_t)); 3144 if (reauth_cache == NULL) { 3145 return SASL_NOMEM; 3146 } 3147 memset(reauth_cache, 0, sizeof(reauth_cache_t)); 3148 reauth_cache->i_am = SERVER; 3149 3150 /* fetch and canonify the reauth_timeout */ 3151 utils->getopt(utils->getopt_context, "DIGEST-MD5", "reauth_timeout", 3152 &timeout, &len); 3153 if (timeout) { 3154 reauth_cache->timeout = (time_t) 60 * strtol(timeout, NULL, 10); 3155 } 3156 if (reauth_cache->timeout < 0) { 3157 reauth_cache->timeout = 0; 3158 } 3159 3160 if (reauth_cache->timeout) { 3161 /* mutex */ 3162 reauth_cache->mutex = utils->mutex_alloc(); 3163 if (!reauth_cache->mutex) { 3164 utils->free(reauth_cache); 3165 return SASL_FAIL; 3166 } 3167 3168 /* entries */ 3169 reauth_cache->size = 100; 3170 reauth_cache->e = utils->malloc(reauth_cache->size * 3171 sizeof(reauth_entry_t)); 3172 if (reauth_cache->e == NULL) { 3173 utils->mutex_free(reauth_cache->mutex); 3174 utils->free(reauth_cache); 3175 return SASL_NOMEM; 3176 } 3177 memset(reauth_cache->e, 0, reauth_cache->size * sizeof(reauth_entry_t)); 3178 } 3179 3180 ((digest_glob_context_t *) digestmd5_server_plugins[0].glob_context)->reauth = reauth_cache; 3181 3182 *out_version = SASL_SERVER_PLUG_VERSION; 3183 *pluglist = digestmd5_server_plugins; 3184 *plugcount = 1; 3185 3186 return SASL_OK; 3187} 3188 3189/***************************** Client Section *****************************/ 3190 3191typedef struct client_context { 3192 context_t common; 3193 3194 sasl_secret_t *password; /* user password */ 3195 unsigned int free_password; /* set if we need to free password */ 3196 3197 int protection; 3198 struct digest_cipher *cipher; 3199 unsigned long server_maxbuf; 3200 3201 /* for HTTP mode (RFC 2617) only */ 3202 char *algorithm; 3203 unsigned char *opaque; 3204} client_context_t; 3205 3206static digest_glob_context_t client_glob_context; 3207 3208/* calculate H(A1) as per spec */ 3209static void DigestCalcHA1(context_t * text, 3210 const sasl_utils_t * utils, 3211 char *pszAlg, 3212 unsigned char *pszUserName, 3213 unsigned char *pszRealm, 3214 sasl_secret_t * pszPassword, 3215 unsigned char *pszAuthorization_id, 3216 unsigned char *pszNonce, 3217 unsigned char *pszCNonce, 3218 HASHHEX SessionKey) 3219{ 3220 MD5_CTX Md5Ctx; 3221 HASH HA1; 3222 3223 DigestCalcSecret(utils, 3224 pszUserName, 3225 pszRealm, 3226 (unsigned char *) pszPassword->data, 3227 pszPassword->len, 3228 FALSE, 3229 HA1); 3230 3231 if (!text->http_mode || /* per RFC 2831 */ 3232 (pszAlg && strcasecmp(pszAlg, "md5-sess") == 0)) { /* per RFC 2617 */ 3233 /* calculate the session key */ 3234 utils->MD5Init(&Md5Ctx); 3235 if (text->http_mode) { 3236 /* per RFC 2617 Errata ID 1649 */ 3237 HASHHEX HA1Hex; 3238 3239 CvtHex(HA1, HA1Hex); 3240 utils->MD5Update(&Md5Ctx, HA1Hex, HASHHEXLEN); 3241 } 3242 else { 3243 /* per RFC 2831 */ 3244 utils->MD5Update(&Md5Ctx, HA1, HASHLEN); 3245 } 3246 utils->MD5Update(&Md5Ctx, COLON, 1); 3247 utils->MD5Update(&Md5Ctx, pszNonce, (unsigned) strlen((char *) pszNonce)); 3248 utils->MD5Update(&Md5Ctx, COLON, 1); 3249 utils->MD5Update(&Md5Ctx, pszCNonce, (unsigned) strlen((char *) pszCNonce)); 3250 if (pszAuthorization_id != NULL) { 3251 utils->MD5Update(&Md5Ctx, COLON, 1); 3252 utils->MD5Update(&Md5Ctx, pszAuthorization_id, 3253 (unsigned) strlen((char *) pszAuthorization_id)); 3254 } 3255 utils->MD5Final(HA1, &Md5Ctx); 3256 } 3257 3258 CvtHex(HA1, SessionKey); 3259 3260 /* xxx rc-* use different n */ 3261 3262 /* save HA1 because we'll need it for the privacy and integrity keys */ 3263 memcpy(text->HA1, HA1, sizeof(HASH)); 3264 3265} 3266 3267static char *calculate_response(context_t * text, 3268 const sasl_utils_t * utils, 3269 char *algorithm, 3270 unsigned char *username, 3271 unsigned char *realm, 3272 unsigned char *nonce, 3273 unsigned int ncvalue, 3274 unsigned char *cnonce, 3275 char *qop, 3276 const sasl_http_request_t *request, 3277 sasl_secret_t * passwd, 3278 unsigned char *authorization_id, 3279 char **response_value) 3280{ 3281 HASHHEX SessionKey; 3282 HASH EntityHash; 3283 HASHHEX HEntity; 3284 HASHHEX Response; 3285 char *result; 3286 3287 /* Verifing that all parameters was defined */ 3288 if(!username || !cnonce || !nonce || !ncvalue || !request || !passwd) { 3289 PARAMERROR( utils ); 3290 return NULL; 3291 } 3292 3293 if (realm == NULL) { 3294 /* a NULL realm is equivalent to the empty string */ 3295 realm = (unsigned char *) ""; 3296 } 3297 3298 if (qop == NULL) { 3299 /* default to a qop of just authentication */ 3300 qop = "auth"; 3301 } 3302 3303 DigestCalcHA1(text, 3304 utils, 3305 algorithm, 3306 username, 3307 realm, 3308 passwd, 3309 authorization_id, 3310 nonce, 3311 cnonce, 3312 SessionKey); 3313 3314 if (text->http_mode) { 3315 /* per RFC 2617 */ 3316 MD5_CTX Md5Ctx; 3317 3318 utils->MD5Init(&Md5Ctx); 3319 utils->MD5Update(&Md5Ctx, request->entity, request->elen); 3320 utils->MD5Final(EntityHash, &Md5Ctx); 3321 } 3322 else { 3323 /* per RFC 2831 */ 3324 memset(EntityHash, 0, HASHLEN); 3325 } 3326 CvtHex(EntityHash, HEntity); 3327 3328 DigestCalcResponse(utils, 3329 SessionKey,/* HEX(H(A1)) */ 3330 nonce, /* nonce from server */ 3331 ncvalue, /* 8 hex digits */ 3332 cnonce, /* client nonce */ 3333 (unsigned char *) qop, /* qop-value: "", "auth", 3334 * "auth-int" */ 3335 (unsigned char *) request->uri, /* requested URL */ 3336 (unsigned char *) request->method, 3337 HEntity, /* H(entity body) if qop="auth-int" */ 3338 Response /* request-digest or response-digest */ 3339 ); 3340 3341 result = utils->malloc(HASHHEXLEN + 1); 3342 memcpy(result, Response, HASHHEXLEN); 3343 result[HASHHEXLEN] = 0; 3344 3345 if (response_value != NULL) { 3346 char * new_response_value; 3347 3348 DigestCalcResponse(utils, 3349 SessionKey, /* HEX(H(A1)) */ 3350 nonce, /* nonce from server */ 3351 ncvalue, /* 8 hex digits */ 3352 cnonce, /* client nonce */ 3353 (unsigned char *) qop, /* qop-value: "", "auth", 3354 * "auth-int" */ 3355 (unsigned char *) request->uri, /* requested URL */ 3356 NULL, 3357 HEntity, /* H(entity body) if qop="auth-int" */ 3358 Response /* request-digest or response-digest */ 3359 ); 3360 3361 new_response_value = utils->realloc(*response_value, HASHHEXLEN + 1); 3362 if (new_response_value == NULL) { 3363 free (*response_value); 3364 *response_value = NULL; 3365 return NULL; 3366 } 3367 *response_value = new_response_value; 3368 3369 memcpy(*response_value, Response, HASHHEXLEN); 3370 (*response_value)[HASHHEXLEN] = 0; 3371 3372 } 3373 3374 return result; 3375} 3376 3377static int make_client_response(context_t *text, 3378 sasl_client_params_t *params, 3379 sasl_out_params_t *oparams) 3380{ 3381 client_context_t *ctext = (client_context_t *) text; 3382 char *qop = NULL; 3383 unsigned nbits = 0; 3384 char *digesturi = NULL; 3385 bool IsUTF8 = FALSE; 3386 char ncvalue[10]; 3387 char maxbufstr[64]; 3388 char *response = NULL; 3389 unsigned resplen = 0; 3390 int result = SASL_OK; 3391 cipher_free_t *old_cipher_free = NULL; 3392 sasl_http_request_t rfc2831_request; 3393 const sasl_http_request_t *request; 3394 3395 params->utils->log(params->utils->conn, SASL_LOG_DEBUG, 3396 "DIGEST-MD5 make_client_response()"); 3397 3398 if (oparams->mech_ssf > 1) { 3399 /* Remember the old cipher free function (if any). 3400 It will be called later, once we are absolutely 3401 sure that authentication was successful. */ 3402 old_cipher_free = text->cipher_free; 3403 /* free the old cipher context first */ 3404 } 3405 3406 switch (ctext->protection) { 3407 case DIGEST_PRIVACY: 3408 qop = "auth-conf"; 3409 oparams->encode = &digestmd5_encode; 3410 oparams->decode = &digestmd5_decode; 3411 oparams->mech_ssf = ctext->cipher->ssf; 3412 3413 nbits = ctext->cipher->n; 3414 text->cipher_enc = ctext->cipher->cipher_enc; 3415 text->cipher_dec = ctext->cipher->cipher_dec; 3416 text->cipher_free = ctext->cipher->cipher_free; 3417 text->cipher_init = ctext->cipher->cipher_init; 3418 break; 3419 case DIGEST_INTEGRITY: 3420 qop = "auth-int"; 3421 oparams->encode = &digestmd5_encode; 3422 oparams->decode = &digestmd5_decode; 3423 oparams->mech_ssf = 1; 3424 break; 3425 case DIGEST_NOLAYER: 3426 default: 3427 qop = "auth"; 3428 oparams->encode = NULL; 3429 oparams->decode = NULL; 3430 oparams->mech_ssf = 0; 3431 } 3432 3433 if (text->http_mode) { 3434 /* per RFC 2617 (HTTP Request as set by calling application) */ 3435 request = params->http_request; 3436 } 3437 else { 3438 /* per RFC 2831 */ 3439 digesturi = params->utils->malloc(strlen(params->service) + 1 + 3440 strlen(params->serverFQDN) + 1 + 3441 1); 3442 if (digesturi == NULL) { 3443 result = SASL_NOMEM; 3444 goto FreeAllocatedMem; 3445 } 3446 3447 /* allocated exactly this. safe */ 3448 strcpy(digesturi, params->service); 3449 strcat(digesturi, "/"); 3450 strcat(digesturi, params->serverFQDN); 3451 /* 3452 * strcat (digesturi, "/"); strcat (digesturi, params->serverFQDN); 3453 */ 3454 3455 rfc2831_request.method = "AUTHENTICATE"; 3456 rfc2831_request.uri = digesturi; 3457 rfc2831_request.entity = NULL; 3458 rfc2831_request.elen = 0; 3459 rfc2831_request.non_persist = 0; 3460 request = &rfc2831_request; 3461 } 3462 3463 /* response */ 3464 response = 3465 calculate_response(text, 3466 params->utils, 3467 ctext->algorithm, 3468 (unsigned char *) oparams->authid, 3469 (unsigned char *) text->realm, 3470 text->nonce, 3471 text->nonce_count, 3472 text->cnonce, 3473 qop, 3474 request, 3475 ctext->password, 3476 strcmp(oparams->user, oparams->authid) ? 3477 (unsigned char *) oparams->user : NULL, 3478 &text->response_value); 3479 3480 3481 resplen = 0; 3482 if (text->out_buf) params->utils->free(text->out_buf); 3483 text->out_buf = NULL; 3484 text->out_buf_len = 0; 3485 if (add_to_challenge(params->utils, 3486 &text->out_buf, &text->out_buf_len, &resplen, 3487 "username", (unsigned char *) oparams->authid, 3488 TRUE) != SASL_OK) { 3489 result = SASL_FAIL; 3490 goto FreeAllocatedMem; 3491 } 3492 3493 if (add_to_challenge(params->utils, 3494 &text->out_buf, &text->out_buf_len, &resplen, 3495 "realm", (unsigned char *) text->realm, 3496 TRUE) != SASL_OK) { 3497 result = SASL_FAIL; 3498 goto FreeAllocatedMem; 3499 } 3500 if (strcmp(oparams->user, oparams->authid)) { 3501 if (add_to_challenge(params->utils, 3502 &text->out_buf, &text->out_buf_len, &resplen, 3503 "authzid", (unsigned char *) oparams->user, TRUE) != SASL_OK) { 3504 result = SASL_FAIL; 3505 goto FreeAllocatedMem; 3506 } 3507 } 3508 if (add_to_challenge(params->utils, 3509 &text->out_buf, &text->out_buf_len, &resplen, 3510 "nonce", text->nonce, TRUE) != SASL_OK) { 3511 result = SASL_FAIL; 3512 goto FreeAllocatedMem; 3513 } 3514 if (add_to_challenge(params->utils, 3515 &text->out_buf, &text->out_buf_len, &resplen, 3516 "cnonce", text->cnonce, TRUE) != SASL_OK) { 3517 result = SASL_FAIL; 3518 goto FreeAllocatedMem; 3519 } 3520 snprintf(ncvalue, sizeof(ncvalue), "%08x", text->nonce_count); 3521 if (add_to_challenge(params->utils, 3522 &text->out_buf, &text->out_buf_len, &resplen, 3523 "nc", (unsigned char *) ncvalue, FALSE) != SASL_OK) { 3524 result = SASL_FAIL; 3525 goto FreeAllocatedMem; 3526 } 3527 if (add_to_challenge(params->utils, 3528 &text->out_buf, &text->out_buf_len, &resplen, 3529 "qop", (unsigned char *) qop, FALSE) != SASL_OK) { 3530 result = SASL_FAIL; 3531 goto FreeAllocatedMem; 3532 } 3533 if (ctext->cipher != NULL) { 3534 if (add_to_challenge(params->utils, 3535 &text->out_buf, &text->out_buf_len, &resplen, 3536 "cipher", 3537 (unsigned char *) ctext->cipher->name, 3538 FALSE) != SASL_OK) { 3539 result = SASL_FAIL; 3540 goto FreeAllocatedMem; 3541 } 3542 } 3543 3544 if (params->props.maxbufsize) { 3545 snprintf(maxbufstr, sizeof(maxbufstr), "%d", params->props.maxbufsize); 3546 if (add_to_challenge(params->utils, 3547 &text->out_buf, &text->out_buf_len, &resplen, 3548 "maxbuf", (unsigned char *) maxbufstr, 3549 FALSE) != SASL_OK) { 3550 SETERROR(params->utils, 3551 "internal error: add_to_challenge maxbuf failed"); 3552 goto FreeAllocatedMem; 3553 } 3554 } 3555 3556 if (IsUTF8) { 3557 if (add_to_challenge(params->utils, 3558 &text->out_buf, &text->out_buf_len, &resplen, 3559 "charset", (unsigned char *) "utf-8", 3560 FALSE) != SASL_OK) { 3561 result = SASL_FAIL; 3562 goto FreeAllocatedMem; 3563 } 3564 } 3565 if (add_to_challenge(params->utils, 3566 &text->out_buf, &text->out_buf_len, &resplen, 3567 text->http_mode ? "uri" /* per RFC 2617 */ 3568 : "digest-uri", /* per RFC 2831 */ 3569 (unsigned char *) request->uri, 3570 TRUE) != SASL_OK) { 3571 result = SASL_FAIL; 3572 goto FreeAllocatedMem; 3573 } 3574 if (text->http_mode) { 3575 /* per RFC 2617: algorithm & opaque MUST be sent back to server */ 3576 if (add_to_challenge(params->utils, 3577 &text->out_buf, &text->out_buf_len, &resplen, 3578 "algorithm", (unsigned char *) ctext->algorithm, 3579 FALSE) != SASL_OK) { 3580 result = SASL_FAIL; 3581 goto FreeAllocatedMem; 3582 } 3583 if (ctext->opaque) { 3584 if (add_to_challenge(params->utils, 3585 &text->out_buf, &text->out_buf_len, &resplen, 3586 "opaque", ctext->opaque, TRUE) != SASL_OK) { 3587 result = SASL_FAIL; 3588 goto FreeAllocatedMem; 3589 } 3590 } 3591 } 3592 if (add_to_challenge(params->utils, 3593 &text->out_buf, &text->out_buf_len, &resplen, 3594 "response", (unsigned char *) response, 3595 FALSE) != SASL_OK) { 3596 3597 result = SASL_FAIL; 3598 goto FreeAllocatedMem; 3599 } 3600 3601 /* self check */ 3602 if (strlen(text->out_buf) > 2048) { 3603 result = SASL_FAIL; 3604 goto FreeAllocatedMem; 3605 } 3606 3607 /* set oparams */ 3608 oparams->maxoutbuf = ctext->server_maxbuf; 3609 if(oparams->mech_ssf > 1) { 3610 /* MAC block (privacy) */ 3611 oparams->maxoutbuf -= 25; 3612 } else if(oparams->mech_ssf == 1) { 3613 /* MAC block (integrity) */ 3614 oparams->maxoutbuf -= 16; 3615 } 3616 3617 text->seqnum = 0; /* for integrity/privacy */ 3618 text->rec_seqnum = 0; /* for integrity/privacy */ 3619 text->utils = params->utils; 3620 3621 /* Free the old security layer, if any */ 3622 if (old_cipher_free) old_cipher_free(text); 3623 3624 /* used by layers */ 3625 _plug_decode_init(&text->decode_context, text->utils, 3626 params->props.maxbufsize ? params->props.maxbufsize : 3627 DEFAULT_BUFSIZE); 3628 3629 if (oparams->mech_ssf > 0) { 3630 unsigned char enckey[16]; 3631 unsigned char deckey[16]; 3632 3633 create_layer_keys(text, params->utils, text->HA1, nbits, 3634 enckey, deckey); 3635 3636 /* initialize cipher if need be */ 3637 if (text->cipher_init) { 3638 text->cipher_init(text, enckey, deckey); 3639 } 3640 } 3641 3642 result = SASL_OK; 3643 3644 FreeAllocatedMem: 3645 if (digesturi) params->utils->free(digesturi); 3646 if (response) params->utils->free(response); 3647 3648 return result; 3649} 3650 3651static int parse_server_challenge(client_context_t *ctext, 3652 sasl_client_params_t *params, 3653 const char *serverin, unsigned serverinlen, 3654 char ***outrealms, int *noutrealm) 3655{ 3656 context_t *text = (context_t *) ctext; 3657 int result = SASL_OK; 3658 char *in_start = NULL; 3659 char *in = NULL; 3660 char **realms = NULL; 3661 int nrealm = 0; 3662 sasl_ssf_t limit, musthave = 0; 3663 sasl_ssf_t external; 3664 int protection = 0; 3665 int saw_qop = 0; 3666 int ciphers = 0; 3667 int maxbuf_count = 0; 3668 bool IsUTF8 = FALSE; 3669 int algorithm_count = 0; 3670 int opaque_count = 0; 3671 3672 params->utils->log(params->utils->conn, SASL_LOG_DEBUG, 3673 "DIGEST-MD5 parse_server_challenge()"); 3674 3675 if (!serverin || !serverinlen) { 3676 params->utils->seterror(params->utils->conn, 0, 3677 "no server challenge"); 3678 return SASL_FAIL; 3679 } 3680 3681 in_start = in = params->utils->malloc(serverinlen + 1); 3682 if (in == NULL) return SASL_NOMEM; 3683 3684 memcpy(in, serverin, serverinlen); 3685 in[serverinlen] = 0; 3686 3687 ctext->server_maxbuf = 65536; /* Default value for maxbuf */ 3688 3689 /* create a new cnonce */ 3690 text->cnonce = create_nonce(params->utils); 3691 if (text->cnonce == NULL) { 3692 params->utils->seterror(params->utils->conn, 0, 3693 "failed to create cnonce"); 3694 result = SASL_FAIL; 3695 goto FreeAllocatedMem; 3696 } 3697 3698 /* parse the challenge */ 3699 while (in[0] != '\0') { 3700 char *name, *value; 3701 3702 get_pair(&in, &name, &value); 3703 3704 /* if parse error */ 3705 if (name == NULL) { 3706 params->utils->seterror(params->utils->conn, 0, "Parse error"); 3707 result = SASL_BADAUTH; 3708 goto FreeAllocatedMem; 3709 } 3710 3711 if (*name == '\0') { 3712 break; 3713 } 3714 3715 if (strcasecmp(name, "realm") == 0) { 3716 nrealm++; 3717 3718 if(!realms) 3719 realms = params->utils->malloc(sizeof(char *) * (nrealm + 1)); 3720 else 3721 realms = params->utils->realloc(realms, 3722 sizeof(char *) * (nrealm + 1)); 3723 3724 if (realms == NULL) { 3725 result = SASL_NOMEM; 3726 goto FreeAllocatedMem; 3727 } 3728 3729 _plug_strdup(params->utils, value, &realms[nrealm-1], NULL); 3730 realms[nrealm] = NULL; 3731 } else if (strcasecmp(name, "nonce") == 0) { 3732 _plug_strdup(params->utils, value, (char **) &text->nonce, 3733 NULL); 3734 text->nonce_count = 1; 3735 } else if (strcasecmp(name, "qop") == 0) { 3736 saw_qop = 1; 3737 while (value && *value) { 3738 char *comma; 3739 char *end_val; 3740 3741SKIP_SPACES_IN_QOP: 3742 /* skipping spaces: */ 3743 value = skip_lws(value); 3744 if (*value == '\0') { 3745 break; 3746 } 3747 3748 /* check for an extreme case when there is no data: LWSP ',' */ 3749 if (*value == ',') { 3750 value++; 3751 goto SKIP_SPACES_IN_QOP; 3752 } 3753 3754 comma = strchr(value, ','); 3755 3756 if (comma != NULL) { 3757 *comma++ = '\0'; 3758 } 3759 3760 /* skip LWSP at the end of the value (if any), skip_r_lws returns pointer to 3761 the first LWSP character, NUL (if there were none) or NULL if the value 3762 is entirely from LWSP characters */ 3763 end_val = skip_r_lws (value); 3764 if (end_val == NULL) { 3765 value = comma; 3766 continue; 3767 } else { 3768 /* strip LWSP */ 3769 *end_val = '\0'; 3770 } 3771 3772 if (strcasecmp(value, "auth-conf") == 0) { 3773 protection |= DIGEST_PRIVACY; 3774 } else if (strcasecmp(value, "auth-int") == 0) { 3775 protection |= DIGEST_INTEGRITY; 3776 } else if (strcasecmp(value, "auth") == 0) { 3777 protection |= DIGEST_NOLAYER; 3778 } else { 3779 params->utils->log(params->utils->conn, SASL_LOG_DEBUG, 3780 "Server supports unknown layer: %s\n", 3781 value); 3782 } 3783 3784 value = comma; 3785 } 3786 } else if (strcasecmp(name, "cipher") == 0) { 3787 while (value && *value) { 3788 struct digest_cipher *cipher = available_ciphers; 3789 char *comma; 3790 char *end_val; 3791 3792SKIP_SPACES_IN_CIPHER: 3793 /* skipping spaces: */ 3794 value = skip_lws(value); 3795 if (*value == '\0') { 3796 break; 3797 } 3798 3799 /* check for an extreme case when there is no data: LWSP ',' */ 3800 if (*value == ',') { 3801 value++; 3802 goto SKIP_SPACES_IN_CIPHER; 3803 } 3804 3805 comma = strchr(value, ','); 3806 3807 if (comma != NULL) { 3808 *comma++ = '\0'; 3809 } 3810 3811 /* skip LWSP at the end of the value, skip_r_lws returns pointer to 3812 the first LWSP character or NULL */ 3813 end_val = skip_r_lws (value); 3814 if (end_val == NULL) { 3815 value = comma; 3816 continue; 3817 } else { 3818 /* strip LWSP */ 3819 *end_val = '\0'; 3820 } 3821 3822 /* do we support this cipher? */ 3823 while (cipher->name) { 3824 if (!strcasecmp(value, cipher->name)) break; 3825 cipher++; 3826 } 3827 if (cipher->name) { 3828 ciphers |= cipher->flag; 3829 } else { 3830 params->utils->log(params->utils->conn, SASL_LOG_DEBUG, 3831 "Server supports unknown cipher: %s\n", 3832 value); 3833 } 3834 3835 value = comma; 3836 } 3837 } else if (strcasecmp(name, "stale") == 0 && ctext->password) { 3838 /* clear any cached password */ 3839 if (ctext->free_password) 3840 _plug_free_secret(params->utils, &ctext->password); 3841 ctext->password = NULL; 3842 } else if (strcasecmp(name, "maxbuf") == 0) { 3843 /* maxbuf A number indicating the size of the largest 3844 * buffer the server is able to receive when using 3845 * "auth-int". If this directive is missing, the default 3846 * value is 65536. This directive may appear at most once; 3847 * if multiple instances are present, the client should 3848 * abort the authentication exchange. 3849 */ 3850 maxbuf_count++; 3851 3852 if (maxbuf_count != 1) { 3853 result = SASL_BADAUTH; 3854 params->utils->seterror(params->utils->conn, 0, 3855 "At least two maxbuf directives found. Authentication aborted"); 3856 goto FreeAllocatedMem; 3857 } 3858 3859 if (str2ul32 (value, &ctext->server_maxbuf) == FALSE) { 3860 result = SASL_BADAUTH; 3861 params->utils->seterror(params->utils->conn, 0, 3862 "Invalid maxbuf parameter received from server (%s)", value); 3863 goto FreeAllocatedMem; 3864 } 3865 3866 if (ctext->server_maxbuf <= 16) { 3867 result = SASL_BADAUTH; 3868 params->utils->seterror(params->utils->conn, 0, 3869 "Invalid maxbuf parameter received from server (too small: %s)", value); 3870 goto FreeAllocatedMem; 3871 } 3872 3873 if (ctext->server_maxbuf > MAX_SASL_BUFSIZE) { 3874 result = SASL_BADAUTH; 3875 params->utils->seterror(params->utils->conn, 0, 3876 "Invalid maxbuf parameter received from server (too big: %s)", value); 3877 goto FreeAllocatedMem; 3878 } 3879 } else if (strcasecmp(name, "charset") == 0) { 3880 if (strcasecmp(value, "utf-8") != 0) { 3881 result = SASL_BADAUTH; 3882 params->utils->seterror(params->utils->conn, 0, 3883 "Charset must be UTF-8"); 3884 goto FreeAllocatedMem; 3885 } else { 3886 IsUTF8 = TRUE; 3887 } 3888 } else if (strcasecmp(name,"algorithm")==0) { 3889 if (text->http_mode && strcasecmp(value, "md5") == 0) { 3890 /* per RFC 2617: need to support both "md5" and "md5-sess" */ 3891 } 3892 else if (strcasecmp(value, "md5-sess") != 0) 3893 { 3894 params->utils->seterror(params->utils->conn, 0, 3895 "'algorithm' isn't 'md5-sess'"); 3896 result = SASL_FAIL; 3897 goto FreeAllocatedMem; 3898 } 3899 3900 if (text->http_mode) { 3901 /* per RFC 2617: algorithm MUST be saved */ 3902 _plug_strdup(params->utils, value, (char **) &ctext->algorithm, 3903 NULL); 3904 } 3905 algorithm_count++; 3906 if (algorithm_count > 1) 3907 { 3908 params->utils->seterror(params->utils->conn, 0, 3909 "Must see 'algorithm' only once"); 3910 result = SASL_FAIL; 3911 goto FreeAllocatedMem; 3912 } 3913 } else if (strcasecmp(name,"opaque")==0) { 3914 /* per RFC 2831: opaque MUST be ignored if received */ 3915 if (text->http_mode) { 3916 /* per RFC 2617: opaque MUST be saved */ 3917 _plug_strdup(params->utils, value, (char **) &ctext->opaque, 3918 NULL); 3919 opaque_count++; 3920 if (opaque_count > 1) 3921 { 3922 params->utils->seterror(params->utils->conn, 0, 3923 "Must see 'opaque' only once"); 3924 result = SASL_FAIL; 3925 goto FreeAllocatedMem; 3926 } 3927 } 3928 } else { 3929 params->utils->log(params->utils->conn, SASL_LOG_DEBUG, 3930 "DIGEST-MD5 unrecognized pair %s/%s: ignoring", 3931 name, value); 3932 } 3933 } 3934 3935 if (protection == 0) { 3936 /* From RFC 2831[bis]: 3937 This directive is optional; if not present it defaults to "auth". */ 3938 if (saw_qop == 0) { 3939 protection = DIGEST_NOLAYER; 3940 } else { 3941 result = SASL_BADAUTH; 3942 params->utils->seterror(params->utils->conn, 0, 3943 "Server doesn't support any known qop level"); 3944 goto FreeAllocatedMem; 3945 } 3946 } 3947 3948 if (algorithm_count != 1) { 3949 params->utils->seterror(params->utils->conn, 0, 3950 "Must see 'algorithm' once. Didn't see at all"); 3951 result = SASL_FAIL; 3952 goto FreeAllocatedMem; 3953 } 3954 3955 /* make sure we have everything we require */ 3956 if (text->nonce == NULL) { 3957 params->utils->seterror(params->utils->conn, 0, 3958 "Don't have nonce."); 3959 result = SASL_FAIL; 3960 goto FreeAllocatedMem; 3961 } 3962 3963 /* get requested ssf */ 3964 external = params->external_ssf; 3965 3966 /* what do we _need_? how much is too much? */ 3967 if (!text->http_mode && /* HTTP Digest doesn't need buffer */ 3968 params->props.maxbufsize == 0) { 3969 musthave = 0; 3970 limit = 0; 3971 } else { 3972 if (params->props.max_ssf > external) { 3973 limit = params->props.max_ssf - external; 3974 } else { 3975 limit = 0; 3976 } 3977 if (params->props.min_ssf > external) { 3978 musthave = params->props.min_ssf - external; 3979 } else { 3980 musthave = 0; 3981 } 3982 } 3983 3984 /* we now go searching for an option that gives us at least "musthave" 3985 and at most "limit" bits of ssf. */ 3986 if ((limit > 1) && (protection & DIGEST_PRIVACY)) { 3987 struct digest_cipher *cipher; 3988 3989 /* let's find an encryption scheme that we like */ 3990 cipher = available_ciphers; 3991 while (cipher->name) { 3992 /* examine each cipher we support, see if it meets our security 3993 requirements, and see if the server supports it. 3994 choose the best one of these */ 3995 if ((limit >= cipher->ssf) && (musthave <= cipher->ssf) && 3996 (ciphers & cipher->flag) && 3997 (!ctext->cipher || (cipher->ssf > ctext->cipher->ssf))) { 3998 ctext->cipher = cipher; 3999 } 4000 cipher++; 4001 } 4002 4003 if (ctext->cipher) { 4004 /* we found a cipher we like */ 4005 ctext->protection = DIGEST_PRIVACY; 4006 } else { 4007 /* we didn't find any ciphers we like */ 4008 params->utils->seterror(params->utils->conn, 0, 4009 "No good privacy layers"); 4010 } 4011 } 4012 4013 if (ctext->cipher == NULL) { 4014 /* we failed to find an encryption layer we liked; 4015 can we use integrity or nothing? */ 4016 4017 if ((limit >= 1) && (musthave <= 1) 4018 && (protection & DIGEST_INTEGRITY)) { 4019 /* integrity */ 4020 ctext->protection = DIGEST_INTEGRITY; 4021 } else if (musthave <= 0) { 4022 /* no layer */ 4023 ctext->protection = DIGEST_NOLAYER; 4024 4025 /* See if server supports not having a layer */ 4026 if ((protection & DIGEST_NOLAYER) != DIGEST_NOLAYER) { 4027 params->utils->seterror(params->utils->conn, 0, 4028 "Server doesn't support \"no layer\""); 4029 result = SASL_FAIL; 4030 goto FreeAllocatedMem; 4031 } 4032 } else { 4033 params->utils->seterror(params->utils->conn, 0, 4034 "Can't find an acceptable layer"); 4035 result = SASL_TOOWEAK; 4036 goto FreeAllocatedMem; 4037 } 4038 } 4039 4040 *outrealms = realms; 4041 *noutrealm = nrealm; 4042 4043 FreeAllocatedMem: 4044 if (in_start) params->utils->free(in_start); 4045 4046 if (result != SASL_OK && realms) { 4047 int lup; 4048 4049 /* need to free all the realms */ 4050 for (lup = 0;lup < nrealm; lup++) 4051 params->utils->free(realms[lup]); 4052 4053 params->utils->free(realms); 4054 } 4055 4056 return result; 4057} 4058 4059static int ask_user_info(client_context_t *ctext, 4060 sasl_client_params_t *params, 4061 char **realms, int nrealm, 4062 sasl_interact_t **prompt_need, 4063 sasl_out_params_t *oparams) 4064{ 4065 context_t *text = (context_t *) ctext; 4066 int result = SASL_OK; 4067 const char *authid = NULL, *userid = NULL, *realm = NULL; 4068 char *realm_chal = NULL; 4069 int user_result = SASL_OK; 4070 int auth_result = SASL_OK; 4071 int pass_result = SASL_OK; 4072 int realm_result = SASL_FAIL; 4073 int i; 4074 size_t len; 4075 4076 params->utils->log(params->utils->conn, SASL_LOG_DEBUG, 4077 "DIGEST-MD5 ask_user_info()"); 4078 4079 /* try to get the authid */ 4080 if (oparams->authid == NULL) { 4081 auth_result = _plug_get_authid(params->utils, &authid, prompt_need); 4082 4083 if ((auth_result != SASL_OK) && (auth_result != SASL_INTERACT)) { 4084 return auth_result; 4085 } 4086 } 4087 4088 /* try to get the userid */ 4089 if (oparams->user == NULL) { 4090 user_result = _plug_get_userid(params->utils, &userid, prompt_need); 4091 4092 if ((user_result != SASL_OK) && (user_result != SASL_INTERACT)) { 4093 return user_result; 4094 } 4095 } 4096 4097 /* try to get the password */ 4098 if (ctext->password == NULL) { 4099 pass_result = _plug_get_password(params->utils, &ctext->password, 4100 &ctext->free_password, prompt_need); 4101 if ((pass_result != SASL_OK) && (pass_result != SASL_INTERACT)) { 4102 return pass_result; 4103 } 4104 } 4105 4106 /* try to get the realm */ 4107 if (text->realm == NULL) { 4108 if (realms) { 4109 if(nrealm == 1) { 4110 /* only one choice */ 4111 realm = realms[0]; 4112 realm_result = SASL_OK; 4113 } else { 4114 /* ask the user */ 4115 realm_result = _plug_get_realm(params->utils, 4116 (const char **) realms, 4117 (const char **) &realm, 4118 prompt_need); 4119 } 4120 } 4121 4122 /* fake the realm if we must */ 4123 if ((realm_result != SASL_OK) && (realm_result != SASL_INTERACT)) { 4124 if (params->serverFQDN) { 4125 realm = params->serverFQDN; 4126 } else { 4127 return realm_result; 4128 } 4129 } 4130 } 4131 4132 /* free prompts we got */ 4133 if (prompt_need && *prompt_need) { 4134 params->utils->free(*prompt_need); 4135 *prompt_need = NULL; 4136 } 4137 4138 /* if there are prompts not filled in */ 4139 if ((user_result == SASL_INTERACT) || (auth_result == SASL_INTERACT) || 4140 (pass_result == SASL_INTERACT) || (realm_result == SASL_INTERACT)) { 4141 4142 /* make our default realm */ 4143 if (realm_result == SASL_INTERACT) { 4144 if (realms) { 4145 len = strlen(REALM_CHAL_PREFIX); 4146 for (i = 0; i < nrealm; i++) { 4147 len += strlen(realms[i]) + 4 /* " {}," */; 4148 } 4149 realm_chal = params->utils->malloc(len + 1); 4150 strcpy (realm_chal, REALM_CHAL_PREFIX); 4151 for (i = 0; i < nrealm; i++) { 4152 strcat (realm_chal, " {"); 4153 strcat (realm_chal, realms[i]); 4154 strcat (realm_chal, "},"); 4155 } 4156 /* Replace the terminating comma with dot */ 4157 realm_chal[len-1] = '.'; 4158 4159 } else if (params->serverFQDN) { 4160 realm_chal = params->utils->malloc(3+strlen(params->serverFQDN)); 4161 if (realm_chal) { 4162 sprintf(realm_chal, "{%s}", params->serverFQDN); 4163 } else { 4164 return SASL_NOMEM; 4165 } 4166 } 4167 } 4168 4169 /* make the prompt list */ 4170 result = 4171 _plug_make_prompts(params->utils, prompt_need, 4172 user_result == SASL_INTERACT ? 4173 "Please enter your authorization name" : NULL, 4174 NULL, 4175 auth_result == SASL_INTERACT ? 4176 "Please enter your authentication name" : NULL, 4177 NULL, 4178 pass_result == SASL_INTERACT ? 4179 "Please enter your password" : NULL, NULL, 4180 NULL, NULL, NULL, 4181 realm_chal ? realm_chal : "{}", 4182 realm_result == SASL_INTERACT ? 4183 "Please enter your realm" : NULL, 4184 params->serverFQDN ? params->serverFQDN : NULL); 4185 4186 if (result == SASL_OK) return SASL_INTERACT; 4187 4188 return result; 4189 } 4190 4191 if (oparams->authid == NULL) { 4192 if (!userid || !*userid) { 4193 result = params->canon_user(params->utils->conn, authid, 0, 4194 SASL_CU_AUTHID | SASL_CU_AUTHZID, 4195 oparams); 4196 } 4197 else { 4198 result = params->canon_user(params->utils->conn, 4199 authid, 0, SASL_CU_AUTHID, oparams); 4200 if (result != SASL_OK) return result; 4201 4202 result = params->canon_user(params->utils->conn, 4203 userid, 0, SASL_CU_AUTHZID, oparams); 4204 } 4205 if (result != SASL_OK) return result; 4206 } 4207 4208 /* Get an allocated version of the realm into the structure */ 4209 if (realm && text->realm == NULL) { 4210 _plug_strdup(params->utils, realm, (char **) &text->realm, NULL); 4211 } 4212 4213 return result; 4214} 4215 4216static int digestmd5_client_mech_new(void *glob_context, 4217 sasl_client_params_t * params, 4218 void **conn_context) 4219{ 4220 context_t *text; 4221 4222 if ((params->flags & SASL_NEED_HTTP) && !params->http_request) { 4223 SETERROR(params->utils, 4224 "DIGEST-MD5 unavailable due to lack of HTTP request"); 4225 return SASL_BADPARAM; 4226 } 4227 4228 /* holds state are in -- allocate client size */ 4229 text = params->utils->malloc(sizeof(client_context_t)); 4230 if (text == NULL) 4231 return SASL_NOMEM; 4232 memset(text, 0, sizeof(client_context_t)); 4233 4234 text->state = 1; 4235 text->i_am = CLIENT; 4236 text->http_mode = (params->flags & SASL_NEED_HTTP); 4237 text->reauth = ((digest_glob_context_t *) glob_context)->reauth; 4238 4239 *conn_context = text; 4240 4241 return SASL_OK; 4242} 4243 4244static int 4245digestmd5_client_mech_step1(client_context_t *ctext, 4246 sasl_client_params_t *params, 4247 const char *serverin __attribute__((unused)), 4248 unsigned serverinlen __attribute__((unused)), 4249 sasl_interact_t **prompt_need, 4250 const char **clientout, 4251 unsigned *clientoutlen, 4252 sasl_out_params_t *oparams) 4253{ 4254 context_t *text = (context_t *) ctext; 4255 int result = SASL_FAIL; 4256 unsigned val; 4257 4258 params->utils->log(params->utils->conn, SASL_LOG_DEBUG, 4259 "DIGEST-MD5 client step 1"); 4260 4261 result = ask_user_info(ctext, params, NULL, 0, prompt_need, oparams); 4262 if (result != SASL_OK) return result; 4263 4264 /* check if we have cached info for this user on this server */ 4265 val = hash(params->serverFQDN) % text->reauth->size; 4266 if (params->utils->mutex_lock(text->reauth->mutex) == SASL_OK) { /* LOCK */ 4267 if (text->reauth->e[val].u.c.serverFQDN && 4268 !strcasecmp(text->reauth->e[val].u.c.serverFQDN, 4269 params->serverFQDN) && 4270 !strcmp(text->reauth->e[val].authid, oparams->authid)) { 4271 4272 /* we have info, so use it */ 4273 if (text->realm) params->utils->free(text->realm); 4274 _plug_strdup(params->utils, text->reauth->e[val].realm, 4275 &text->realm, NULL); 4276 _plug_strdup(params->utils, (char *) text->reauth->e[val].nonce, 4277 (char **) &text->nonce, NULL); 4278 text->nonce_count = ++text->reauth->e[val].nonce_count; 4279 _plug_strdup(params->utils, (char *) text->reauth->e[val].cnonce, 4280 (char **) &text->cnonce, NULL); 4281 if (text->http_mode) { 4282 /* per RFC 2617: algorithm & opaque MUST be sent back to server */ 4283 _plug_strdup(params->utils, 4284 (char *) text->reauth->e[val].u.c.algorithm, 4285 (char **) &ctext->algorithm, NULL); 4286 if (text->reauth->e[val].u.c.opaque) { 4287 _plug_strdup(params->utils, 4288 (char *) text->reauth->e[val].u.c.opaque, 4289 (char **) &ctext->opaque, NULL); 4290 } 4291 } 4292 ctext->protection = text->reauth->e[val].u.c.protection; 4293 ctext->cipher = text->reauth->e[val].u.c.cipher; 4294 ctext->server_maxbuf = text->reauth->e[val].u.c.server_maxbuf; 4295 } 4296 params->utils->mutex_unlock(text->reauth->mutex); /* UNLOCK */ 4297 } 4298 4299 if (!text->nonce) { 4300 /* we don't have any reauth info, so just return 4301 * that there is no initial client send */ 4302 text->state = 2; 4303 return SASL_CONTINUE; 4304 } 4305 4306 /* 4307 * (username | realm | nonce | cnonce | nonce-count | qop digest-uri | 4308 * response | maxbuf | charset | auth-param ) 4309 */ 4310 4311 result = make_client_response(text, params, oparams); 4312 if (result != SASL_OK) return result; 4313 4314 *clientoutlen = (unsigned) strlen(text->out_buf); 4315 *clientout = text->out_buf; 4316 4317 text->state = 3; 4318 return SASL_CONTINUE; 4319} 4320 4321static int digestmd5_client_mech_step2(client_context_t *ctext, 4322 sasl_client_params_t *params, 4323 const char *serverin, 4324 unsigned serverinlen, 4325 sasl_interact_t **prompt_need, 4326 const char **clientout, 4327 unsigned *clientoutlen, 4328 sasl_out_params_t *oparams) 4329{ 4330 context_t *text = (context_t *) ctext; 4331 int result = SASL_FAIL; 4332 char **realms = NULL; 4333 int nrealm = 0; 4334 4335 params->utils->log(params->utils->conn, SASL_LOG_DEBUG, 4336 "DIGEST-MD5 client step 2"); 4337 4338 if (params->props.min_ssf > params->props.max_ssf) { 4339 return SASL_BADPARAM; 4340 } 4341 4342 /* don't bother parsing the challenge more than once */ 4343 if (text->nonce == NULL) { 4344 result = parse_server_challenge(ctext, params, serverin, serverinlen, 4345 &realms, &nrealm); 4346 if (result != SASL_OK) goto FreeAllocatedMem; 4347 4348 if (nrealm == 1) { 4349 /* only one choice! */ 4350 /* APPLE: free */ 4351 if (text->realm != NULL) { 4352 params->utils->free(text->realm); 4353 } 4354 text->realm = realms[0]; 4355 4356 /* free realms */ 4357 params->utils->free(realms); 4358 realms = NULL; 4359 } else { 4360 /* Save realms for later use */ 4361 text->realms = realms; 4362 text->realm_cnt = nrealm; 4363 } 4364 } else { 4365 /* Restore the list of realms */ 4366 realms = text->realms; 4367 nrealm = text->realm_cnt; 4368 } 4369 4370 result = ask_user_info(ctext, params, realms, nrealm, 4371 prompt_need, oparams); 4372 if (result != SASL_OK) goto FreeAllocatedMem; 4373 4374 /* 4375 * (username | realm | nonce | cnonce | nonce-count | qop | digest-uri | 4376 * response | maxbuf | charset | auth-param ) 4377 */ 4378 4379 result = make_client_response(text, params, oparams); 4380 if (result != SASL_OK) goto FreeAllocatedMem; 4381 4382 *clientoutlen = (unsigned) strlen(text->out_buf); 4383 *clientout = text->out_buf; 4384 4385 text->state = 3; 4386 4387 result = SASL_CONTINUE; 4388 4389 FreeAllocatedMem: 4390 return result; 4391} 4392 4393static int 4394digestmd5_client_mech_step3(client_context_t *ctext, 4395 sasl_client_params_t *params, 4396 const char *serverin, 4397 unsigned serverinlen, 4398 sasl_interact_t **prompt_need __attribute__((unused)), 4399 const char **clientout __attribute__((unused)), 4400 unsigned *clientoutlen __attribute__((unused)), 4401 sasl_out_params_t *oparams) 4402{ 4403 context_t *text = (context_t *) ctext; 4404 char *in = NULL; 4405 char *in_start; 4406 int result = SASL_FAIL; 4407 4408 params->utils->log(params->utils->conn, SASL_LOG_DEBUG, 4409 "DIGEST-MD5 client step 3"); 4410 4411 /* Verify that server is really what he claims to be */ 4412 in_start = in = params->utils->malloc(serverinlen + 1); 4413 if (in == NULL) return SASL_NOMEM; 4414 4415 memcpy(in, serverin, serverinlen); 4416 in[serverinlen] = 0; 4417 4418 /* parse the response */ 4419 while (in[0] != '\0') { 4420 char *name, *value; 4421 get_pair(&in, &name, &value); 4422 4423 if (name == NULL) { 4424 params->utils->seterror(params->utils->conn, 0, 4425 "DIGEST-MD5 Received Garbage"); 4426 result = SASL_BADAUTH; 4427 break; 4428 } 4429 4430 if (*name == '\0') { 4431 break; 4432 } 4433 4434 if (strcasecmp(name, "rspauth") == 0) { 4435 4436 if (strcmp(text->response_value, value) != 0) { 4437 params->utils->seterror(params->utils->conn, 0, 4438 "DIGEST-MD5: This server wants us to believe that he knows shared secret"); 4439 result = SASL_BADSERV; 4440 } else { 4441 oparams->doneflag = 1; 4442 oparams->param_version = 0; 4443 4444 result = SASL_OK; 4445 } 4446 break; 4447 } else { 4448 params->utils->log(params->utils->conn, SASL_LOG_DEBUG, 4449 "DIGEST-MD5 unrecognized pair %s/%s: ignoring", 4450 name, value); 4451 } 4452 } 4453 4454 params->utils->free(in_start); 4455 4456 if (params->utils->mutex_lock(text->reauth->mutex) == SASL_OK) { /* LOCK */ 4457 unsigned val = hash(params->serverFQDN) % text->reauth->size; 4458 switch (result) { 4459 case SASL_OK: 4460 if (text->nonce_count == 1) { 4461 /* successful initial auth, setup for future reauth */ 4462 clear_reauth_entry(&text->reauth->e[val], CLIENT, params->utils); 4463 _plug_strdup(params->utils, oparams->authid, 4464 &text->reauth->e[val].authid, NULL); 4465 text->reauth->e[val].realm = text->realm; text->realm = NULL; 4466 text->reauth->e[val].nonce = text->nonce; text->nonce = NULL; 4467 text->reauth->e[val].nonce_count = text->nonce_count; 4468 text->reauth->e[val].cnonce = text->cnonce; text->cnonce = NULL; 4469 _plug_strdup(params->utils, params->serverFQDN, 4470 &text->reauth->e[val].u.c.serverFQDN, NULL); 4471 if (text->http_mode) { 4472 /* per RFC 2617: algorithm & opaque MUST be saved */ 4473 text->reauth->e[val].u.c.algorithm = ctext->algorithm; 4474 ctext->algorithm = NULL; 4475 text->reauth->e[val].u.c.opaque = ctext->opaque; 4476 ctext->opaque = NULL; 4477 } 4478 text->reauth->e[val].u.c.protection = ctext->protection; 4479 text->reauth->e[val].u.c.cipher = ctext->cipher; 4480 text->reauth->e[val].u.c.server_maxbuf = ctext->server_maxbuf; 4481 } 4482 else { 4483 /* reauth, we already incremented nonce_count */ 4484 } 4485 break; 4486 default: 4487 if (text->nonce_count > 1) { 4488 /* failed reauth, clear cache */ 4489 clear_reauth_entry(&text->reauth->e[val], CLIENT, params->utils); 4490 } 4491 else { 4492 /* failed initial auth, leave existing cache */ 4493 } 4494 } 4495 params->utils->mutex_unlock(text->reauth->mutex); /* UNLOCK */ 4496 } 4497 4498 return result; 4499} 4500 4501static int digestmd5_client_mech_step(void *conn_context, 4502 sasl_client_params_t *params, 4503 const char *serverin, 4504 unsigned serverinlen, 4505 sasl_interact_t **prompt_need, 4506 const char **clientout, 4507 unsigned *clientoutlen, 4508 sasl_out_params_t *oparams) 4509{ 4510 context_t *text = (context_t *) conn_context; 4511 client_context_t *ctext = (client_context_t *) conn_context; 4512 unsigned val = hash(params->serverFQDN) % text->reauth->size; 4513 4514 if (serverinlen > 2048) return SASL_BADPROT; 4515 4516 *clientout = NULL; 4517 *clientoutlen = 0; 4518 4519 switch (text->state) { 4520 4521 case 1: 4522 if (!serverin) { 4523 /* here's where we attempt fast reauth if possible */ 4524 int reauth = 0; 4525 4526 /* check if we have saved info for this server */ 4527 if (params->utils->mutex_lock(text->reauth->mutex) == SASL_OK) { /* LOCK */ 4528 reauth = text->reauth->e[val].u.c.serverFQDN && 4529 !strcasecmp(text->reauth->e[val].u.c.serverFQDN, 4530 params->serverFQDN); 4531 params->utils->mutex_unlock(text->reauth->mutex); /* UNLOCK */ 4532 } 4533 if (reauth) { 4534 return digestmd5_client_mech_step1(ctext, params, 4535 serverin, serverinlen, 4536 prompt_need, 4537 clientout, clientoutlen, 4538 oparams); 4539 } 4540 else { 4541 /* we don't have any reauth info, so just return 4542 * that there is no initial client send */ 4543 text->state = 2; 4544 return SASL_CONTINUE; 4545 } 4546 } 4547 4548 /* fall through and respond to challenge */ 4549 4550 case 3: 4551 if (serverin && !strncasecmp(serverin, "rspauth=", 8)) { 4552 return digestmd5_client_mech_step3(ctext, params, 4553 serverin, serverinlen, 4554 prompt_need, 4555 clientout, clientoutlen, 4556 oparams); 4557 } 4558 4559 /* fall through and respond to challenge */ 4560 text->state = 2; 4561 4562 /* cleanup after a failed reauth attempt */ 4563 if (params->utils->mutex_lock(text->reauth->mutex) == SASL_OK) { /* LOCK */ 4564 clear_reauth_entry(&text->reauth->e[val], CLIENT, params->utils); 4565 4566 params->utils->mutex_unlock(text->reauth->mutex); /* UNLOCK */ 4567 } 4568 4569 if (text->realm) params->utils->free(text->realm); 4570 if (text->nonce) params->utils->free(text->nonce); 4571 if (text->cnonce) params->utils->free(text->cnonce); 4572 text->realm = NULL; 4573 text->nonce = text->cnonce = NULL; 4574 ctext->cipher = NULL; 4575 4576 case 2: 4577 return digestmd5_client_mech_step2(ctext, params, 4578 serverin, serverinlen, 4579 prompt_need, 4580 clientout, clientoutlen, 4581 oparams); 4582 4583 default: 4584 params->utils->log(NULL, SASL_LOG_ERR, 4585 "Invalid DIGEST-MD5 client step %d\n", text->state); 4586 return SASL_FAIL; 4587 } 4588 4589 return SASL_FAIL; /* should never get here */ 4590} 4591 4592static void digestmd5_client_mech_dispose(void *conn_context, 4593 const sasl_utils_t *utils) 4594{ 4595 client_context_t *ctext = (client_context_t *) conn_context; 4596 4597 if (!ctext || !utils) return; 4598 4599 utils->log(utils->conn, SASL_LOG_DEBUG, 4600 "DIGEST-MD5 client mech dispose"); 4601 4602 if (ctext->free_password) _plug_free_secret(utils, &ctext->password); 4603 4604 digestmd5_common_mech_dispose(conn_context, utils); 4605} 4606 4607static sasl_client_plug_t digestmd5_client_plugins[] = 4608{ 4609 { 4610 "DIGEST-MD5", 4611#ifdef WITH_RC4 /* mech_name */ 4612 128, /* max ssf */ 4613#elif defined(WITH_DES) 4614 112, 4615#else 4616 1, 4617#endif 4618 SASL_SEC_NOPLAINTEXT 4619 | SASL_SEC_NOANONYMOUS 4620 | SASL_SEC_MUTUAL_AUTH, /* security_flags */ 4621 SASL_FEAT_NEEDSERVERFQDN 4622 | SASL_FEAT_ALLOWS_PROXY 4623 | SASL_FEAT_SUPPORTS_HTTP, /* features */ 4624 NULL, /* required_prompts */ 4625 &client_glob_context, /* glob_context */ 4626 &digestmd5_client_mech_new, /* mech_new */ 4627 &digestmd5_client_mech_step, /* mech_step */ 4628 &digestmd5_client_mech_dispose, /* mech_dispose */ 4629 &digestmd5_common_mech_free, /* mech_free */ 4630 NULL, /* idle */ 4631 NULL, /* spare1 */ 4632 NULL /* spare2 */ 4633 } 4634}; 4635 4636int digestmd5_client_plug_init(sasl_utils_t *utils, 4637 int maxversion, 4638 int *out_version, 4639 sasl_client_plug_t **pluglist, 4640 int *plugcount) 4641{ 4642 reauth_cache_t *reauth_cache; 4643 4644 if (maxversion < SASL_CLIENT_PLUG_VERSION) 4645 return SASL_BADVERS; 4646 4647 /* reauth cache */ 4648 reauth_cache = utils->malloc(sizeof(reauth_cache_t)); 4649 if (reauth_cache == NULL) 4650 return SASL_NOMEM; 4651 memset(reauth_cache, 0, sizeof(reauth_cache_t)); 4652 reauth_cache->i_am = CLIENT; 4653 4654 /* mutex */ 4655 reauth_cache->mutex = utils->mutex_alloc(); 4656 if (!reauth_cache->mutex) 4657 return SASL_FAIL; 4658 4659 /* entries */ 4660 reauth_cache->size = 10; 4661 reauth_cache->e = utils->malloc(reauth_cache->size * 4662 sizeof(reauth_entry_t)); 4663 if (reauth_cache->e == NULL) 4664 return SASL_NOMEM; 4665 memset(reauth_cache->e, 0, reauth_cache->size * sizeof(reauth_entry_t)); 4666 4667 ((digest_glob_context_t *) digestmd5_client_plugins[0].glob_context)->reauth = reauth_cache; 4668 4669 *out_version = SASL_CLIENT_PLUG_VERSION; 4670 *pluglist = digestmd5_client_plugins; 4671 *plugcount = 1; 4672 4673 return SASL_OK; 4674} 4675