1 /* $NetBSD: mech_digestmd5.c,v 1.15 2025/12/17 15:58:36 nia Exp $ */ 2 3 /* Copyright (c) 2010 The NetBSD Foundation, Inc. 4 * All rights reserved. 5 * 6 * This code is derived from software contributed to The NetBSD Foundation 7 * by Mateusz Kocielski. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. Neither the name of The NetBSD Foundation nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 * POSSIBILITY OF SUCH DAMAGE. 32 */ 33 #include <sys/cdefs.h> 34 __RCSID("$NetBSD: mech_digestmd5.c,v 1.15 2025/12/17 15:58:36 nia Exp $"); 35 36 #include <sys/param.h> 37 38 #include <assert.h> 39 #include <endian.h> 40 #include <ctype.h> 41 #include <md5.h> 42 #include <saslc.h> 43 #include <stdbool.h> 44 #include <stdio.h> 45 #include <string.h> 46 47 #include <openssl/evp.h> 48 49 #include "buffer.h" 50 #include "crypto.h" 51 #include "error.h" 52 #include "list.h" 53 #include "mech.h" 54 #include "msg.h" 55 #include "saslc_private.h" 56 57 /* See RFC 2831. */ 58 59 /* 60 * TODO: 61 * 62 * 1) Add support for Subsequent Authentication (see RFC 2831 section 2.2). 63 */ 64 65 /* properties */ 66 #define SASLC_DIGESTMD5_AUTHCID SASLC_PROP_AUTHCID 67 #define SASLC_DIGESTMD5_AUTHZID SASLC_PROP_AUTHZID 68 #define SASLC_DIGESTMD5_CIPHERMASK SASLC_PROP_CIPHERMASK 69 #define SASLC_DIGESTMD5_HOSTNAME SASLC_PROP_HOSTNAME 70 #define SASLC_DIGESTMD5_MAXBUF SASLC_PROP_MAXBUF 71 #define SASLC_DIGESTMD5_PASSWD SASLC_PROP_PASSWD 72 #define SASLC_DIGESTMD5_QOPMASK SASLC_PROP_QOPMASK 73 #define SASLC_DIGESTMD5_REALM SASLC_PROP_REALM 74 #define SASLC_DIGESTMD5_SERVICE SASLC_PROP_SERVICE 75 #define SASLC_DIGESTMD5_SERVNAME SASLC_PROP_SERVNAME 76 /* 77 * XXX: define this if you want to be able to set a fixed cnonce for 78 * debugging purposes. 79 */ 80 #define SASLC_DIGESTMD5_CNONCE "CNONCE" 81 /* 82 * XXX: define this if you want to test the saslc_sess_encode() and 83 * saslc_sess_decode() routines against themselves, i.e., have them 84 * use the same key. 85 */ 86 #define SASLC_DIGESTMD5_SELFTEST "SELFTEST" 87 88 #define DEFAULT_QOP_MASK (F_QOP_NONE | F_QOP_INT | F_QOP_CONF) 89 #define DEFAULT_CIPHER_MASK (F_CIPHER_DES | F_CIPHER_3DES | \ 90 F_CIPHER_RC4 | F_CIPHER_RC4_40 | \ 91 F_CIPHER_RC4_56 | F_CIPHER_AES) 92 93 #define DEFAULT_MAXBUF 0x10000 94 #define MAX_MAXBUF 0xffffff 95 #define INVALID_MAXBUF(m) ((m) <= sizeof(md5hash_t) && (m) > MAX_MAXBUF) 96 97 #define NONCE_LEN 33 /* Minimum recommended length is 64bits (rfc2831). 98 cyrus-sasl uses 33 bytes. */ 99 100 typedef enum { 101 CHALLENGE_IGNORE = -1, /* must be -1 */ 102 CHALLENGE_REALM = 0, 103 CHALLENGE_NONCE = 1, 104 CHALLENGE_QOP = 2, 105 CHALLENGE_STALE = 3, 106 CHALLENGE_MAXBUF = 4, 107 CHALLENGE_CHARSET = 5, 108 CHALLENGE_ALGORITHM = 6, 109 CHALLENGE_CIPHER = 7 110 } challenge_t; 111 112 typedef enum { 113 /* 114 * NB: Values used to index cipher_tbl[] and cipher_ctx_tbl[] 115 * in cipher_context_create(). 116 */ 117 CIPHER_DES = 0, 118 CIPHER_3DES = 1, 119 CIPHER_RC4 = 2, 120 CIPHER_RC4_40 = 3, 121 CIPHER_RC4_56 = 4, 122 CIPHER_AES = 5 123 } cipher_t; 124 125 #define F_CIPHER_DES (1 << CIPHER_DES) 126 #define F_CIPHER_3DES (1 << CIPHER_3DES) 127 #define F_CIPHER_RC4 (1 << CIPHER_RC4) 128 #define F_CIPHER_RC4_40 (1 << CIPHER_RC4_40) 129 #define F_CIPHER_RC4_56 (1 << CIPHER_RC4_56) 130 #define F_CIPHER_AES (1 << CIPHER_AES) 131 132 static const named_flag_t cipher_tbl[] = { 133 /* NB: to be indexed by cipher_t values */ 134 { "des", F_CIPHER_DES }, 135 { "3des", F_CIPHER_3DES }, 136 { "rc4", F_CIPHER_RC4 }, 137 { "rc4-40", F_CIPHER_RC4_40 }, 138 { "rc4-56", F_CIPHER_RC4_56 }, 139 { "aes", F_CIPHER_AES }, 140 { NULL, 0 } 141 }; 142 143 static inline const char * 144 cipher_name(cipher_t cipher) 145 { 146 147 assert(cipher < __arraycount(cipher_tbl) - 1); /* NULL terminated */ 148 if (cipher < __arraycount(cipher_tbl) - 1) 149 return cipher_tbl[cipher].name; 150 return NULL; 151 } 152 153 static inline unsigned int 154 cipher_list_flags(list_t *list) 155 { 156 157 return saslc__list_flags(list, cipher_tbl); 158 } 159 160 typedef struct { /* data parsed from challenge */ 161 bool utf8; 162 bool algorithm; 163 bool stale; 164 char * nonce; 165 list_t * realm; 166 uint32_t cipher_flags; 167 uint32_t qop_flags; 168 size_t maxbuf; 169 } cdata_t; 170 171 typedef struct { /* response data */ 172 /* NB: the qop is in saslc__mech_sess_t */ 173 char *authcid; 174 char *authzid; 175 char *cnonce; 176 char *digesturi; 177 char *passwd; 178 char *realm; 179 cipher_t cipher; 180 int nonce_cnt; 181 size_t maxbuf; 182 } rdata_t; 183 184 typedef uint8_t md5hash_t[MD5_DIGEST_LENGTH]; 185 186 typedef struct { 187 md5hash_t kic; /* client->server integrity key */ 188 md5hash_t kis; /* server->client integrity key */ 189 md5hash_t kcc; /* client->server confidentiality key */ 190 md5hash_t kcs; /* server->client confidentiality key */ 191 } keys_t; 192 193 typedef struct cipher_context_t { 194 size_t blksize; /* block size for cipher */ 195 EVP_CIPHER_CTX *evp_ctx; /* openssl EVP context */ 196 } cipher_context_t; 197 198 typedef struct coder_context_t { 199 uint8_t *key; /* key for coding */ 200 uint32_t seqnum; /* 4 byte sequence number */ 201 202 void *buf_ctx; /* buffer context */ 203 cipher_context_t *cph_ctx; /* cipher context */ 204 saslc_sess_t *sess; /* session: for error setting */ 205 } coder_context_t; 206 207 /* mech state */ 208 typedef struct { 209 saslc__mech_sess_t mech_sess; /* must be first */ 210 cdata_t cdata; /* data parsed from challenge string */ 211 rdata_t rdata; /* data used for response string */ 212 keys_t keys; /* keys */ 213 coder_context_t dec_ctx; /* decode context */ 214 coder_context_t enc_ctx; /* encode context */ 215 } saslc__mech_digestmd5_sess_t; 216 217 /** 218 * @brief if possible convert a UTF-8 string to a ISO8859-1 string. 219 * @param utf8 original UTF-8 string. 220 * @param iso8859 pointer to pointer to the malloced ISO8859-1 string. 221 * @return -1 if the string cannot be translated. 222 * 223 * NOTE: this allocates memory for its output and the caller is 224 * responsible for freeing it. 225 */ 226 static int 227 utf8_to_8859_1(char *utf8, char **iso8859) 228 { 229 unsigned char *s, *d, *end, *src; 230 size_t cnt; 231 232 src = (unsigned char *)utf8; 233 cnt = 0; 234 end = src + strlen(utf8); 235 for (s = src; s < end; ++s) { 236 if (*s > 0xC3) /* abort if outside 8859-1 range */ 237 return -1; 238 /* 239 * Look for valid 2 byte UTF-8 encoding with, 8 bits 240 * of info. Quit if invalid pair found. 241 */ 242 if (*s >= 0xC0 && *s <= 0xC3) { /* 2 bytes, 8 bits */ 243 if (++s == end || *s < 0x80 || *s > 0xBF) 244 return -1; /* broken utf-8 encoding */ 245 } 246 cnt++; 247 } 248 249 /* Allocate adequate space. */ 250 d = malloc(cnt + 1); 251 if (d == NULL) 252 return -1; 253 254 *iso8859 = (char *)d; 255 256 /* convert to 8859-1 */ 257 do { 258 for (s = src; s < end && *s < 0xC0; ++s) 259 *d++ = *s; 260 if (s + 1 >= end) 261 break; 262 *d++ = ((s[0] & 0x3) << 6) | (s[1] & 0x3f); 263 src = s + 2; 264 } while (src < end); 265 266 *d = '\0'; 267 return 0; 268 } 269 270 /** 271 * @brief unquote a string by removing escapes. 272 * @param str string to unquote. 273 * @return NULL on failure 274 * 275 * NOTE: this allocates memory for its output and the caller is 276 * responsible for freeing it. 277 */ 278 static char * 279 unq(const char *str) 280 { 281 const char *s; 282 char *unq_str, *d; 283 int escaped; 284 285 unq_str = malloc(strlen(str) + 1); 286 if (unq_str == NULL) 287 return NULL; 288 289 escaped = 0; 290 d = unq_str; 291 for (s = str; *s != '\0'; s++) { 292 switch (*s) { 293 case '\\': 294 if (escaped) 295 *d++ = *s; 296 escaped = !escaped; 297 break; 298 default: 299 *d++ = *s; 300 escaped = 0; 301 } 302 } 303 *d = '\0'; 304 305 return unq_str; 306 } 307 308 /** 309 * @brief computing MD5(username:realm:password). 310 * @param ms mechanism session 311 * @param buf buffer for hash 312 * @return 0 on success, -1 on failure 313 */ 314 static int 315 saslc__mech_digestmd5_userhash(saslc__mech_digestmd5_sess_t *ms, uint8_t *buf) 316 { 317 char *tmp; 318 char *unq_username, *unq_realm; 319 ssize_t len; 320 321 if ((unq_username = unq(ms->rdata.authcid)) == NULL) 322 return -1; 323 324 /********************************************************/ 325 /* RFC 2831 section 2.1.2 */ 326 /* ... If the directive is missing, "realm-value" will */ 327 /* set to the empty string when computing A1. */ 328 /********************************************************/ 329 if (ms->rdata.realm == NULL) 330 unq_realm = strdup(""); 331 else 332 unq_realm = unq(ms->rdata.realm); 333 334 if (unq_realm == NULL) { 335 free(unq_username); 336 return -1; 337 } 338 len = asprintf(&tmp, "%s:%s:%s", 339 unq_username, unq_realm, ms->rdata.passwd); 340 free(unq_realm); 341 free(unq_username); 342 343 if (len == -1) 344 return -1; 345 346 saslc__crypto_md5_hash(tmp, (size_t)len, buf); 347 memset(tmp, 0, (size_t)len); 348 free(tmp); 349 return 0; 350 } 351 352 /** 353 * @brief setup the appropriate QOP keys as determined by the chosen 354 * QOP type (see RFC2831 sections 2.3 and 2.4). 355 * @param ms mechanism session 356 * @param a1hash MD5(a1) 357 * @return 0 on success, -1 on failure 358 */ 359 static int 360 setup_qop_keys(saslc__mech_digestmd5_sess_t *ms, md5hash_t a1hash) 361 { 362 #define KIC_MAGIC "Digest session key to client-to-server signing key magic constant" 363 #define KIS_MAGIC "Digest session key to server-to-client signing key magic constant" 364 #define KCC_MAGIC "Digest H(A1) to client-to-server sealing key magic constant" 365 #define KCS_MAGIC "Digest H(A1) to server-to-client sealing key magic constant" 366 #define KIC_MAGIC_LEN (sizeof(KIC_MAGIC) - 1) 367 #define KIS_MAGIC_LEN (sizeof(KIS_MAGIC) - 1) 368 #define KCC_MAGIC_LEN (sizeof(KCC_MAGIC) - 1) 369 #define KCS_MAGIC_LEN (sizeof(KCS_MAGIC) - 1) 370 #define MAX_MAGIC_LEN KIC_MAGIC_LEN 371 372 char buf[MD5_DIGEST_LENGTH + MAX_MAGIC_LEN]; 373 size_t buflen; 374 size_t n; 375 376 switch (ms->mech_sess.qop) { 377 case QOP_NONE: 378 /* nothing to do */ 379 break; 380 381 case QOP_CONF: 382 /*************************************************************************/ 383 /* See RFC2831 section 2.4 (Confidentiality Protection) */ 384 /* */ 385 /* The key for confidentiality protecting messages from client to server */ 386 /* is: */ 387 /* */ 388 /* Kcc = MD5({H(A1)[0..n], */ 389 /* "Digest H(A1) to client-to-server sealing key magic constant"}) */ 390 /* */ 391 /* The key for confidentiality protecting messages from server to client */ 392 /* is: */ 393 /* */ 394 /* Kcs = MD5({H(A1)[0..n], */ 395 /* "Digest H(A1) to server-to-client sealing key magic constant"}) */ 396 /* */ 397 /* where MD5 is as specified in [RFC 1321]. For cipher "rc4-40" n is 5; */ 398 /* for "rc4-56" n is 7; for the rest n is 16. */ 399 /*************************************************************************/ 400 401 switch (ms->rdata.cipher) { 402 case CIPHER_RC4_40: n = 5; break; 403 case CIPHER_RC4_56: n = 7; break; 404 default: n = MD5_DIGEST_LENGTH; break; 405 } 406 memcpy(buf, a1hash, n); 407 408 memcpy(buf + n, KCC_MAGIC, KCC_MAGIC_LEN); 409 buflen = n + KCC_MAGIC_LEN; 410 saslc__crypto_md5_hash(buf, buflen, ms->keys.kcc); 411 412 memcpy(buf + n, KCS_MAGIC, KCS_MAGIC_LEN); 413 buflen = n + KCS_MAGIC_LEN; 414 saslc__crypto_md5_hash(buf, buflen, ms->keys.kcs); 415 416 /*FALLTHROUGH*/ 417 418 case QOP_INT: 419 /*************************************************************************/ 420 /* See RFC2831 section 2.3 (Integrity Protection) */ 421 /* The key for integrity protecting messages from client to server is: */ 422 /* */ 423 /* Kic = MD5({H(A1), */ 424 /* "Digest session key to client-to-server signing key magic constant"}) */ 425 /* */ 426 /* The key for integrity protecting messages from server to client is: */ 427 /* */ 428 /* Kis = MD5({H(A1), */ 429 /* "Digest session key to server-to-client signing key magic constant"}) */ 430 /*************************************************************************/ 431 memcpy(buf, a1hash, MD5_DIGEST_LENGTH); 432 433 memcpy(buf + MD5_DIGEST_LENGTH, KIC_MAGIC, KIC_MAGIC_LEN); 434 buflen = MD5_DIGEST_LENGTH + KIC_MAGIC_LEN; 435 saslc__crypto_md5_hash(buf, buflen, ms->keys.kic); 436 437 memcpy(buf + MD5_DIGEST_LENGTH, KIS_MAGIC, KIS_MAGIC_LEN); 438 buflen = MD5_DIGEST_LENGTH + KIS_MAGIC_LEN; 439 saslc__crypto_md5_hash(buf, buflen, ms->keys.kis); 440 break; 441 } 442 return 0; 443 444 #undef KIC_MAGIC 445 #undef KIS_MAGIC 446 #undef KCC_MAGIC 447 #undef KCS_MAGIC 448 #undef KIC_MAGIC_LEN 449 #undef KIS_MAGIC_LEN 450 #undef KCC_MAGIC_LEN 451 #undef KCS_MAGIC_LEN 452 #undef MAX_MAGIC_LEN 453 } 454 455 /** 456 * @brief computes A1 hash value (see: RFC2831) 457 * @param ms mechanism session 458 * @return hash in hex form 459 */ 460 static char * 461 saslc__mech_digestmd5_a1(saslc__mech_digestmd5_sess_t *ms) 462 { 463 char *tmp1, *tmp2, *r; 464 char *unq_authzid; 465 md5hash_t a1hash, userhash; 466 int plen; 467 size_t len; 468 /*****************************************************************************/ 469 /* If authzid is specified, then A1 is */ 470 /* */ 471 /* A1 = { H({ unq(username-value), ":", unq(realm-value), ":", passwd }), */ 472 /* ":", nonce-value, ":", cnonce-value, ":", unq(authzid-value) } */ 473 /* */ 474 /* If authzid is not specified, then A1 is */ 475 /* */ 476 /* A1 = { H({ unq(username-value), ":", unq(realm-value), ":", passwd }), */ 477 /* ":", nonce-value, ":", cnonce-value } */ 478 /*****************************************************************************/ 479 480 if (saslc__mech_digestmd5_userhash(ms, userhash) == -1) 481 return NULL; 482 483 if (ms->rdata.authzid == NULL) 484 plen = asprintf(&tmp1, ":%s:%s", 485 ms->cdata.nonce, ms->rdata.cnonce); 486 else { 487 if ((unq_authzid = unq(ms->rdata.authzid)) == NULL) 488 return NULL; 489 490 plen = asprintf(&tmp1, ":%s:%s:%s", 491 ms->cdata.nonce, ms->rdata.cnonce, unq_authzid); 492 free(unq_authzid); 493 } 494 if (plen == -1) 495 return NULL; 496 len = plen; 497 498 tmp2 = malloc(MD5_DIGEST_LENGTH + len); 499 if (tmp2 == NULL) { 500 free(tmp1); 501 return NULL; 502 } 503 memcpy(tmp2, userhash, MD5_DIGEST_LENGTH); 504 memcpy(tmp2 + MD5_DIGEST_LENGTH, tmp1, len); 505 free(tmp1); 506 507 saslc__crypto_md5_hash(tmp2, MD5_DIGEST_LENGTH + len, a1hash); 508 free(tmp2); 509 510 r = saslc__crypto_hash_to_hex(a1hash); 511 setup_qop_keys(ms, a1hash); 512 return r; 513 } 514 515 /** 516 * @brief computes A2 hash value (see: RFC2831) 517 * @param ms mechanism session 518 * @param method string indicating method "AUTHENTICATE" or "" 519 * @return hash converted to ascii 520 */ 521 static char * 522 saslc__mech_digestmd5_a2(saslc__mech_digestmd5_sess_t *ms, 523 const char *method) 524 { 525 char *tmp, *r; 526 int rval; 527 /*****************************************************************/ 528 /* If the "qop" directive's value is "auth", then A2 is: */ 529 /* */ 530 /* A2 = { "AUTHENTICATE:", digest-uri-value } */ 531 /* */ 532 /* If the "qop" value is "auth-int" or "auth-conf" then A2 is: */ 533 /* */ 534 /* A2 = { "AUTHENTICATE:", digest-uri-value, */ 535 /* ":00000000000000000000000000000000" } */ 536 /*****************************************************************/ 537 538 rval = -1; 539 switch(ms->mech_sess.qop) { 540 case QOP_NONE: 541 rval = asprintf(&tmp, "%s:%s", method, 542 ms->rdata.digesturi); 543 break; 544 case QOP_INT: 545 case QOP_CONF: 546 rval = asprintf(&tmp, 547 "%s:%s:00000000000000000000000000000000", 548 method, ms->rdata.digesturi); 549 break; 550 } 551 if (rval == -1) 552 return NULL; 553 554 r = saslc__crypto_md5_hex(tmp, strlen(tmp)); 555 free(tmp); 556 return r; 557 } 558 559 /** 560 * @brief computes result hash. 561 * @param ms mechanism session 562 * @param a1 A1 hash value 563 * @param a2 A2 hash value 564 * @return hash converted to ascii, NULL on failure. 565 */ 566 static char * 567 saslc__mech_digestmd5_rhash(saslc__mech_digestmd5_sess_t *ms, 568 const char *a1, const char *a2) 569 { 570 char *tmp, *r; 571 /******************************************************************/ 572 /* response-value = */ 573 /* HEX( KD ( HEX(H(A1)), */ 574 /* { nonce-value, ":" nc-value, ":", */ 575 /* cnonce-value, ":", qop-value, ":", HEX(H(A2)) })) */ 576 /******************************************************************/ 577 578 if (asprintf(&tmp, "%s:%s:%08x:%s:%s:%s", a1, ms->cdata.nonce, 579 ms->rdata.nonce_cnt, ms->rdata.cnonce, 580 saslc__mech_qop_name(ms->mech_sess.qop), a2) 581 == -1) 582 return NULL; 583 584 r = saslc__crypto_md5_hex(tmp, strlen(tmp)); 585 free(tmp); 586 return r; 587 } 588 589 /** 590 * @brief building response string. Basing on 591 * session and mechanism properties. 592 * @param ms mechanism session 593 * @param method string indicating method: "AUTHENTICATE" or "" 594 * @return response string, NULL on failure. 595 */ 596 static char * 597 saslc__mech_digestmd5_response(saslc__mech_digestmd5_sess_t *ms, 598 const char *method) 599 { 600 char *r, *a1, *a2; 601 602 /******************************************************************/ 603 /* charset = "charset" "=" "utf-8" */ 604 /* */ 605 /* This directive, if present, specifies that the client has used */ 606 /* UTF-8 [UTF-8] encoding for the username, realm and */ 607 /* password. If present, the username, realm and password are in */ 608 /* Unicode, prepared using the "SASLPrep" profile [SASLPrep] of */ 609 /* the "stringprep" algorithm [StringPrep] and than encoded as */ 610 /* UTF-8 [UTF-8]. If not present, the username and password must */ 611 /* be encoded in ISO 8859-1 [ISO-8859] (of which US-ASCII */ 612 /* [USASCII] is a subset). The client should send this directive */ 613 /* only if the server has indicated it supports UTF-8 */ 614 /* [UTF-8]. The directive is needed for backwards compatibility */ 615 /* with HTTP Digest, which only supports ISO 8859-1. */ 616 /******************************************************************/ 617 /* 618 * NOTE: We don't set charset in the response, so this is not 619 * an issue here. However, see the note in stringprep_realms() 620 * which is called when processing the challenge. 621 */ 622 /******************************************************************/ 623 /* response-value = */ 624 /* HEX( KD ( HEX(H(A1)), */ 625 /* { nonce-value, ":" nc-value, ":", */ 626 /* cnonce-value, ":", qop-value, ":", HEX(H(A2)) })) */ 627 /******************************************************************/ 628 629 r = NULL; 630 631 a1 = saslc__mech_digestmd5_a1(ms); 632 if (a1 == NULL) 633 return NULL; 634 635 a2 = saslc__mech_digestmd5_a2(ms, method); 636 if (a2 != NULL) { 637 r = saslc__mech_digestmd5_rhash(ms, a1, a2); 638 free(a2); 639 } 640 free(a1); 641 return r; 642 } 643 644 /** 645 * @brief Choose a string from a user provided host qualified list, 646 * i.e., a comma delimited list with possible hostname qualifiers on 647 * the elements. 648 * @param hqlist a comma delimited list with entries of the form 649 * "[hostname:]string". 650 * @param hostname the hostname to use in the selection. 651 * @param rval pointer to location for returned string. Set to NULL 652 * if none found, otherwise set to strdup(3) of the string found. 653 * @return 0 on success, -1 on failure (no memory). 654 * 655 * NOTE: hqlist and rval must not be NULL. 656 * NOTE: this allocates memory for its output and the caller is 657 * responsible for freeing it. 658 */ 659 static int 660 choose_from_hqlist(const char *hqlist, const char *hostname, char **rval) 661 { 662 list_t *l, *list; 663 size_t len; 664 char *p; 665 666 if (saslc__list_parse(&list, hqlist) == -1) 667 return -1; /* no memory */ 668 669 /* 670 * If the user provided a list and the caller provided a 671 * hostname, pick the first string from the list that 672 * corresponds to the hostname. 673 */ 674 if (hostname != NULL) { 675 len = strlen(hostname); 676 for (l = list; l != NULL; l = l->next) { 677 p = l->value + len; 678 if (*p != ':' || 679 strncasecmp(l->value, hostname, len) != 0) 680 continue; 681 682 if (*(++p) != '\0' && isalnum((unsigned char)*p)) { 683 if ((p = strdup(p)) == NULL) 684 goto nomem; 685 goto done; 686 } 687 } 688 } 689 /* 690 * If one couldn't be found, look for first string in the list 691 * without a hostname specifier. 692 */ 693 p = NULL; 694 for (l = list; l != NULL; l = l->next) { 695 if (strchr(l->value, ':') == NULL) { 696 if ((p = strdup(l->value)) == NULL) 697 goto nomem; 698 goto done; 699 } 700 } 701 done: 702 saslc__list_free(list); 703 *rval = p; 704 return 0; 705 nomem: 706 saslc__list_free(list); 707 return -1; 708 } 709 710 /** 711 * @brief builds digesturi string 712 * @param serv_type type of service to use, e.g., "smtp" 713 * @param host fully-qualified canonical DNS name of host 714 * @param serv_name service name if it is replicated via DNS records; may 715 * be NULL. 716 * @return digesturi string, NULL on failure. 717 */ 718 static char * 719 saslc__mech_digestmd5_digesturi(saslc_sess_t *sess, const char *serv_host) 720 { 721 const char *serv_list; 722 char *serv_name; 723 const char *serv_type; 724 char *r; 725 int rv; 726 727 serv_type = saslc_sess_getprop(sess, SASLC_DIGESTMD5_SERVICE); 728 if (serv_type == NULL) { 729 saslc__error_set(ERR(sess), ERROR_MECH, 730 "service is required for an authentication"); 731 return NULL; 732 } 733 serv_list = saslc_sess_getprop(sess, SASLC_DIGESTMD5_SERVNAME); 734 if (serv_list == NULL) 735 serv_name = NULL; 736 else if (choose_from_hqlist(serv_list, serv_host, &serv_name) == -1) 737 goto nomem; 738 739 saslc__msg_dbg("%s: serv_name='%s'", __func__, 740 serv_name ? serv_name : "<null>"); 741 742 /****************************************************************/ 743 /* digest-uri = "digest-uri" "=" <"> digest-uri-value <"> */ 744 /* digest-uri-value = serv-type "/" host [ "/" serv-name ] */ 745 /* */ 746 /* If the service is not replicated, or the serv-name is */ 747 /* identical to the host, then the serv-name component MUST be */ 748 /* omitted. The service is considered to be replicated if the */ 749 /* client's service-location process involves resolution using */ 750 /* standard DNS lookup operations, and if these operations */ 751 /* involve DNS records (such as SRV, or MX) which resolve one */ 752 /* DNS name into a set of other DNS names. */ 753 /****************************************************************/ 754 755 rv = serv_name == NULL || strcmp(serv_host, serv_name) == 0 756 ? asprintf(&r, "%s/%s", serv_type, serv_host) 757 : asprintf(&r, "%s/%s/%s", serv_type, serv_host, serv_name); 758 if (serv_name != NULL) 759 free(serv_name); 760 if (rv == -1) 761 goto nomem; 762 763 saslc__msg_dbg("%s: digest-uri='%s'", __func__, r); 764 return r; 765 nomem: 766 saslc__error_set_errno(ERR(sess), ERROR_NOMEM); 767 return NULL; 768 } 769 770 /** 771 * @brief creates client's nonce. (Basing on crypto.h) 772 * @param s length of nonce 773 * @return nonce string, NULL on failure. 774 */ 775 static char * 776 saslc__mech_digestmd5_nonce(size_t s) 777 { 778 char *nonce; 779 char *r; 780 781 nonce = saslc__crypto_nonce(s); 782 if (nonce == NULL) 783 return NULL; 784 785 if (saslc__crypto_encode_base64(nonce, s, &r, NULL) == -1) 786 return NULL; 787 free(nonce); 788 789 return r; 790 } 791 792 /** 793 * @brief strip quotes from a string (modifies the string) 794 * @param str the string 795 * @return string without quotes. 796 */ 797 static char * 798 strip_quotes(char *str) 799 { 800 char *p; 801 size_t len; 802 803 if (*str != '"') 804 return str; 805 806 len = strlen(str); 807 p = str + len; 808 if (len < 2 || p[-1] != '"') 809 return str; 810 811 p[-1] = '\0'; 812 return ++str; 813 } 814 815 /** 816 * @brief convert a list of realms from utf-8 to iso8859-q if necessary. 817 * @param is_utf8 the characterset of the realms (true if utf8) 818 * @param realms the realm list 819 */ 820 static int 821 stringprep_realms(bool is_utf8, list_t *realms) 822 { 823 list_t *l; 824 char *utf8, *iso8859; 825 826 /******************************************************************/ 827 /* If at least one realm is present and the charset directive is */ 828 /* also specified (which means that realm(s) are encoded as */ 829 /* UTF-8), the client should prepare each instance of realm using */ 830 /* the "SASLPrep" profile [SASLPrep] of the "stringprep" */ 831 /* algorithm [StringPrep]. If preparation of a realm instance */ 832 /* fails or results in an empty string, the client should abort */ 833 /* the authentication exchange. */ 834 /******************************************************************/ 835 if (!is_utf8) 836 return 0; 837 838 for (l = realms; l != NULL; l = l->next) { 839 utf8 = l->value; 840 if (utf8_to_8859_1(utf8, &iso8859) == -1) 841 return -1; 842 free(utf8); 843 l->value = iso8859; 844 } 845 return 0; 846 } 847 848 /** 849 * @brief choose a realm from a list of possible realms provided by the server 850 * @param sess the session context 851 * @param realms the list of realms 852 * @return our choice of realm or NULL on failure. It is the user's 853 * responsibility to free the memory allocated for the return string. 854 */ 855 static char * 856 choose_realm(saslc_sess_t *sess, const char *hostname, list_t *realms) 857 { 858 const char *user_realms; 859 list_t *l; 860 char *p = NULL; 861 862 /*****************************************************************/ 863 /* The realm containing the user's account. This directive is */ 864 /* required if the server provided any realms in the */ 865 /* "digest-challenge", in which case it may appear exactly once */ 866 /* and its value SHOULD be one of those realms. If the directive */ 867 /* is missing, "realm-value" will set to the empty string when */ 868 /* computing A1 (see below for details). */ 869 /*****************************************************************/ 870 871 user_realms = saslc_sess_getprop(sess, SASLC_DIGESTMD5_REALM); 872 873 /* 874 * If the challenge provided no realms, try to pick one from a 875 * user specified list, which may be keyed by the hostname. 876 * If one can't be found, return NULL; 877 */ 878 if (realms == NULL) { 879 /* 880 * No realm was supplied in challenge. Figure out a 881 * plausable default. 882 */ 883 if (user_realms == NULL) { 884 saslc__error_set(ERR(sess), ERROR_MECH, 885 "cannot determine the realm"); 886 return NULL; 887 } 888 if (choose_from_hqlist(user_realms, hostname, &p) == -1) 889 goto nomem; 890 891 if (p == NULL) 892 saslc__error_set(ERR(sess), ERROR_MECH, 893 "cannot choose a realm"); 894 return p; 895 } 896 897 /************************************************************/ 898 /* Multiple realm directives are allowed, in which case the */ 899 /* user or client must choose one as the realm for which to */ 900 /* supply to username and password. */ 901 /************************************************************/ 902 /* 903 * If the user hasn't specified any realms, or we can't find 904 * one from the user provided list, just take the first realm 905 * from the challenge. 906 */ 907 if (user_realms == NULL) 908 goto use_1st_realm; 909 910 if (choose_from_hqlist(user_realms, hostname, &p) == -1) 911 goto nomem; 912 913 if (p == NULL) 914 goto use_1st_realm; 915 916 /* 917 * If we found a matching user provide realm, make sure it is 918 * on the list of realms. If it isn't, just take the first 919 * realm in the challenge. 920 */ 921 for (l = realms; l != NULL; l = l->next) { 922 if (strcasecmp(p, l->value) == 0) 923 return p; 924 } 925 use_1st_realm: 926 free(p); 927 if ((p = strdup(realms->value)) == NULL) 928 goto nomem; 929 return p; 930 nomem: 931 saslc__error_set_errno(ERR(sess), ERROR_NOMEM); 932 return NULL; 933 } 934 935 /** 936 * @brief destroy a cipher context 937 * @param ctx cipher context 938 * @return nothing 939 */ 940 static void 941 cipher_context_destroy(cipher_context_t *ctx) 942 { 943 944 if (ctx != NULL) { 945 if (ctx->evp_ctx != NULL) 946 EVP_CIPHER_CTX_free(ctx->evp_ctx); 947 free(ctx); 948 } 949 } 950 951 /** 952 * @brief slide the bits from 7 bytes into the high 7 bits of 8 bites 953 * @param ikey input key 954 * @param okey output key 955 * 956 * This matches cyrus-sasl 2.1.23 957 */ 958 static inline void 959 slidebits(uint8_t *ikey, uint8_t *okey) 960 { 961 962 okey[0] = ikey[0] << 0; 963 okey[1] = ikey[0] << 7 | (unsigned)ikey[1] >> 1; 964 okey[2] = ikey[1] << 6 | (unsigned)ikey[2] >> 2; 965 okey[3] = ikey[2] << 5 | (unsigned)ikey[3] >> 3; 966 okey[4] = ikey[3] << 4 | (unsigned)ikey[4] >> 4; 967 okey[5] = ikey[4] << 3 | (unsigned)ikey[5] >> 5; 968 okey[6] = ikey[5] << 2 | (unsigned)ikey[6] >> 6; 969 okey[7] = ikey[6] << 1; 970 } 971 972 /** 973 * @brief convert our key to a DES key 974 * @param key our key 975 * @param keylen our key length 976 * @param deskey the key in DES format 977 * 978 * NOTE: The openssl implementations of "des" and "3des" expect their 979 * keys to be in the high 7 bits of 8 bytes and 16 bytes, 980 * respectively. Thus, our key length will be 7 and 14 bytes, 981 * respectively. 982 */ 983 static void 984 make_deskey(uint8_t *key, size_t keylen, uint8_t *deskey) 985 { 986 987 assert(keylen == 7 || keylen == 14); 988 989 slidebits(deskey + 0, key + 0); 990 if (keylen == 14) 991 slidebits(deskey + 7, key + 7); 992 } 993 994 /** 995 * @brief create a cipher context, including EVP cipher initialization. 996 * @param sess session context 997 * @param cipher cipher to use 998 * @param do_enc encode context if set, decode context if 0 999 * @param key crypt key to use 1000 * @return cipher context, or NULL on error 1001 */ 1002 static cipher_context_t * 1003 cipher_context_create(saslc_sess_t *sess, cipher_t cipher, int do_enc, uint8_t *key) 1004 { 1005 #define AES_IV_MAGIC "aes-128" 1006 #define AES_IV_MAGIC_LEN (sizeof(AES_IV_MAGIC) - 1) 1007 static const struct cipher_ctx_tbl_s { 1008 cipher_t eval; /* for error checking */ 1009 const EVP_CIPHER *(*evp_type)(void);/* type of cipher */ 1010 size_t keylen; /* key length */ 1011 ssize_t blksize; /* block size for cipher */ 1012 size_t ivlen; /* initial value length */ 1013 } cipher_ctx_tbl[] = { 1014 /* NB: table indexed by cipher_t */ 1015 /* eval evp_type keylen blksize ivlen */ 1016 { CIPHER_DES, EVP_des_cbc, 7, 8, 8 }, 1017 { CIPHER_3DES, EVP_des_ede_cbc, 14, 8, 8 }, 1018 { CIPHER_RC4, EVP_rc4, 16, 1, 0 }, 1019 { CIPHER_RC4_40, EVP_rc4, 5, 1, 0 }, 1020 { CIPHER_RC4_56, EVP_rc4, 7, 1, 0 }, 1021 { CIPHER_AES, EVP_aes_128_cbc, 16, 16, 16 } 1022 }; 1023 const struct cipher_ctx_tbl_s *ctp; 1024 char buf[sizeof(md5hash_t) + AES_IV_MAGIC_LEN]; 1025 uint8_t deskey[16]; 1026 md5hash_t aes_iv; /* initial value buffer for aes */ 1027 cipher_context_t *ctx; /* cipher context */ 1028 uint8_t *ivp; 1029 const char *errmsg; 1030 int rv; 1031 1032 /*************************************************************************/ 1033 /* See draft-ietf-sasl-rfc2831bis-02.txt section 2.4 (mentions "aes") */ 1034 /* The key for the "rc4" and "aes" ciphers is all 16 bytes of Kcc or Kcs.*/ 1035 /* The key for the "rc4-40" cipher is the first 5 bytes of Kcc or Kcs. */ 1036 /* The key for the "rc4-56" is the first 7 bytes of Kcc or Kcs. */ 1037 /* The key for "des" is the first 7 bytes of Kcc or Kcs. */ 1038 /* The key for "3des" is the first 14 bytes of Kcc or Kcs. */ 1039 /* */ 1040 /* The IV used to send/receive the initial buffer of security encoded */ 1041 /* data for "des" and "3des" is the last 8 bytes of Kcc or Kcs. For all */ 1042 /* subsequent buffers the last 8 bytes of the ciphertext of the buffer */ 1043 /* NNN is used as the IV for the buffer (NNN + 1). */ 1044 /* */ 1045 /* The IV for the "aes" cipher in CBC mode for messages going from the */ 1046 /* client to the server (IVc) consists of 16 bytes calculated as */ 1047 /* follows: IVc = MD5({Kcc, "aes-128"}) */ 1048 /* */ 1049 /* The IV for the "aes" cipher in CBC mode for messages going from the */ 1050 /* server to the client (IVs) consists of 16 bytes calculated as */ 1051 /* follows: IVs = MD5({Kcs, "aes-128"}) */ 1052 /*************************************************************************/ 1053 1054 assert(cipher < __arraycount(cipher_ctx_tbl)); 1055 if (cipher >= __arraycount(cipher_ctx_tbl)) { 1056 saslc__error_set_errno(ERR(sess), ERROR_BADARG); 1057 return NULL; 1058 } 1059 1060 ctx = malloc(sizeof(*ctx)); 1061 if (ctx == NULL) { 1062 saslc__error_set_errno(ERR(sess), ERROR_NOMEM); 1063 return NULL; 1064 } 1065 1066 ctp = &cipher_ctx_tbl[cipher]; 1067 assert(ctp->eval == cipher); 1068 1069 ctx->blksize = ctp->blksize; 1070 1071 ctx->evp_ctx = EVP_CIPHER_CTX_new(); 1072 if (ctx->evp_ctx == NULL) { 1073 errmsg = "EVP_CIPHER_CTX_new failed"; 1074 goto err; 1075 } 1076 if (EVP_CipherInit_ex(ctx->evp_ctx, ctp->evp_type(), NULL, NULL, NULL, 1077 do_enc) == 0) { 1078 errmsg = "EVP_CipherInit_ex failed"; 1079 goto err; 1080 } 1081 if (EVP_CIPHER_CTX_set_padding(ctx->evp_ctx, 0) == 0) { 1082 errmsg = "EVP_CIPHER_CTX_set_padding failed"; 1083 goto err; 1084 } 1085 ivp = NULL; 1086 switch (cipher) { /* prepare key and IV */ 1087 case CIPHER_RC4: 1088 case CIPHER_RC4_40: 1089 case CIPHER_RC4_56: 1090 assert(ctp->ivlen == 0); /* no IV */ 1091 rv = EVP_CIPHER_CTX_set_key_length(ctx->evp_ctx, 1092 (int)ctp->keylen); 1093 if (rv == 0) { 1094 errmsg = "EVP_CIPHER_CTX_set_key_length failed"; 1095 goto err; 1096 } 1097 break; 1098 case CIPHER_DES: 1099 case CIPHER_3DES: 1100 assert(ctp->ivlen == 8); 1101 ivp = key + 8; 1102 make_deskey(key, ctp->keylen, deskey); 1103 key = deskey; 1104 break; 1105 case CIPHER_AES: 1106 assert(ctp->ivlen == 16); 1107 /* IVs = MD5({Kcs, "aes-128"}) */ 1108 memcpy(buf, key, sizeof(md5hash_t)); 1109 memcpy(buf + sizeof(md5hash_t), AES_IV_MAGIC, AES_IV_MAGIC_LEN); 1110 saslc__crypto_md5_hash(buf, sizeof(buf), aes_iv); 1111 ivp = aes_iv; 1112 break; 1113 } 1114 if (EVP_CipherInit_ex(ctx->evp_ctx, NULL, NULL, key, ivp, do_enc) == 0) { 1115 errmsg = "EVP_CipherInit_ex 2 failed"; 1116 goto err; 1117 } 1118 return ctx; 1119 err: 1120 cipher_context_destroy(ctx); 1121 saslc__error_set(ERR(sess), ERROR_MECH, errmsg); 1122 return NULL; 1123 1124 #undef AES_IV_MAGIC_LEN 1125 #undef AES_IV_MAGIC 1126 } 1127 1128 /** 1129 * @brief compute the necessary padding length 1130 * @param ctx the cipher context 1131 * @param inlen the data length to put in the packet 1132 * @return the length of padding needed (zero if none needed) 1133 */ 1134 static size_t 1135 get_padlen(cipher_context_t *ctx, size_t inlen) 1136 { 1137 size_t blksize; 1138 1139 if (ctx == NULL) 1140 return 0; 1141 1142 blksize = ctx->blksize; 1143 if (blksize == 1) 1144 return 0; 1145 1146 return blksize - ((inlen + 10) % blksize); 1147 } 1148 1149 /** 1150 * @brief compute the packet integrity including the version and 1151 * sequence number 1152 * @param key the hmac_md5 hash key 1153 * @param seqnum the sequence number 1154 * @param in the input buffer 1155 * @param inlen the input buffer length 1156 * @return 0 on success, -1 on failure 1157 */ 1158 static int 1159 packet_integrity(md5hash_t key, uint32_t seqnum, void *in, size_t inlen, 1160 md5hash_t mac) 1161 { 1162 1163 be32enc(in, seqnum); 1164 if (saslc__crypto_hmac_md5_hash(key, MD5_DIGEST_LENGTH, in, inlen, mac) 1165 == -1) 1166 return -1; 1167 1168 /* we keep only the first 10 bytes of the hash */ 1169 be16enc(mac + 10, 0x0001); /* add 2 byte version number */ 1170 be32enc(mac + 12, seqnum); /* add 4 byte sequence number */ 1171 return 0; 1172 } 1173 1174 /** 1175 * @brief encode or decode a buffer (in place) 1176 * @param ctx the cipher context 1177 * @param in the input buffer 1178 * @param inlen the buffer length 1179 * @return the length of the result left in the input buffer after 1180 * processing, or -1 on failure. 1181 */ 1182 static ssize_t 1183 cipher_update(cipher_context_t *ctx, void *in, size_t inlen) 1184 { 1185 int outl, rv; 1186 void *out; 1187 1188 out = in; /* XXX: this assumes we can encoded and decode in place */ 1189 rv = EVP_CipherUpdate(ctx->evp_ctx, out, &outl, in, (int)inlen); 1190 if (rv == 0) 1191 return -1; 1192 1193 return outl; 1194 } 1195 1196 /** 1197 * @brief incapsulate a message with confidentiality (sign and encrypt) 1198 * @param ctx coder context 1199 * @param in pointer to message to encode 1200 * @param inlen length of message 1201 * @param out encoded output packet (including prefixed 4 byte length field) 1202 * @param outlen decoded output packet length 1203 * @returns 0 on success, -1 on failure 1204 * 1205 * NOTE: this allocates memory for its output and the caller is 1206 * responsible for freeing it. 1207 * 1208 * integrity (auth-int): 1209 * len, HMAC(ki, {SeqNum, msg})[0..9], x0001, SeqNum 1210 * 1211 * confidentiality (auth-conf): 1212 * len, CIPHER(Kc, {msg, pag, HMAC(ki, {SeqNum, msg})[0..9]}), x0001, SeqNum 1213 */ 1214 static ssize_t 1215 encode_buffer(coder_context_t *ctx, const void *in, size_t inlen, 1216 void **out, size_t *outlen) 1217 { 1218 void *buf; 1219 uint8_t *mac, *p; 1220 ssize_t tmplen; 1221 size_t buflen; 1222 size_t padlen; 1223 1224 padlen = get_padlen(ctx->cph_ctx, inlen); 1225 buflen = 4 + inlen + padlen + sizeof(md5hash_t); 1226 buf = malloc(buflen); 1227 if (buf == NULL) { 1228 saslc__error_set_errno(ERR(ctx->sess), ERROR_NOMEM); 1229 return -1; 1230 } 1231 p = buf; 1232 memcpy(p + 4, in, inlen); 1233 mac = p + 4 + inlen + padlen; 1234 if (packet_integrity(ctx->key, ctx->seqnum, buf, 4 + inlen, mac) 1235 == -1) { 1236 saslc__error_set(ERR(ctx->sess), ERROR_MECH, "HMAC failed"); 1237 free(buf); 1238 return -1; 1239 } 1240 1241 if (padlen) 1242 memset(p + 4 + inlen, (int)padlen, padlen); 1243 1244 if (ctx->cph_ctx != NULL) { 1245 if ((tmplen = cipher_update(ctx->cph_ctx, p + 4, 1246 inlen + padlen + 10)) == -1) { 1247 saslc__error_set(ERR(ctx->sess), ERROR_MECH, 1248 "cipher error"); 1249 free(buf); 1250 return -1; 1251 } 1252 assert((size_t)tmplen == inlen + padlen + 10); 1253 if ((size_t)tmplen != inlen + padlen + 10) 1254 return -1; 1255 } 1256 1257 be32enc(buf, (uint32_t)(buflen - 4)); 1258 1259 *out = buf; 1260 *outlen = buflen; 1261 ctx->seqnum++; /* wraps at 2^32 */ 1262 return 0; 1263 } 1264 1265 /** 1266 * @brief decode one complete confidentiality encoded packet 1267 * @param ctx coder context 1268 * @param in pointer to packet, including the beginning 4 byte length field. 1269 * @param inlen length of packet 1270 * @param out decoded output 1271 * @param outlen decoded output length 1272 * @returns 0 on success, -1 on failure 1273 * 1274 * NOTE: this modifies the intput buffer! 1275 * NOTE: this allocates memory for its output and the caller is 1276 * responsible for freeing it. 1277 * 1278 * integrity (auth-int): 1279 * len, HMAC(ki, {SeqNum, msg})[0..9], x0001, SeqNum 1280 * 1281 * confidentiality (auth-conf): 1282 * len, CIPHER(Kc, {msg, pag, HMAC(ki, {SeqNum, msg})[0..9]}), x0001, SeqNum 1283 */ 1284 static ssize_t 1285 decode_buffer(coder_context_t *ctx, void *in, size_t inlen, 1286 void **out, size_t *outlen) 1287 { 1288 md5hash_t mac; 1289 void *buf; 1290 uint8_t *p; 1291 size_t blksize, buflen, padlen; 1292 ssize_t tmplen; 1293 uint32_t len; 1294 1295 padlen = get_padlen(ctx->cph_ctx, 1); 1296 if (inlen < 4 + 1 + padlen + MD5_DIGEST_LENGTH) { 1297 saslc__error_set(ERR(ctx->sess), ERROR_MECH, 1298 "zero payload packet"); 1299 return -1; 1300 } 1301 len = be32dec(in); 1302 if (len + 4 != inlen) { 1303 saslc__error_set(ERR(ctx->sess), ERROR_MECH, 1304 "bad packet length"); 1305 return -1; 1306 } 1307 1308 if (ctx->cph_ctx != NULL) { 1309 p = in; 1310 if ((tmplen = cipher_update(ctx->cph_ctx, p + 4, len - 6)) == -1) { 1311 saslc__error_set(ERR(ctx->sess), ERROR_MECH, 1312 "cipher error"); 1313 return -1; 1314 } 1315 assert(tmplen == (ssize_t)len - 6); 1316 if (tmplen != (ssize_t)len - 6) 1317 return -1; 1318 } 1319 1320 blksize = ctx->cph_ctx ? ctx->cph_ctx->blksize : 0; 1321 if (blksize <= 1) 1322 padlen = 0; 1323 else{ 1324 p = in; 1325 padlen = p[inlen - sizeof(md5hash_t) - 1]; 1326 if (padlen > blksize || padlen == 0) { 1327 saslc__error_set(ERR(ctx->sess), ERROR_MECH, 1328 "invalid padding length after decode"); 1329 return -1; 1330 } 1331 } 1332 if (packet_integrity(ctx->key, ctx->seqnum, in, 1333 inlen - padlen - sizeof(mac), mac) == -1) { 1334 saslc__error_set(ERR(ctx->sess), ERROR_MECH, "HMAC failed"); 1335 return -1; 1336 } 1337 1338 p = in; 1339 p += 4 + len - MD5_DIGEST_LENGTH; 1340 if (memcmp(p, mac, MD5_DIGEST_LENGTH) != 0) { 1341 uint32_t seqnum; 1342 1343 p = in; 1344 seqnum = be32dec(p + inlen - 4); 1345 saslc__error_set(ERR(ctx->sess), ERROR_MECH, 1346 seqnum != ctx->seqnum ? "invalid MAC (bad seqnum)" : 1347 "invalid MAC"); 1348 return -1; 1349 } 1350 1351 buflen = len - padlen - MD5_DIGEST_LENGTH; 1352 buf = malloc(buflen); 1353 if (buf == NULL) { 1354 saslc__error_set_errno(ERR(ctx->sess), ERROR_NOMEM); 1355 return -1; 1356 } 1357 p = in; 1358 p += 4; 1359 memcpy(buf, p, buflen); 1360 1361 *out = buf; 1362 *outlen = buflen; 1363 ctx->seqnum++; 1364 return 0; 1365 } 1366 1367 /** 1368 * @brief add integrity or confidentiality layer 1369 * @param sess session handle 1370 * @param in input buffer 1371 * @param inlen input buffer length 1372 * @param out pointer to output buffer 1373 * @param out pointer to output buffer length 1374 * @return number of bytes consumed on success, 0 if insufficient data 1375 * to process, -1 on failure 1376 */ 1377 static ssize_t 1378 saslc__mech_digestmd5_encode(saslc_sess_t *sess, const void *in, size_t inlen, 1379 void **out, size_t *outlen) 1380 { 1381 saslc__mech_digestmd5_sess_t *ms; 1382 uint8_t *buf; 1383 size_t buflen; 1384 ssize_t rval; 1385 1386 ms = sess->mech_sess; 1387 assert(ms->mech_sess.qop != QOP_NONE); 1388 if (ms->mech_sess.qop == QOP_NONE) 1389 return -1; 1390 1391 rval = saslc__buffer_fetch(ms->enc_ctx.buf_ctx, in, inlen, &buf, &buflen); 1392 if (rval == -1) 1393 return -1; 1394 if (buflen == 0) { 1395 *out = NULL; 1396 *outlen = 0; 1397 return rval; 1398 } 1399 if (encode_buffer(&ms->enc_ctx, buf, buflen, out, outlen) == -1) 1400 return -1; 1401 1402 return rval; 1403 } 1404 1405 /** 1406 * @brief remove integrity or confidentiality layer 1407 * @param sess session handle 1408 * @param in input buffer 1409 * @param inlen input buffer length 1410 * @param out pointer to output buffer 1411 * @param out pointer to output buffer length 1412 * @return number of bytes consumed on success, 0 if insufficient data 1413 * to process, -1 on failure 1414 * 1415 * integrity (auth-int): 1416 * len, HMAC(ki, {SeqNum, msg})[0..9], x0001, SeqNum 1417 * 1418 * confidentiality (auth-conf): 1419 * len, CIPHER(Kc, {msg, pag, HMAC(ki, {SeqNum, msg})[0..9]}), x0001, SeqNum 1420 */ 1421 static ssize_t 1422 saslc__mech_digestmd5_decode(saslc_sess_t *sess, const void *in, size_t inlen, 1423 void **out, size_t *outlen) 1424 { 1425 saslc__mech_digestmd5_sess_t *ms; 1426 uint8_t *buf; 1427 size_t buflen; 1428 ssize_t rval; 1429 1430 ms = sess->mech_sess; 1431 assert(ms->mech_sess.qop != QOP_NONE); 1432 if (ms->mech_sess.qop == QOP_NONE) 1433 return -1; 1434 1435 rval = saslc__buffer32_fetch(ms->dec_ctx.buf_ctx, in, inlen, &buf, &buflen); 1436 if (rval == -1) 1437 return -1; 1438 1439 if (buflen == 0) { 1440 *out = NULL; 1441 *outlen = 0; 1442 return rval; 1443 } 1444 if (decode_buffer(&ms->dec_ctx, buf, buflen, out, outlen) == -1) 1445 return -1; 1446 1447 return rval; 1448 } 1449 1450 /************************************************************************ 1451 * XXX: Share with mech_gssapi.c? They are almost identical. 1452 */ 1453 /** 1454 * @brief choose the best qop based on what was provided by the 1455 * challenge and a possible user mask. 1456 * @param sess the session context 1457 * @param qop_flags the qop flags parsed from the challenge string 1458 * @return the selected saslc__mech_sess_qop_t or -1 if no match 1459 */ 1460 static int 1461 choose_qop(saslc_sess_t *sess, uint32_t qop_flags) 1462 { 1463 list_t *list; 1464 const char *user_qop; 1465 1466 if (qop_flags == 0) /* no qop spec in challenge (it's optional) */ 1467 return QOP_NONE; 1468 1469 qop_flags &= DEFAULT_QOP_MASK; 1470 user_qop = saslc_sess_getprop(sess, SASLC_DIGESTMD5_QOPMASK); 1471 if (user_qop != NULL) { 1472 if (saslc__list_parse(&list, user_qop) == -1) { 1473 saslc__error_set_errno(ERR(sess), ERROR_NOMEM); 1474 return -1; 1475 } 1476 qop_flags &= saslc__mech_qop_list_flags(list); 1477 saslc__list_free(list); 1478 } 1479 1480 /* 1481 * Select the most secure supported qop. 1482 */ 1483 if ((qop_flags & F_QOP_CONF) != 0) 1484 return QOP_CONF; 1485 if ((qop_flags & F_QOP_INT) != 0) 1486 return QOP_INT; 1487 if ((qop_flags & F_QOP_NONE) != 0) 1488 return QOP_NONE; 1489 1490 saslc__error_set(ERR(sess), ERROR_MECH, 1491 "cannot choose an acceptable qop"); 1492 return -1; 1493 } 1494 /************************************************************************/ 1495 1496 /** 1497 * @brief choose the best cipher based on what was provided by the 1498 * challenge and a possible user mask. 1499 * @param sess the session context 1500 * @param cipher_flags the cipher flags parsed from the challenge 1501 * string 1502 * @return the selected cipher_t 1503 */ 1504 static int 1505 choose_cipher(saslc_sess_t *sess, unsigned int cipher_flags) 1506 { 1507 list_t *list; 1508 unsigned int cipher_mask; 1509 const char *user_cipher; 1510 1511 if (cipher_flags == 0) { 1512 saslc__error_set(ERR(sess), ERROR_MECH, 1513 "no cipher spec in challenge"); 1514 return -1; 1515 } 1516 cipher_mask = DEFAULT_CIPHER_MASK; 1517 user_cipher = saslc_sess_getprop(sess, SASLC_DIGESTMD5_CIPHERMASK); 1518 if (user_cipher != NULL) { 1519 if (saslc__list_parse(&list, user_cipher) == -1) { 1520 saslc__error_set_errno(ERR(sess), ERROR_NOMEM); 1521 return -1; 1522 } 1523 cipher_mask = cipher_list_flags(list); 1524 saslc__list_free(list); 1525 } 1526 cipher_flags &= cipher_mask; 1527 1528 /* 1529 * Select the most secure cipher supported. 1530 * XXX: Is the order here right? 1531 */ 1532 if ((cipher_flags & F_CIPHER_AES) != 0) 1533 return CIPHER_AES; 1534 if ((cipher_flags & F_CIPHER_3DES) != 0) 1535 return CIPHER_3DES; 1536 if ((cipher_flags & F_CIPHER_DES) != 0) 1537 return CIPHER_DES; 1538 if ((cipher_flags & F_CIPHER_RC4) != 0) 1539 return CIPHER_RC4; 1540 if ((cipher_flags & F_CIPHER_RC4_56) != 0) 1541 return CIPHER_RC4_56; 1542 if ((cipher_flags & F_CIPHER_RC4_40) != 0) 1543 return CIPHER_RC4_40; 1544 1545 saslc__error_set(ERR(sess), ERROR_MECH, 1546 "qop \"auth-conf\" requires a cipher"); 1547 return -1; 1548 } 1549 1550 /** 1551 * @brief get the challenge_t value corresponding to a challenge key 1552 * string. 1553 * @param key challenge key string 1554 * @return the challenge_t value including CHALLENGE_IGNORE (-1) if 1555 * the key is not recognized 1556 */ 1557 static challenge_t 1558 get_challenge_t(const char *key) 1559 { 1560 static const struct { 1561 const char *key; 1562 challenge_t value; 1563 } challenge_keys[] = { 1564 { "realm", CHALLENGE_REALM }, 1565 { "nonce", CHALLENGE_NONCE }, 1566 { "qop", CHALLENGE_QOP }, 1567 { "stale", CHALLENGE_STALE }, 1568 { "maxbuf", CHALLENGE_MAXBUF }, 1569 { "charset", CHALLENGE_CHARSET }, 1570 { "algorithm", CHALLENGE_ALGORITHM }, 1571 { "cipher", CHALLENGE_CIPHER } 1572 }; 1573 size_t i; 1574 1575 for (i = 0; i < __arraycount(challenge_keys); i++) { 1576 if (strcasecmp(key, challenge_keys[i].key) == 0) 1577 return challenge_keys[i].value; 1578 } 1579 return CHALLENGE_IGNORE; 1580 } 1581 1582 /** 1583 * @brief parses challenge and store result in mech_sess. 1584 * @param mech_sess session where parsed data will be stored 1585 * @param challenge challenge 1586 * @return 0 on success, -1 on failure. 1587 */ 1588 static int 1589 saslc__mech_digestmd5_parse_challenge(saslc_sess_t *sess, const char *challenge) 1590 { 1591 saslc__mech_digestmd5_sess_t *ms; 1592 list_t *list, *n; 1593 list_t *tmp_list; 1594 cdata_t *cdata; 1595 size_t maxbuf; 1596 uint32_t tmp_flags; 1597 int rv; 1598 1599 /******************************************************************/ 1600 /* digest-challenge = */ 1601 /* 1#( realm | nonce | qop-options | stale | server_maxbuf | */ 1602 /* charset | algorithm | cipher-opts | auth-param ) */ 1603 /******************************************************************/ 1604 1605 saslc__msg_dbg("challenge: '%s'\n", challenge); 1606 1607 ms = sess->mech_sess; 1608 cdata = &ms->cdata; 1609 1610 rv = -1; 1611 memset(cdata, 0, sizeof(*cdata)); 1612 if (saslc__list_parse(&list, challenge) == -1) { 1613 saslc__error_set_errno(ERR(sess), ERROR_NOMEM); 1614 return -1; 1615 } 1616 saslc__list_log(list, "parse list:\n"); 1617 for (n = list; n != NULL; n = n->next) { 1618 char *key; 1619 char *val; 1620 1621 /* Split string into key and val */ 1622 key = n->value; 1623 val = strchr(key, '='); 1624 if (val == NULL) 1625 goto no_mem; 1626 *val = '\0'; 1627 val = strip_quotes(val + 1); 1628 1629 saslc__msg_dbg("key='%s' val='%s'\n", key, val); 1630 switch (get_challenge_t(key)) { 1631 case CHALLENGE_REALM: 1632 /**************************************************/ 1633 /* realm = "realm" "=" <"> realm-value <"> */ 1634 /* realm-value = qdstr-val */ 1635 /* */ 1636 /* This directive is optional; if not present, */ 1637 /* the client SHOULD solicit it from the user or */ 1638 /* be able to compute a default; a plausible */ 1639 /* default might be the realm supplied by the */ 1640 /* user when they logged in to the client system. */ 1641 /* Multiple realm directives are allowed, in */ 1642 /* which case the user or client must choose one */ 1643 /* as the realm for which to supply to username */ 1644 /* and password. */ 1645 /**************************************************/ 1646 if (saslc__list_append(&cdata->realm, val) == -1) 1647 goto no_mem; 1648 break; 1649 case CHALLENGE_NONCE: 1650 /**************************************************/ 1651 /* nonce = "nonce" "=" <"> nonce-value <"> */ 1652 /* nonce-value = *qdtext */ 1653 /* */ 1654 /* This directive is required and MUST appear */ 1655 /* exactly once; if not present, or if multiple */ 1656 /* instances are present, the client should abort */ 1657 /* the authentication exchange. */ 1658 /**************************************************/ 1659 if (cdata->nonce != NULL) { 1660 saslc__error_set(ERR(sess), ERROR_MECH, 1661 "multiple nonce in challenge"); 1662 goto out; 1663 } 1664 cdata->nonce = strdup(val); 1665 if (cdata->nonce == NULL) 1666 goto no_mem; 1667 break; 1668 case CHALLENGE_QOP: 1669 /**************************************************/ 1670 /* qop-options = "qop" "=" <"> qop-list <"> */ 1671 /* qop-list = 1#qop-value */ 1672 /* qop-value = "auth" | "auth-int" | */ 1673 /* "auth-conf" | token */ 1674 /* */ 1675 /* This directive is optional; if not present it */ 1676 /* defaults to "auth". The client MUST ignore */ 1677 /* unrecognized options; if the client recognizes */ 1678 /* no option, it should abort the authentication */ 1679 /* exchange. */ 1680 /**************************************************/ 1681 if (saslc__list_parse(&tmp_list, val) == -1) 1682 goto no_mem; 1683 saslc__list_log(tmp_list, "qop list:\n"); 1684 tmp_flags = saslc__mech_qop_list_flags(tmp_list); 1685 saslc__list_free(tmp_list); 1686 if (tmp_flags == 0) { 1687 saslc__error_set(ERR(sess), ERROR_MECH, 1688 "qop required in challenge"); 1689 goto out; 1690 } 1691 cdata->qop_flags |= tmp_flags; 1692 break; 1693 case CHALLENGE_STALE: 1694 /**************************************************/ 1695 /* stale = "stale" "=" "true" */ 1696 /* */ 1697 /* This directive may appear at most once; if */ 1698 /* multiple instances are present, the client */ 1699 /* should abort the authentication exchange. */ 1700 /**************************************************/ 1701 if (cdata->stale) { 1702 saslc__error_set(ERR(sess), ERROR_MECH, 1703 "multiple stale in challenge"); 1704 goto out; 1705 } 1706 if (strcasecmp(val, "true") != 0) { 1707 saslc__error_set(ERR(sess), ERROR_MECH, 1708 "stale must be true"); 1709 goto out; 1710 } 1711 cdata->stale = true; 1712 break; 1713 case CHALLENGE_MAXBUF: 1714 /**************************************************/ 1715 /* maxbuf-value = 1*DIGIT */ 1716 /* */ 1717 /* The value MUST be bigger than 16 and smaller */ 1718 /* or equal to 16777215 (i.e. 2**24-1). If this */ 1719 /* directive is missing, the default value is */ 1720 /* 65536. This directive may appear at most once; */ 1721 /* if multiple instances are present, the client */ 1722 /* MUST abort the authentication exchange. */ 1723 /**************************************************/ 1724 if (cdata->maxbuf != 0) { 1725 saslc__error_set(ERR(sess), ERROR_MECH, 1726 "multiple maxbuf in challenge"); 1727 goto out; 1728 } 1729 maxbuf = (size_t)strtoul(val, NULL, 10); 1730 if (INVALID_MAXBUF(maxbuf)) { 1731 saslc__error_set(ERR(sess), ERROR_MECH, 1732 "invalid maxbuf in challenge"); 1733 goto out; 1734 } 1735 cdata->maxbuf = maxbuf; 1736 break; 1737 case CHALLENGE_CHARSET: 1738 /**************************************************/ 1739 /* charset = "charset" "=" "utf-8" */ 1740 /* */ 1741 /* This directive may appear at most once; if */ 1742 /* multiple instances are present, the client */ 1743 /* should abort the authentication exchange. */ 1744 /**************************************************/ 1745 if (cdata->utf8) { 1746 saslc__error_set(ERR(sess), ERROR_MECH, 1747 "multiple charset in challenge"); 1748 goto out; 1749 } 1750 if (strcasecmp(val, "utf-8") != 0) { 1751 saslc__error_set(ERR(sess), ERROR_MECH, 1752 "charset != \"utf-8\" in challenge"); 1753 goto out; 1754 } 1755 cdata->utf8 = true; 1756 break; 1757 case CHALLENGE_ALGORITHM: 1758 /**************************************************/ 1759 /* algorithm = "algorithm" "=" "md5-sess" */ 1760 /* */ 1761 /* This directive is required and MUST appear */ 1762 /* exactly once; if not present, or if multiple */ 1763 /* instances are present, the client should abort */ 1764 /* the authentication exchange. */ 1765 /**************************************************/ 1766 if (cdata->algorithm) { 1767 saslc__error_set(ERR(sess), ERROR_MECH, 1768 "multiple algorithm in challenge"); 1769 goto out; 1770 } 1771 if (strcasecmp(val, "md5-sess") != 0) { 1772 saslc__error_set(ERR(sess), ERROR_MECH, 1773 "algorithm != \"md5-sess\" in challenge"); 1774 goto out; 1775 } 1776 cdata->algorithm = true; 1777 break; 1778 case CHALLENGE_CIPHER: 1779 /**************************************************/ 1780 /* cipher-opts = "cipher" "=" <"> 1#cipher-val <">*/ 1781 /* cipher-val = "3des" | "des" | "rc4-40" | */ 1782 /* "rc4" |"rc4-56" | "aes" | */ 1783 /* token */ 1784 /* */ 1785 /* This directive must be present exactly once if */ 1786 /* "auth-conf" is offered in the "qop-options" */ 1787 /* directive, in which case the "3des" cipher is */ 1788 /* mandatory-to-implement. The client MUST ignore */ 1789 /* unrecognized options; if the client recognizes */ 1790 /* no option, it should abort the authentication */ 1791 /* exchange. */ 1792 /**************************************************/ 1793 if (saslc__list_parse(&tmp_list, val) == -1) 1794 goto no_mem; 1795 saslc__list_log(tmp_list, "cipher list:\n"); 1796 tmp_flags = cipher_list_flags(tmp_list); 1797 saslc__list_free(tmp_list); 1798 if (tmp_flags == 0) { 1799 saslc__error_set(ERR(sess), ERROR_MECH, 1800 "unknown cipher"); 1801 goto out; 1802 } 1803 cdata->cipher_flags |= tmp_flags; 1804 break; 1805 case CHALLENGE_IGNORE: 1806 /**************************************************/ 1807 /* auth-param = token "=" ( token | */ 1808 /* quoted-string ) */ 1809 /* */ 1810 /* The client MUST ignore any unrecognized */ 1811 /* directives. */ 1812 /**************************************************/ 1813 break; 1814 } 1815 } 1816 1817 /* 1818 * make sure realms are in iso8859-1 1819 */ 1820 if (stringprep_realms(cdata->utf8, cdata->realm) == -1) { 1821 saslc__error_set(ERR(sess), ERROR_MECH, 1822 "unable to convert realms in challenge from " 1823 "\"utf-8\" to iso8859-1"); 1824 goto out; 1825 } 1826 1827 /* 1828 * test for required options 1829 */ 1830 if (cdata->nonce == NULL) { 1831 saslc__error_set(ERR(sess), ERROR_MECH, 1832 "nonce required in challenge"); 1833 goto out; 1834 } 1835 1836 if (!cdata->algorithm) { 1837 saslc__error_set(ERR(sess), ERROR_MECH, 1838 "algorithm required in challenge"); 1839 goto out; 1840 } 1841 1842 /* 1843 * set the default maxbuf value if it was missing from the 1844 * challenge. 1845 */ 1846 if (cdata->maxbuf == 0) 1847 cdata->maxbuf = DEFAULT_MAXBUF; 1848 1849 saslc__msg_dbg("qop_flags=0x%04x\n", cdata->qop_flags); 1850 saslc__msg_dbg("cipher_flags=0x%04x\n", cdata->cipher_flags); 1851 1852 rv = 0; 1853 out: 1854 saslc__list_free(list); 1855 return rv; 1856 no_mem: 1857 saslc__error_set_errno(ERR(sess), ERROR_NOMEM); 1858 goto out; 1859 } 1860 1861 /** 1862 * @brief creates digestmd5 mechanism session. 1863 * Function initializes also default options for the session. 1864 * @param sess sasl session 1865 * @return 0 on success, -1 on failure. 1866 */ 1867 static int 1868 saslc__mech_digestmd5_create(saslc_sess_t *sess) 1869 { 1870 saslc__mech_digestmd5_sess_t *c; 1871 1872 if ((c = calloc(1, sizeof(*c))) == NULL) { 1873 saslc__error_set_errno(ERR(sess), ERROR_NOMEM); 1874 return -1; 1875 } 1876 c->rdata.nonce_cnt = 1; 1877 sess->mech_sess = c; 1878 1879 return 0; 1880 } 1881 1882 static void 1883 free_cdata(cdata_t *cdata) 1884 { 1885 1886 free(cdata->nonce); 1887 saslc__list_free(cdata->realm); 1888 } 1889 1890 static void 1891 free_rdata(rdata_t *rdata) 1892 { 1893 1894 free(rdata->authcid); 1895 free(rdata->authzid); 1896 free(rdata->cnonce); 1897 free(rdata->digesturi); 1898 if (rdata->passwd != NULL) { 1899 memset(rdata->passwd, 0, strlen(rdata->passwd)); 1900 free(rdata->passwd); 1901 } 1902 free(rdata->realm); 1903 } 1904 1905 /** 1906 * @brief destroys digestmd5 mechanism session. 1907 * Function also is freeing assigned resources to the session. 1908 * @param sess sasl session 1909 * @return Functions always returns 0. 1910 */ 1911 static int 1912 saslc__mech_digestmd5_destroy(saslc_sess_t *sess) 1913 { 1914 saslc__mech_digestmd5_sess_t *ms; 1915 1916 ms = sess->mech_sess; 1917 1918 free_cdata(&ms->cdata); 1919 free_rdata(&ms->rdata); 1920 1921 saslc__buffer32_destroy(ms->dec_ctx.buf_ctx); 1922 saslc__buffer_destroy(ms->enc_ctx.buf_ctx); 1923 1924 cipher_context_destroy(ms->dec_ctx.cph_ctx); 1925 cipher_context_destroy(ms->enc_ctx.cph_ctx); 1926 1927 free(sess->mech_sess); 1928 sess->mech_sess = NULL; 1929 1930 return 0; 1931 } 1932 1933 /** 1934 * @brief collect the response data necessary to build the reply. 1935 * @param sess the session context 1936 * @return 0 on success, -1 on failure 1937 * 1938 * NOTE: 1939 * The input info is from the challenge (previously saved in cdata of 1940 * saslc__mech_digestmd5_sess_t) or from the property dictionaries. 1941 * 1942 * The output info is saved in (mostly) in rdata of the 1943 * saslc__mech_digestmd5_sess_t structure. The qop is special in that 1944 * it is exposed to the saslc__mech_sess_t layer. 1945 */ 1946 static int 1947 saslc__mech_digestmd5_response_data(saslc_sess_t *sess) 1948 { 1949 saslc__mech_digestmd5_sess_t *ms; 1950 cdata_t *cdata; 1951 rdata_t *rdata; 1952 const char *authcid; 1953 const char *authzid; 1954 const char *hostname; 1955 const char *maxbuf; 1956 const char *passwd; 1957 int rv; 1958 1959 ms = sess->mech_sess; 1960 cdata = &ms->cdata; 1961 rdata = &ms->rdata; 1962 1963 if ((rv = choose_qop(sess, cdata->qop_flags)) == -1) 1964 return -1; /* error message already set */ 1965 ms->mech_sess.qop = rv; 1966 1967 if (ms->mech_sess.qop == QOP_CONF) { 1968 if ((rv = choose_cipher(sess, cdata->cipher_flags)) == -1) 1969 return -1; /* error message already set */ 1970 rdata->cipher = rv; 1971 } 1972 1973 hostname = saslc_sess_getprop(sess, SASLC_DIGESTMD5_HOSTNAME); 1974 if (hostname == NULL) { 1975 saslc__error_set(ERR(sess), ERROR_MECH, 1976 "hostname is required for authentication"); 1977 return -1; 1978 } 1979 1980 rdata->realm = choose_realm(sess, hostname, cdata->realm); 1981 if (rdata->realm == NULL) 1982 return -1; /* error message already set */ 1983 1984 rdata->digesturi = saslc__mech_digestmd5_digesturi(sess, hostname); 1985 if (rdata->digesturi == NULL) 1986 return -1; /* error message already set */ 1987 1988 authcid = saslc_sess_getprop(sess, SASLC_DIGESTMD5_AUTHCID); 1989 if (authcid == NULL) { 1990 saslc__error_set(ERR(sess), ERROR_MECH, 1991 "authcid is required for an authentication"); 1992 return -1; 1993 } 1994 rdata->authcid = strdup(authcid); 1995 if (rdata->authcid == NULL) 1996 goto no_mem; 1997 1998 authzid = saslc_sess_getprop(sess, SASLC_DIGESTMD5_AUTHZID); 1999 if (authzid != NULL) { 2000 rdata->authzid = strdup(authzid); 2001 if (rdata->authzid == NULL) 2002 goto no_mem; 2003 } 2004 2005 passwd = saslc_sess_getprop(sess, SASLC_DIGESTMD5_PASSWD); 2006 if (passwd == NULL) { 2007 saslc__error_set(ERR(sess), ERROR_MECH, 2008 "password is required for an authentication"); 2009 return -1; 2010 } 2011 rdata->passwd = strdup(passwd); 2012 if (rdata->passwd == NULL) 2013 goto no_mem; 2014 2015 rdata->cnonce = saslc__mech_digestmd5_nonce(NONCE_LEN); 2016 if (rdata->cnonce == NULL) { 2017 saslc__error_set(ERR(sess), ERROR_MECH, 2018 "failed to create cnonce"); 2019 return -1; 2020 } 2021 #ifdef SASLC_DIGESTMD5_CNONCE /* XXX: for debugging! */ 2022 { 2023 const char *cnonce; 2024 cnonce = saslc_sess_getprop(sess, SASLC_DIGESTMD5_CNONCE); 2025 if (cnonce != NULL) { 2026 rdata->cnonce = strdup(cnonce); 2027 if (rdata->cnonce == NULL) 2028 goto no_mem; 2029 } 2030 } 2031 #endif 2032 if (ms->mech_sess.qop != QOP_NONE) { 2033 maxbuf = saslc_sess_getprop(sess, SASLC_DIGESTMD5_MAXBUF); 2034 if (maxbuf != NULL) 2035 rdata->maxbuf = (size_t)strtoul(maxbuf, NULL, 10); 2036 if (rdata->maxbuf == 0) 2037 rdata->maxbuf = cdata->maxbuf; 2038 if (INVALID_MAXBUF(rdata->maxbuf)) { 2039 saslc__error_set(ERR(sess), ERROR_MECH, 2040 "maxbuf out of range"); 2041 return -1; 2042 } 2043 } 2044 return 0; 2045 2046 no_mem: 2047 saslc__error_set_errno(ERR(sess), ERROR_NOMEM); 2048 return -1; 2049 } 2050 2051 /** 2052 * @brief compute the maximum payload that can go into an integrity or 2053 * confidentiality packet. 2054 * @param maxbuf the server's maxbuf size. 2055 * @param blksize the ciphers block size. 0 or 1 if there is no blocking. 2056 * @return the payload size 2057 * 2058 * The packet (not including the leading uint32_t packet length field) 2059 * has this structure: 2060 * 2061 * struct { 2062 * uint8_t payload[]; // packet payload 2063 * uint8_t padding[]; // padding to block size 2064 * uint8_t hmac_0_9[10]; // the first 10 bytes of the hmac 2065 * uint8_t version[2]; // version number (1) in BE format 2066 * uint8_t seqnum[4]; // sequence number in BE format 2067 * } __packed 2068 * 2069 * NOTE: if the block size is > 1, then padding is required to make 2070 * the {payload[], padding[], and hmac_0_9[]} a multiple of the block 2071 * size. Furthermore there must be at least one byte of padding! The 2072 * padding bytes are all set to the padding length and one byte of 2073 * padding is necessary to recover the padding length. 2074 */ 2075 static size_t 2076 maxpayload(size_t maxbuf, size_t blksize) 2077 { 2078 size_t l; 2079 2080 if (blksize <= 1) { /* no padding used */ 2081 if (maxbuf <= sizeof(md5hash_t)) 2082 return 0; 2083 2084 return maxbuf - sizeof(md5hash_t); 2085 } 2086 if (maxbuf < 2 * blksize + 6) 2087 return 0; 2088 2089 l = rounddown(maxbuf - 6, blksize); 2090 if (l <= 10 + 1) /* we need at least one byte of padding */ 2091 return 0; 2092 2093 return l - 10 - 1; 2094 } 2095 2096 /** 2097 * @brief initialize the encode and decode coder contexts for the session 2098 * @param sess the current session 2099 * @return 0 on success, -1 on failure. 2100 */ 2101 static int 2102 init_coder_context(saslc_sess_t *sess) 2103 { 2104 saslc__mech_digestmd5_sess_t *ms; 2105 size_t blksize; 2106 #ifdef SASLC_DIGESTMD5_SELFTEST 2107 int selftest; /* XXX: allow for testing against ourselves */ 2108 #endif 2109 2110 ms = sess->mech_sess; 2111 #ifdef SASLC_DIGESTMD5_SELFTEST 2112 selftest = saslc_sess_getprop(sess, SASLC_DIGESTMD5_SELFTEST) != NULL; 2113 #endif 2114 blksize = 0; 2115 switch (ms->mech_sess.qop) { 2116 case QOP_NONE: 2117 return 0; 2118 case QOP_INT: 2119 #ifdef SASLC_DIGESTMD5_SELFTEST 2120 ms->dec_ctx.key = selftest ? ms->keys.kic : ms->keys.kis; 2121 #else 2122 ms->dec_ctx.key = ms->keys.kis; 2123 #endif 2124 ms->enc_ctx.key = ms->keys.kic; 2125 ms->dec_ctx.cph_ctx = NULL; 2126 ms->enc_ctx.cph_ctx = NULL; 2127 break; 2128 case QOP_CONF: 2129 #ifdef SASLC_DIGESTMD5_SELFTEST 2130 ms->dec_ctx.key = selftest ? ms->keys.kcc : ms->keys.kcs; 2131 #else 2132 ms->dec_ctx.key = ms->keys.kcs; 2133 #endif 2134 ms->enc_ctx.key = ms->keys.kcc; 2135 ms->dec_ctx.cph_ctx = cipher_context_create(sess, 2136 ms->rdata.cipher, 0, ms->dec_ctx.key); 2137 if (ms->dec_ctx.cph_ctx == NULL) 2138 return -1; 2139 2140 ms->enc_ctx.cph_ctx = cipher_context_create(sess, 2141 ms->rdata.cipher, 1, ms->enc_ctx.key); 2142 if (ms->enc_ctx.cph_ctx == NULL) 2143 return -1; 2144 2145 blksize = ms->enc_ctx.cph_ctx->blksize; 2146 break; 2147 } 2148 ms->dec_ctx.sess = sess; 2149 ms->enc_ctx.sess = sess; 2150 ms->dec_ctx.buf_ctx = saslc__buffer32_create(sess, ms->rdata.maxbuf); 2151 if (ms->cdata.maxbuf < 2 * blksize + 6) { 2152 saslc__error_set(ERR(sess), ERROR_MECH, 2153 "server buffer too small for packet"); 2154 return -1; 2155 } 2156 ms->enc_ctx.buf_ctx = saslc__buffer_create(sess, 2157 maxpayload(ms->cdata.maxbuf, blksize)); 2158 2159 if (ms->dec_ctx.buf_ctx == NULL || ms->enc_ctx.buf_ctx == NULL) { 2160 saslc__error_set_errno(ERR(sess), ERROR_NOMEM); 2161 return -1; 2162 } 2163 return 0; 2164 } 2165 2166 /** 2167 * @brief construct the reply string. 2168 * @param sess session context 2169 * @param response string 2170 * @return reply string or NULL on failure. 2171 */ 2172 static char * 2173 saslc__mech_digestmd5_reply(saslc_sess_t *sess, char *response) 2174 { 2175 saslc__mech_digestmd5_sess_t *ms; 2176 char *out; 2177 char *cipher, *maxbuf, *realm; 2178 2179 ms = sess->mech_sess; 2180 2181 out = NULL; 2182 cipher = __UNCONST(""); 2183 maxbuf = __UNCONST(""); 2184 realm = __UNCONST(""); 2185 2186 switch (ms->mech_sess.qop) { 2187 case QOP_CONF: 2188 if (asprintf(&cipher, "cipher=\"%s\",", 2189 cipher_name(ms->rdata.cipher)) == -1) { 2190 saslc__error_set_errno(ERR(sess), ERROR_NOMEM); 2191 goto done; 2192 } 2193 /*FALLTHROUGH*/ 2194 case QOP_INT: 2195 if (asprintf(&maxbuf, "maxbuf=%zu,", ms->rdata.maxbuf) == -1) { 2196 saslc__error_set_errno(ERR(sess), ERROR_NOMEM); 2197 goto done; 2198 } 2199 break; 2200 case QOP_NONE: 2201 break; 2202 default: 2203 assert(/*CONSTCOND*/0); 2204 return NULL; 2205 } 2206 if (ms->rdata.realm != NULL && 2207 asprintf(&realm, "realm=\"%s\",", ms->rdata.realm) == -1) { 2208 saslc__error_set_errno(ERR(sess), ERROR_NOMEM); 2209 goto done; 2210 } 2211 2212 if (asprintf(&out, 2213 "username=\"%s\"," 2214 "%s" /* realm= */ 2215 "nonce=\"%s\"," 2216 "cnonce=\"%s\"," 2217 "nc=%08d," 2218 "qop=%s," 2219 "%s" /* cipher= */ 2220 "%s" /* maxbuf= */ 2221 "digest-uri=\"%s\"," 2222 "response=%s", 2223 ms->rdata.authcid, 2224 realm, 2225 ms->cdata.nonce, 2226 ms->rdata.cnonce, 2227 ms->rdata.nonce_cnt, 2228 saslc__mech_qop_name(ms->mech_sess.qop), 2229 cipher, 2230 maxbuf, 2231 ms->rdata.digesturi, 2232 response 2233 ) == -1) { 2234 saslc__error_set_errno(ERR(sess), ERROR_NOMEM); 2235 out = NULL; 2236 } 2237 done: 2238 if (realm[0] != '\0') 2239 free(realm); 2240 if (maxbuf[0] != '\0') 2241 free(maxbuf); 2242 if (cipher[0] != '\0') 2243 free(cipher); 2244 2245 return out; 2246 } 2247 2248 /** 2249 * @brief do one step of the sasl authentication 2250 * @param sess sasl session 2251 * @param in input data 2252 * @param inlen input data length 2253 * @param out place to store output data 2254 * @param outlen output data length 2255 * @return MECH_OK - authentication successful, 2256 * MECH_STEP - more steps are needed, 2257 * MECH_ERROR - error 2258 */ 2259 static int 2260 saslc__mech_digestmd5_cont(saslc_sess_t *sess, const void *in, size_t inlen, 2261 void **out, size_t *outlen) 2262 { 2263 saslc__mech_digestmd5_sess_t *ms; 2264 char *response; 2265 const char *p; 2266 2267 ms = sess->mech_sess; 2268 2269 switch(ms->mech_sess.step) { 2270 case 0: 2271 /* in case we are called before getting data from server */ 2272 if (inlen == 0) { 2273 *out = NULL; 2274 *outlen = 0; 2275 return MECH_STEP; 2276 } 2277 /* if input data was provided, then doing the first step */ 2278 ms->mech_sess.step++; 2279 /*FALLTHROUGH*/ 2280 case 1: 2281 if (saslc__mech_digestmd5_parse_challenge(sess, in) == -1) 2282 return MECH_ERROR; 2283 2284 if (saslc__mech_digestmd5_response_data(sess) == -1) 2285 return MECH_ERROR; 2286 2287 if ((response = saslc__mech_digestmd5_response(ms, 2288 "AUTHENTICATE")) == NULL) { 2289 saslc__error_set(ERR(sess), ERROR_MECH, 2290 "unable to construct response"); 2291 return MECH_ERROR; 2292 } 2293 *out = saslc__mech_digestmd5_reply(sess, response); 2294 free(response); 2295 if (*out == NULL) 2296 return MECH_ERROR; 2297 2298 *outlen = strlen(*out); 2299 return MECH_STEP; 2300 case 2: 2301 if ((response = saslc__mech_digestmd5_response(ms, "")) 2302 == NULL) { 2303 saslc__error_set(ERR(sess), ERROR_MECH, 2304 "unable to construct rspauth"); 2305 return MECH_ERROR; 2306 } 2307 p = in; 2308 if (strncmp(p, "rspauth=", 8) != 0 || 2309 strcmp(response, p + 8) != 0) { 2310 saslc__msg_dbg("rspauth='%s'\n", response); 2311 saslc__error_set(ERR(sess), ERROR_MECH, 2312 "failed to validate rspauth response"); 2313 free(response); 2314 return MECH_ERROR; 2315 } 2316 free(response); 2317 if (init_coder_context(sess) == -1) 2318 return MECH_ERROR; 2319 *out = NULL; 2320 *outlen = 0; 2321 return MECH_OK; 2322 default: 2323 assert(/*CONSTCOND*/0); /* impossible */ 2324 return MECH_ERROR; 2325 } 2326 } 2327 2328 /* mechanism definition */ 2329 const saslc__mech_t saslc__mech_digestmd5 = { 2330 .name = "DIGEST-MD5", 2331 .flags = FLAG_MUTUAL | FLAG_DICTIONARY, 2332 .create = saslc__mech_digestmd5_create, 2333 .cont = saslc__mech_digestmd5_cont, 2334 .encode = saslc__mech_digestmd5_encode, 2335 .decode = saslc__mech_digestmd5_decode, 2336 .destroy = saslc__mech_digestmd5_destroy 2337 }; 2338