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