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