1 /* $NetBSD: mech_digestmd5.c,v 1.13 2018/01/30 15:28:39 shm 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.13 2018/01/30 15:28:39 shm 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 = NULL; 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 free(p); 930 if ((p = strdup(realms->value)) == NULL) 931 goto nomem; 932 return p; 933 nomem: 934 saslc__error_set_errno(ERR(sess), ERROR_NOMEM); 935 return NULL; 936 } 937 938 /** 939 * @brief destroy a cipher context 940 * @param ctx cipher context 941 * @return nothing 942 */ 943 static void 944 cipher_context_destroy(cipher_context_t *ctx) 945 { 946 947 if (ctx != NULL) { 948 if (ctx->evp_ctx != NULL) 949 EVP_CIPHER_CTX_free(ctx->evp_ctx); 950 free(ctx); 951 } 952 } 953 954 /** 955 * @brief slide the bits from 7 bytes into the high 7 bits of 8 bites 956 * @param ikey input key 957 * @param okey output key 958 * 959 * This matches cyrus-sasl 2.1.23 960 */ 961 static inline void 962 slidebits(uint8_t *ikey, uint8_t *okey) 963 { 964 965 okey[0] = ikey[0] << 0; 966 okey[1] = ikey[0] << 7 | (unsigned)ikey[1] >> 1; 967 okey[2] = ikey[1] << 6 | (unsigned)ikey[2] >> 2; 968 okey[3] = ikey[2] << 5 | (unsigned)ikey[3] >> 3; 969 okey[4] = ikey[3] << 4 | (unsigned)ikey[4] >> 4; 970 okey[5] = ikey[4] << 3 | (unsigned)ikey[5] >> 5; 971 okey[6] = ikey[5] << 2 | (unsigned)ikey[6] >> 6; 972 okey[7] = ikey[6] << 1; 973 } 974 975 /** 976 * @brief convert our key to a DES key 977 * @param key our key 978 * @param keylen our key length 979 * @param deskey the key in DES format 980 * 981 * NOTE: The openssl implementations of "des" and "3des" expect their 982 * keys to be in the high 7 bits of 8 bytes and 16 bytes, 983 * respectively. Thus, our key length will be 7 and 14 bytes, 984 * respectively. 985 */ 986 static void 987 make_deskey(uint8_t *key, size_t keylen, uint8_t *deskey) 988 { 989 990 assert(keylen == 7 || keylen == 14); 991 992 slidebits(deskey + 0, key + 0); 993 if (keylen == 14) 994 slidebits(deskey + 7, key + 7); 995 } 996 997 /** 998 * @brief create a cipher context, including EVP cipher initialization. 999 * @param sess session context 1000 * @param cipher cipher to use 1001 * @param do_enc encode context if set, decode context if 0 1002 * @param key crypt key to use 1003 * @return cipher context, or NULL on error 1004 */ 1005 static cipher_context_t * 1006 cipher_context_create(saslc_sess_t *sess, cipher_t cipher, int do_enc, uint8_t *key) 1007 { 1008 #define AES_IV_MAGIC "aes-128" 1009 #define AES_IV_MAGIC_LEN (sizeof(AES_IV_MAGIC) - 1) 1010 static const struct cipher_ctx_tbl_s { 1011 cipher_t eval; /* for error checking */ 1012 const EVP_CIPHER *(*evp_type)(void);/* type of cipher */ 1013 size_t keylen; /* key length */ 1014 ssize_t blksize; /* block size for cipher */ 1015 size_t ivlen; /* initial value length */ 1016 } cipher_ctx_tbl[] = { 1017 /* NB: table indexed by cipher_t */ 1018 /* eval evp_type keylen blksize ivlen */ 1019 { CIPHER_DES, EVP_des_cbc, 7, 8, 8 }, 1020 { CIPHER_3DES, EVP_des_ede_cbc, 14, 8, 8 }, 1021 { CIPHER_RC4, EVP_rc4, 16, 1, 0 }, 1022 { CIPHER_RC4_40, EVP_rc4, 5, 1, 0 }, 1023 { CIPHER_RC4_56, EVP_rc4, 7, 1, 0 }, 1024 { CIPHER_AES, EVP_aes_128_cbc, 16, 16, 16 } 1025 }; 1026 const struct cipher_ctx_tbl_s *ctp; 1027 char buf[sizeof(md5hash_t) + AES_IV_MAGIC_LEN]; 1028 uint8_t deskey[16]; 1029 md5hash_t aes_iv; /* initial value buffer for aes */ 1030 cipher_context_t *ctx; /* cipher context */ 1031 uint8_t *ivp; 1032 const char *errmsg; 1033 int rv; 1034 1035 /*************************************************************************/ 1036 /* See draft-ietf-sasl-rfc2831bis-02.txt section 2.4 (mentions "aes") */ 1037 /* The key for the "rc4" and "aes" ciphers is all 16 bytes of Kcc or Kcs.*/ 1038 /* The key for the "rc4-40" cipher is the first 5 bytes of Kcc or Kcs. */ 1039 /* The key for the "rc4-56" is the first 7 bytes of Kcc or Kcs. */ 1040 /* The key for "des" is the first 7 bytes of Kcc or Kcs. */ 1041 /* The key for "3des" is the first 14 bytes of Kcc or Kcs. */ 1042 /* */ 1043 /* The IV used to send/receive the initial buffer of security encoded */ 1044 /* data for "des" and "3des" is the last 8 bytes of Kcc or Kcs. For all */ 1045 /* subsequent buffers the last 8 bytes of the ciphertext of the buffer */ 1046 /* NNN is used as the IV for the buffer (NNN + 1). */ 1047 /* */ 1048 /* The IV for the "aes" cipher in CBC mode for messages going from the */ 1049 /* client to the server (IVc) consists of 16 bytes calculated as */ 1050 /* follows: IVc = MD5({Kcc, "aes-128"}) */ 1051 /* */ 1052 /* The IV for the "aes" cipher in CBC mode for messages going from the */ 1053 /* server to the client (IVs) consists of 16 bytes calculated as */ 1054 /* follows: IVs = MD5({Kcs, "aes-128"}) */ 1055 /*************************************************************************/ 1056 1057 assert(cipher < __arraycount(cipher_ctx_tbl)); 1058 if (cipher >= __arraycount(cipher_ctx_tbl)) { 1059 saslc__error_set_errno(ERR(sess), ERROR_BADARG); 1060 return NULL; 1061 } 1062 1063 ctx = malloc(sizeof(*ctx)); 1064 if (ctx == NULL) { 1065 saslc__error_set_errno(ERR(sess), ERROR_NOMEM); 1066 return NULL; 1067 } 1068 1069 ctp = &cipher_ctx_tbl[cipher]; 1070 assert(ctp->eval == cipher); 1071 1072 ctx->blksize = ctp->blksize; 1073 1074 ctx->evp_ctx = EVP_CIPHER_CTX_new(); 1075 if (ctx->evp_ctx == NULL) { 1076 errmsg = "EVP_CIPHER_CTX_new failed"; 1077 goto err; 1078 } 1079 if (EVP_CipherInit_ex(ctx->evp_ctx, ctp->evp_type(), NULL, NULL, NULL, 1080 do_enc) == 0) { 1081 errmsg = "EVP_CipherInit_ex failed"; 1082 goto err; 1083 } 1084 if (EVP_CIPHER_CTX_set_padding(ctx->evp_ctx, 0) == 0) { 1085 errmsg = "EVP_CIPHER_CTX_set_padding failed"; 1086 goto err; 1087 } 1088 ivp = NULL; 1089 switch (cipher) { /* prepare key and IV */ 1090 case CIPHER_RC4: 1091 case CIPHER_RC4_40: 1092 case CIPHER_RC4_56: 1093 assert(ctp->ivlen == 0); /* no IV */ 1094 rv = EVP_CIPHER_CTX_set_key_length(ctx->evp_ctx, 1095 (int)ctp->keylen); 1096 if (rv == 0) { 1097 errmsg = "EVP_CIPHER_CTX_set_key_length failed"; 1098 goto err; 1099 } 1100 break; 1101 case CIPHER_DES: 1102 case CIPHER_3DES: 1103 assert(ctp->ivlen == 8); 1104 ivp = key + 8; 1105 make_deskey(key, ctp->keylen, deskey); 1106 key = deskey; 1107 break; 1108 case CIPHER_AES: 1109 assert(ctp->ivlen == 16); 1110 /* IVs = MD5({Kcs, "aes-128"}) */ 1111 memcpy(buf, key, sizeof(md5hash_t)); 1112 memcpy(buf + sizeof(md5hash_t), AES_IV_MAGIC, AES_IV_MAGIC_LEN); 1113 saslc__crypto_md5_hash(buf, sizeof(buf), aes_iv); 1114 ivp = aes_iv; 1115 break; 1116 } 1117 if (EVP_CipherInit_ex(ctx->evp_ctx, NULL, NULL, key, ivp, do_enc) == 0) { 1118 errmsg = "EVP_CipherInit_ex 2 failed"; 1119 goto err; 1120 } 1121 return ctx; 1122 err: 1123 cipher_context_destroy(ctx); 1124 saslc__error_set(ERR(sess), ERROR_MECH, errmsg); 1125 return NULL; 1126 1127 #undef AES_IV_MAGIC_LEN 1128 #undef AES_IV_MAGIC 1129 } 1130 1131 /** 1132 * @brief compute the necessary padding length 1133 * @param ctx the cipher context 1134 * @param inlen the data length to put in the packet 1135 * @return the length of padding needed (zero if none needed) 1136 */ 1137 static size_t 1138 get_padlen(cipher_context_t *ctx, size_t inlen) 1139 { 1140 size_t blksize; 1141 1142 if (ctx == NULL) 1143 return 0; 1144 1145 blksize = ctx->blksize; 1146 if (blksize == 1) 1147 return 0; 1148 1149 return blksize - ((inlen + 10) % blksize); 1150 } 1151 1152 /** 1153 * @brief compute the packet integrity including the version and 1154 * sequence number 1155 * @param key the hmac_md5 hash key 1156 * @param seqnum the sequence number 1157 * @param in the input buffer 1158 * @param inlen the input buffer length 1159 * @return 0 on success, -1 on failure 1160 */ 1161 static int 1162 packet_integrity(md5hash_t key, uint32_t seqnum, void *in, size_t inlen, 1163 md5hash_t mac) 1164 { 1165 1166 be32enc(in, seqnum); 1167 if (saslc__crypto_hmac_md5_hash(key, MD5_DIGEST_LENGTH, in, inlen, mac) 1168 == -1) 1169 return -1; 1170 1171 /* we keep only the first 10 bytes of the hash */ 1172 be16enc(mac + 10, 0x0001); /* add 2 byte version number */ 1173 be32enc(mac + 12, seqnum); /* add 4 byte sequence number */ 1174 return 0; 1175 } 1176 1177 /** 1178 * @brief encode or decode a buffer (in place) 1179 * @param ctx the cipher context 1180 * @param in the input buffer 1181 * @param inlen the buffer length 1182 * @return the length of the result left in the input buffer after 1183 * processing, or -1 on failure. 1184 */ 1185 static ssize_t 1186 cipher_update(cipher_context_t *ctx, void *in, size_t inlen) 1187 { 1188 int outl, rv; 1189 void *out; 1190 1191 out = in; /* XXX: this assumes we can encoded and decode in place */ 1192 rv = EVP_CipherUpdate(ctx->evp_ctx, out, &outl, in, (int)inlen); 1193 if (rv == 0) 1194 return -1; 1195 1196 return outl; 1197 } 1198 1199 /** 1200 * @brief incapsulate a message with confidentiality (sign and encrypt) 1201 * @param ctx coder context 1202 * @param in pointer to message to encode 1203 * @param inlen length of message 1204 * @param out encoded output packet (including prefixed 4 byte length field) 1205 * @param outlen decoded output packet length 1206 * @returns 0 on success, -1 on failure 1207 * 1208 * NOTE: this allocates memory for its output and the caller is 1209 * responsible for freeing it. 1210 * 1211 * integrity (auth-int): 1212 * len, HMAC(ki, {SeqNum, msg})[0..9], x0001, SeqNum 1213 * 1214 * confidentiality (auth-conf): 1215 * len, CIPHER(Kc, {msg, pag, HMAC(ki, {SeqNum, msg})[0..9]}), x0001, SeqNum 1216 */ 1217 static ssize_t 1218 encode_buffer(coder_context_t *ctx, const void *in, size_t inlen, 1219 void **out, size_t *outlen) 1220 { 1221 void *buf; 1222 uint8_t *mac, *p; 1223 ssize_t tmplen; 1224 size_t buflen; 1225 size_t padlen; 1226 1227 padlen = get_padlen(ctx->cph_ctx, inlen); 1228 buflen = 4 + inlen + padlen + sizeof(md5hash_t); 1229 buf = malloc(buflen); 1230 if (buf == NULL) { 1231 saslc__error_set_errno(ERR(ctx->sess), ERROR_NOMEM); 1232 return -1; 1233 } 1234 p = buf; 1235 memcpy(p + 4, in, inlen); 1236 mac = p + 4 + inlen + padlen; 1237 if (packet_integrity(ctx->key, ctx->seqnum, buf, 4 + inlen, mac) 1238 == -1) { 1239 saslc__error_set(ERR(ctx->sess), ERROR_MECH, "HMAC failed"); 1240 free(buf); 1241 return -1; 1242 } 1243 1244 if (padlen) 1245 memset(p + 4 + inlen, (int)padlen, padlen); 1246 1247 if (ctx->cph_ctx != NULL) { 1248 if ((tmplen = cipher_update(ctx->cph_ctx, p + 4, 1249 inlen + padlen + 10)) == -1) { 1250 saslc__error_set(ERR(ctx->sess), ERROR_MECH, 1251 "cipher error"); 1252 free(buf); 1253 return -1; 1254 } 1255 assert((size_t)tmplen == inlen + padlen + 10); 1256 if ((size_t)tmplen != inlen + padlen + 10) 1257 return -1; 1258 } 1259 1260 be32enc(buf, (uint32_t)(buflen - 4)); 1261 1262 *out = buf; 1263 *outlen = buflen; 1264 ctx->seqnum++; /* wraps at 2^32 */ 1265 return 0; 1266 } 1267 1268 /** 1269 * @brief decode one complete confidentiality encoded packet 1270 * @param ctx coder context 1271 * @param in pointer to packet, including the beginning 4 byte length field. 1272 * @param inlen length of packet 1273 * @param out decoded output 1274 * @param outlen decoded output length 1275 * @returns 0 on success, -1 on failure 1276 * 1277 * NOTE: this modifies the intput buffer! 1278 * NOTE: this allocates memory for its output and the caller is 1279 * responsible for freeing it. 1280 * 1281 * integrity (auth-int): 1282 * len, HMAC(ki, {SeqNum, msg})[0..9], x0001, SeqNum 1283 * 1284 * confidentiality (auth-conf): 1285 * len, CIPHER(Kc, {msg, pag, HMAC(ki, {SeqNum, msg})[0..9]}), x0001, SeqNum 1286 */ 1287 static ssize_t 1288 decode_buffer(coder_context_t *ctx, void *in, size_t inlen, 1289 void **out, size_t *outlen) 1290 { 1291 md5hash_t mac; 1292 void *buf; 1293 uint8_t *p; 1294 size_t blksize, buflen, padlen; 1295 ssize_t tmplen; 1296 uint32_t len; 1297 1298 padlen = get_padlen(ctx->cph_ctx, 1); 1299 if (inlen < 4 + 1 + padlen + MD5_DIGEST_LENGTH) { 1300 saslc__error_set(ERR(ctx->sess), ERROR_MECH, 1301 "zero payload packet"); 1302 return -1; 1303 } 1304 len = be32dec(in); 1305 if (len + 4 != inlen) { 1306 saslc__error_set(ERR(ctx->sess), ERROR_MECH, 1307 "bad packet length"); 1308 return -1; 1309 } 1310 1311 if (ctx->cph_ctx != NULL) { 1312 p = in; 1313 if ((tmplen = cipher_update(ctx->cph_ctx, p + 4, len - 6)) == -1) { 1314 saslc__error_set(ERR(ctx->sess), ERROR_MECH, 1315 "cipher error"); 1316 return -1; 1317 } 1318 assert(tmplen == (ssize_t)len - 6); 1319 if (tmplen != (ssize_t)len - 6) 1320 return -1; 1321 } 1322 1323 blksize = ctx->cph_ctx ? ctx->cph_ctx->blksize : 0; 1324 if (blksize <= 1) 1325 padlen = 0; 1326 else{ 1327 p = in; 1328 padlen = p[inlen - sizeof(md5hash_t) - 1]; 1329 if (padlen > blksize || padlen == 0) { 1330 saslc__error_set(ERR(ctx->sess), ERROR_MECH, 1331 "invalid padding length after decode"); 1332 return -1; 1333 } 1334 } 1335 if (packet_integrity(ctx->key, ctx->seqnum, in, 1336 inlen - padlen - sizeof(mac), mac) == -1) { 1337 saslc__error_set(ERR(ctx->sess), ERROR_MECH, "HMAC failed"); 1338 return -1; 1339 } 1340 1341 p = in; 1342 p += 4 + len - MD5_DIGEST_LENGTH; 1343 if (memcmp(p, mac, MD5_DIGEST_LENGTH) != 0) { 1344 uint32_t seqnum; 1345 1346 p = in; 1347 seqnum = be32dec(p + inlen - 4); 1348 saslc__error_set(ERR(ctx->sess), ERROR_MECH, 1349 seqnum != ctx->seqnum ? "invalid MAC (bad seqnum)" : 1350 "invalid MAC"); 1351 return -1; 1352 } 1353 1354 buflen = len - padlen - MD5_DIGEST_LENGTH; 1355 buf = malloc(buflen); 1356 if (buf == NULL) { 1357 saslc__error_set_errno(ERR(ctx->sess), ERROR_NOMEM); 1358 return -1; 1359 } 1360 p = in; 1361 p += 4; 1362 memcpy(buf, p, buflen); 1363 1364 *out = buf; 1365 *outlen = buflen; 1366 ctx->seqnum++; 1367 return 0; 1368 } 1369 1370 /** 1371 * @brief add integrity or confidentiality layer 1372 * @param sess session handle 1373 * @param in input buffer 1374 * @param inlen input buffer length 1375 * @param out pointer to output buffer 1376 * @param out pointer to output buffer length 1377 * @return number of bytes consumed on success, 0 if insufficient data 1378 * to process, -1 on failure 1379 */ 1380 static ssize_t 1381 saslc__mech_digestmd5_encode(saslc_sess_t *sess, const void *in, size_t inlen, 1382 void **out, size_t *outlen) 1383 { 1384 saslc__mech_digestmd5_sess_t *ms; 1385 uint8_t *buf; 1386 size_t buflen; 1387 ssize_t rval; 1388 1389 ms = sess->mech_sess; 1390 assert(ms->mech_sess.qop != QOP_NONE); 1391 if (ms->mech_sess.qop == QOP_NONE) 1392 return -1; 1393 1394 rval = saslc__buffer_fetch(ms->enc_ctx.buf_ctx, in, inlen, &buf, &buflen); 1395 if (rval == -1) 1396 return -1; 1397 if (buflen == 0) { 1398 *out = NULL; 1399 *outlen = 0; 1400 return rval; 1401 } 1402 if (encode_buffer(&ms->enc_ctx, buf, buflen, out, outlen) == -1) 1403 return -1; 1404 1405 return rval; 1406 } 1407 1408 /** 1409 * @brief remove integrity or confidentiality layer 1410 * @param sess session handle 1411 * @param in input buffer 1412 * @param inlen input buffer length 1413 * @param out pointer to output buffer 1414 * @param out pointer to output buffer length 1415 * @return number of bytes consumed on success, 0 if insufficient data 1416 * to process, -1 on failure 1417 * 1418 * integrity (auth-int): 1419 * len, HMAC(ki, {SeqNum, msg})[0..9], x0001, SeqNum 1420 * 1421 * confidentiality (auth-conf): 1422 * len, CIPHER(Kc, {msg, pag, HMAC(ki, {SeqNum, msg})[0..9]}), x0001, SeqNum 1423 */ 1424 static ssize_t 1425 saslc__mech_digestmd5_decode(saslc_sess_t *sess, const void *in, size_t inlen, 1426 void **out, size_t *outlen) 1427 { 1428 saslc__mech_digestmd5_sess_t *ms; 1429 uint8_t *buf; 1430 size_t buflen; 1431 ssize_t rval; 1432 1433 ms = sess->mech_sess; 1434 assert(ms->mech_sess.qop != QOP_NONE); 1435 if (ms->mech_sess.qop == QOP_NONE) 1436 return -1; 1437 1438 rval = saslc__buffer32_fetch(ms->dec_ctx.buf_ctx, in, inlen, &buf, &buflen); 1439 if (rval == -1) 1440 return -1; 1441 1442 if (buflen == 0) { 1443 *out = NULL; 1444 *outlen = 0; 1445 return rval; 1446 } 1447 if (decode_buffer(&ms->dec_ctx, buf, buflen, out, outlen) == -1) 1448 return -1; 1449 1450 return rval; 1451 } 1452 1453 /************************************************************************ 1454 * XXX: Share with mech_gssapi.c? They are almost identical. 1455 */ 1456 /** 1457 * @brief choose the best qop based on what was provided by the 1458 * challenge and a possible user mask. 1459 * @param sess the session context 1460 * @param qop_flags the qop flags parsed from the challenge string 1461 * @return the selected saslc__mech_sess_qop_t or -1 if no match 1462 */ 1463 static int 1464 choose_qop(saslc_sess_t *sess, uint32_t qop_flags) 1465 { 1466 list_t *list; 1467 const char *user_qop; 1468 1469 if (qop_flags == 0) /* no qop spec in challenge (it's optional) */ 1470 return QOP_NONE; 1471 1472 qop_flags &= DEFAULT_QOP_MASK; 1473 user_qop = saslc_sess_getprop(sess, SASLC_DIGESTMD5_QOPMASK); 1474 if (user_qop != NULL) { 1475 if (saslc__list_parse(&list, user_qop) == -1) { 1476 saslc__error_set_errno(ERR(sess), ERROR_NOMEM); 1477 return -1; 1478 } 1479 qop_flags &= saslc__mech_qop_list_flags(list); 1480 saslc__list_free(list); 1481 } 1482 1483 /* 1484 * Select the most secure supported qop. 1485 */ 1486 if ((qop_flags & F_QOP_CONF) != 0) 1487 return QOP_CONF; 1488 if ((qop_flags & F_QOP_INT) != 0) 1489 return QOP_INT; 1490 if ((qop_flags & F_QOP_NONE) != 0) 1491 return QOP_NONE; 1492 1493 saslc__error_set(ERR(sess), ERROR_MECH, 1494 "cannot choose an acceptable qop"); 1495 return -1; 1496 } 1497 /************************************************************************/ 1498 1499 /** 1500 * @brief choose the best cipher based on what was provided by the 1501 * challenge and a possible user mask. 1502 * @param sess the session context 1503 * @param cipher_flags the cipher flags parsed from the challenge 1504 * string 1505 * @return the selected cipher_t 1506 */ 1507 static int 1508 choose_cipher(saslc_sess_t *sess, unsigned int cipher_flags) 1509 { 1510 list_t *list; 1511 unsigned int cipher_mask; 1512 const char *user_cipher; 1513 1514 if (cipher_flags == 0) { 1515 saslc__error_set(ERR(sess), ERROR_MECH, 1516 "no cipher spec in challenge"); 1517 return -1; 1518 } 1519 cipher_mask = DEFAULT_CIPHER_MASK; 1520 user_cipher = saslc_sess_getprop(sess, SASLC_DIGESTMD5_CIPHERMASK); 1521 if (user_cipher != NULL) { 1522 if (saslc__list_parse(&list, user_cipher) == -1) { 1523 saslc__error_set_errno(ERR(sess), ERROR_NOMEM); 1524 return -1; 1525 } 1526 cipher_mask = cipher_list_flags(list); 1527 saslc__list_free(list); 1528 } 1529 cipher_flags &= cipher_mask; 1530 1531 /* 1532 * Select the most secure cipher supported. 1533 * XXX: Is the order here right? 1534 */ 1535 if ((cipher_flags & F_CIPHER_AES) != 0) 1536 return CIPHER_AES; 1537 if ((cipher_flags & F_CIPHER_3DES) != 0) 1538 return CIPHER_3DES; 1539 if ((cipher_flags & F_CIPHER_DES) != 0) 1540 return CIPHER_DES; 1541 if ((cipher_flags & F_CIPHER_RC4) != 0) 1542 return CIPHER_RC4; 1543 if ((cipher_flags & F_CIPHER_RC4_56) != 0) 1544 return CIPHER_RC4_56; 1545 if ((cipher_flags & F_CIPHER_RC4_40) != 0) 1546 return CIPHER_RC4_40; 1547 1548 saslc__error_set(ERR(sess), ERROR_MECH, 1549 "qop \"auth-conf\" requires a cipher"); 1550 return -1; 1551 } 1552 1553 /** 1554 * @brief get the challenge_t value corresponding to a challenge key 1555 * string. 1556 * @param key challenge key string 1557 * @return the challenge_t value including CHALLENGE_IGNORE (-1) if 1558 * the key is not recognized 1559 */ 1560 static challenge_t 1561 get_challenge_t(const char *key) 1562 { 1563 static const struct { 1564 const char *key; 1565 challenge_t value; 1566 } challenge_keys[] = { 1567 { "realm", CHALLENGE_REALM }, 1568 { "nonce", CHALLENGE_NONCE }, 1569 { "qop", CHALLENGE_QOP }, 1570 { "stale", CHALLENGE_STALE }, 1571 { "maxbuf", CHALLENGE_MAXBUF }, 1572 { "charset", CHALLENGE_CHARSET }, 1573 { "algorithm", CHALLENGE_ALGORITHM }, 1574 { "cipher", CHALLENGE_CIPHER } 1575 }; 1576 size_t i; 1577 1578 for (i = 0; i < __arraycount(challenge_keys); i++) { 1579 if (strcasecmp(key, challenge_keys[i].key) == 0) 1580 return challenge_keys[i].value; 1581 } 1582 return CHALLENGE_IGNORE; 1583 } 1584 1585 /** 1586 * @brief parses challenge and store result in mech_sess. 1587 * @param mech_sess session where parsed data will be stored 1588 * @param challenge challenge 1589 * @return 0 on success, -1 on failure. 1590 */ 1591 static int 1592 saslc__mech_digestmd5_parse_challenge(saslc_sess_t *sess, const char *challenge) 1593 { 1594 saslc__mech_digestmd5_sess_t *ms; 1595 list_t *list, *n; 1596 list_t *tmp_list; 1597 cdata_t *cdata; 1598 size_t maxbuf; 1599 uint32_t tmp_flags; 1600 int rv; 1601 1602 /******************************************************************/ 1603 /* digest-challenge = */ 1604 /* 1#( realm | nonce | qop-options | stale | server_maxbuf | */ 1605 /* charset | algorithm | cipher-opts | auth-param ) */ 1606 /******************************************************************/ 1607 1608 saslc__msg_dbg("challenge: '%s'\n", challenge); 1609 1610 ms = sess->mech_sess; 1611 cdata = &ms->cdata; 1612 1613 rv = -1; 1614 memset(cdata, 0, sizeof(*cdata)); 1615 if (saslc__list_parse(&list, challenge) == -1) { 1616 saslc__error_set_errno(ERR(sess), ERROR_NOMEM); 1617 return -1; 1618 } 1619 saslc__list_log(list, "parse list:\n"); 1620 for (n = list; n != NULL; n = n->next) { 1621 char *key; 1622 char *val; 1623 1624 /* Split string into key and val */ 1625 key = n->value; 1626 val = strchr(key, '='); 1627 if (val == NULL) 1628 goto no_mem; 1629 *val = '\0'; 1630 val = strip_quotes(val + 1); 1631 1632 saslc__msg_dbg("key='%s' val='%s'\n", key, val); 1633 switch (get_challenge_t(key)) { 1634 case CHALLENGE_REALM: 1635 /**************************************************/ 1636 /* realm = "realm" "=" <"> realm-value <"> */ 1637 /* realm-value = qdstr-val */ 1638 /* */ 1639 /* This directive is optional; if not present, */ 1640 /* the client SHOULD solicit it from the user or */ 1641 /* be able to compute a default; a plausible */ 1642 /* default might be the realm supplied by the */ 1643 /* user when they logged in to the client system. */ 1644 /* Multiple realm directives are allowed, in */ 1645 /* which case the user or client must choose one */ 1646 /* as the realm for which to supply to username */ 1647 /* and password. */ 1648 /**************************************************/ 1649 if (saslc__list_append(&cdata->realm, val) == -1) 1650 goto no_mem; 1651 break; 1652 case CHALLENGE_NONCE: 1653 /**************************************************/ 1654 /* nonce = "nonce" "=" <"> nonce-value <"> */ 1655 /* nonce-value = *qdtext */ 1656 /* */ 1657 /* This directive is required and MUST appear */ 1658 /* exactly once; if not present, or if multiple */ 1659 /* instances are present, the client should abort */ 1660 /* the authentication exchange. */ 1661 /**************************************************/ 1662 if (cdata->nonce != NULL) { 1663 saslc__error_set(ERR(sess), ERROR_MECH, 1664 "multiple nonce in challenge"); 1665 goto out; 1666 } 1667 cdata->nonce = strdup(val); 1668 if (cdata->nonce == NULL) 1669 goto no_mem; 1670 break; 1671 case CHALLENGE_QOP: 1672 /**************************************************/ 1673 /* qop-options = "qop" "=" <"> qop-list <"> */ 1674 /* qop-list = 1#qop-value */ 1675 /* qop-value = "auth" | "auth-int" | */ 1676 /* "auth-conf" | token */ 1677 /* */ 1678 /* This directive is optional; if not present it */ 1679 /* defaults to "auth". The client MUST ignore */ 1680 /* unrecognized options; if the client recognizes */ 1681 /* no option, it should abort the authentication */ 1682 /* exchange. */ 1683 /**************************************************/ 1684 if (saslc__list_parse(&tmp_list, val) == -1) 1685 goto no_mem; 1686 saslc__list_log(tmp_list, "qop list:\n"); 1687 tmp_flags = saslc__mech_qop_list_flags(tmp_list); 1688 saslc__list_free(tmp_list); 1689 if (tmp_flags == 0) { 1690 saslc__error_set(ERR(sess), ERROR_MECH, 1691 "qop required in challenge"); 1692 goto out; 1693 } 1694 cdata->qop_flags |= tmp_flags; 1695 break; 1696 case CHALLENGE_STALE: 1697 /**************************************************/ 1698 /* stale = "stale" "=" "true" */ 1699 /* */ 1700 /* This directive may appear at most once; if */ 1701 /* multiple instances are present, the client */ 1702 /* should abort the authentication exchange. */ 1703 /**************************************************/ 1704 if (cdata->stale) { 1705 saslc__error_set(ERR(sess), ERROR_MECH, 1706 "multiple stale in challenge"); 1707 goto out; 1708 } 1709 if (strcasecmp(val, "true") != 0) { 1710 saslc__error_set(ERR(sess), ERROR_MECH, 1711 "stale must be true"); 1712 goto out; 1713 } 1714 cdata->stale = true; 1715 break; 1716 case CHALLENGE_MAXBUF: 1717 /**************************************************/ 1718 /* maxbuf-value = 1*DIGIT */ 1719 /* */ 1720 /* The value MUST be bigger than 16 and smaller */ 1721 /* or equal to 16777215 (i.e. 2**24-1). If this */ 1722 /* directive is missing, the default value is */ 1723 /* 65536. This directive may appear at most once; */ 1724 /* if multiple instances are present, the client */ 1725 /* MUST abort the authentication exchange. */ 1726 /**************************************************/ 1727 if (cdata->maxbuf != 0) { 1728 saslc__error_set(ERR(sess), ERROR_MECH, 1729 "multiple maxbuf in challenge"); 1730 goto out; 1731 } 1732 maxbuf = (size_t)strtoul(val, NULL, 10); 1733 if (INVALID_MAXBUF(maxbuf)) { 1734 saslc__error_set(ERR(sess), ERROR_MECH, 1735 "invalid maxbuf in challenge"); 1736 goto out; 1737 } 1738 cdata->maxbuf = maxbuf; 1739 break; 1740 case CHALLENGE_CHARSET: 1741 /**************************************************/ 1742 /* charset = "charset" "=" "utf-8" */ 1743 /* */ 1744 /* This directive may appear at most once; if */ 1745 /* multiple instances are present, the client */ 1746 /* should abort the authentication exchange. */ 1747 /**************************************************/ 1748 if (cdata->utf8) { 1749 saslc__error_set(ERR(sess), ERROR_MECH, 1750 "multiple charset in challenge"); 1751 goto out; 1752 } 1753 if (strcasecmp(val, "utf-8") != 0) { 1754 saslc__error_set(ERR(sess), ERROR_MECH, 1755 "charset != \"utf-8\" in challenge"); 1756 goto out; 1757 } 1758 cdata->utf8 = true; 1759 break; 1760 case CHALLENGE_ALGORITHM: 1761 /**************************************************/ 1762 /* algorithm = "algorithm" "=" "md5-sess" */ 1763 /* */ 1764 /* This directive is required and MUST appear */ 1765 /* exactly once; if not present, or if multiple */ 1766 /* instances are present, the client should abort */ 1767 /* the authentication exchange. */ 1768 /**************************************************/ 1769 if (cdata->algorithm) { 1770 saslc__error_set(ERR(sess), ERROR_MECH, 1771 "multiple algorithm in challenge"); 1772 goto out; 1773 } 1774 if (strcasecmp(val, "md5-sess") != 0) { 1775 saslc__error_set(ERR(sess), ERROR_MECH, 1776 "algorithm != \"md5-sess\" in challenge"); 1777 goto out; 1778 } 1779 cdata->algorithm = true; 1780 break; 1781 case CHALLENGE_CIPHER: 1782 /**************************************************/ 1783 /* cipher-opts = "cipher" "=" <"> 1#cipher-val <">*/ 1784 /* cipher-val = "3des" | "des" | "rc4-40" | */ 1785 /* "rc4" |"rc4-56" | "aes" | */ 1786 /* token */ 1787 /* */ 1788 /* This directive must be present exactly once if */ 1789 /* "auth-conf" is offered in the "qop-options" */ 1790 /* directive, in which case the "3des" cipher is */ 1791 /* mandatory-to-implement. The client MUST ignore */ 1792 /* unrecognized options; if the client recognizes */ 1793 /* no option, it should abort the authentication */ 1794 /* exchange. */ 1795 /**************************************************/ 1796 if (saslc__list_parse(&tmp_list, val) == -1) 1797 goto no_mem; 1798 saslc__list_log(tmp_list, "cipher list:\n"); 1799 tmp_flags = cipher_list_flags(tmp_list); 1800 saslc__list_free(tmp_list); 1801 if (tmp_flags == 0) { 1802 saslc__error_set(ERR(sess), ERROR_MECH, 1803 "unknown cipher"); 1804 goto out; 1805 } 1806 cdata->cipher_flags |= tmp_flags; 1807 break; 1808 case CHALLENGE_IGNORE: 1809 /**************************************************/ 1810 /* auth-param = token "=" ( token | */ 1811 /* quoted-string ) */ 1812 /* */ 1813 /* The client MUST ignore any unrecognized */ 1814 /* directives. */ 1815 /**************************************************/ 1816 break; 1817 } 1818 } 1819 1820 /* 1821 * make sure realms are in iso8859-1 1822 */ 1823 if (stringprep_realms(cdata->utf8, cdata->realm) == -1) { 1824 saslc__error_set(ERR(sess), ERROR_MECH, 1825 "unable to convert realms in challenge from " 1826 "\"utf-8\" to iso8859-1"); 1827 goto out; 1828 } 1829 1830 /* 1831 * test for required options 1832 */ 1833 if (cdata->nonce == NULL) { 1834 saslc__error_set(ERR(sess), ERROR_MECH, 1835 "nonce required in challenge"); 1836 goto out; 1837 } 1838 1839 if (!cdata->algorithm) { 1840 saslc__error_set(ERR(sess), ERROR_MECH, 1841 "algorithm required in challenge"); 1842 goto out; 1843 } 1844 1845 /* 1846 * set the default maxbuf value if it was missing from the 1847 * challenge. 1848 */ 1849 if (cdata->maxbuf == 0) 1850 cdata->maxbuf = DEFAULT_MAXBUF; 1851 1852 saslc__msg_dbg("qop_flags=0x%04x\n", cdata->qop_flags); 1853 saslc__msg_dbg("cipher_flags=0x%04x\n", cdata->cipher_flags); 1854 1855 rv = 0; 1856 out: 1857 saslc__list_free(list); 1858 return rv; 1859 no_mem: 1860 saslc__error_set_errno(ERR(sess), ERROR_NOMEM); 1861 goto out; 1862 } 1863 1864 /** 1865 * @brief creates digestmd5 mechanism session. 1866 * Function initializes also default options for the session. 1867 * @param sess sasl session 1868 * @return 0 on success, -1 on failure. 1869 */ 1870 static int 1871 saslc__mech_digestmd5_create(saslc_sess_t *sess) 1872 { 1873 saslc__mech_digestmd5_sess_t *c; 1874 1875 if ((c = calloc(1, sizeof(*c))) == NULL) { 1876 saslc__error_set_errno(ERR(sess), ERROR_NOMEM); 1877 return -1; 1878 } 1879 c->rdata.nonce_cnt = 1; 1880 sess->mech_sess = c; 1881 1882 return 0; 1883 } 1884 1885 static void 1886 free_cdata(cdata_t *cdata) 1887 { 1888 1889 free(cdata->nonce); 1890 saslc__list_free(cdata->realm); 1891 } 1892 1893 static void 1894 free_rdata(rdata_t *rdata) 1895 { 1896 1897 free(rdata->authcid); 1898 free(rdata->authzid); 1899 free(rdata->cnonce); 1900 free(rdata->digesturi); 1901 if (rdata->passwd != NULL) { 1902 memset(rdata->passwd, 0, strlen(rdata->passwd)); 1903 free(rdata->passwd); 1904 } 1905 free(rdata->realm); 1906 } 1907 1908 /** 1909 * @brief destroys digestmd5 mechanism session. 1910 * Function also is freeing assigned resources to the session. 1911 * @param sess sasl session 1912 * @return Functions always returns 0. 1913 */ 1914 static int 1915 saslc__mech_digestmd5_destroy(saslc_sess_t *sess) 1916 { 1917 saslc__mech_digestmd5_sess_t *ms; 1918 1919 ms = sess->mech_sess; 1920 1921 free_cdata(&ms->cdata); 1922 free_rdata(&ms->rdata); 1923 1924 saslc__buffer32_destroy(ms->dec_ctx.buf_ctx); 1925 saslc__buffer_destroy(ms->enc_ctx.buf_ctx); 1926 1927 cipher_context_destroy(ms->dec_ctx.cph_ctx); 1928 cipher_context_destroy(ms->enc_ctx.cph_ctx); 1929 1930 free(sess->mech_sess); 1931 sess->mech_sess = NULL; 1932 1933 return 0; 1934 } 1935 1936 /** 1937 * @brief collect the response data necessary to build the reply. 1938 * @param sess the session context 1939 * @return 0 on success, -1 on failure 1940 * 1941 * NOTE: 1942 * The input info is from the challenge (previously saved in cdata of 1943 * saslc__mech_digestmd5_sess_t) or from the property dictionaries. 1944 * 1945 * The output info is saved in (mostly) in rdata of the 1946 * saslc__mech_digestmd5_sess_t structure. The qop is special in that 1947 * it is exposed to the saslc__mech_sess_t layer. 1948 */ 1949 static int 1950 saslc__mech_digestmd5_response_data(saslc_sess_t *sess) 1951 { 1952 saslc__mech_digestmd5_sess_t *ms; 1953 cdata_t *cdata; 1954 rdata_t *rdata; 1955 const char *authcid; 1956 const char *authzid; 1957 const char *hostname; 1958 const char *maxbuf; 1959 const char *passwd; 1960 int rv; 1961 1962 ms = sess->mech_sess; 1963 cdata = &ms->cdata; 1964 rdata = &ms->rdata; 1965 1966 if ((rv = choose_qop(sess, cdata->qop_flags)) == -1) 1967 return -1; /* error message already set */ 1968 ms->mech_sess.qop = rv; 1969 1970 if (ms->mech_sess.qop == QOP_CONF) { 1971 if ((rv = choose_cipher(sess, cdata->cipher_flags)) == -1) 1972 return -1; /* error message already set */ 1973 rdata->cipher = rv; 1974 } 1975 1976 hostname = saslc_sess_getprop(sess, SASLC_DIGESTMD5_HOSTNAME); 1977 if (hostname == NULL) { 1978 saslc__error_set(ERR(sess), ERROR_MECH, 1979 "hostname is required for authentication"); 1980 return -1; 1981 } 1982 1983 rdata->realm = choose_realm(sess, hostname, cdata->realm); 1984 if (rdata->realm == NULL) 1985 return -1; /* error message already set */ 1986 1987 rdata->digesturi = saslc__mech_digestmd5_digesturi(sess, hostname); 1988 if (rdata->digesturi == NULL) 1989 return -1; /* error message already set */ 1990 1991 authcid = saslc_sess_getprop(sess, SASLC_DIGESTMD5_AUTHCID); 1992 if (authcid == NULL) { 1993 saslc__error_set(ERR(sess), ERROR_MECH, 1994 "authcid is required for an authentication"); 1995 return -1; 1996 } 1997 rdata->authcid = strdup(authcid); 1998 if (rdata->authcid == NULL) 1999 goto no_mem; 2000 2001 authzid = saslc_sess_getprop(sess, SASLC_DIGESTMD5_AUTHZID); 2002 if (authzid != NULL) { 2003 rdata->authzid = strdup(authzid); 2004 if (rdata->authzid == NULL) 2005 goto no_mem; 2006 } 2007 2008 passwd = saslc_sess_getprop(sess, SASLC_DIGESTMD5_PASSWD); 2009 if (passwd == NULL) { 2010 saslc__error_set(ERR(sess), ERROR_MECH, 2011 "password is required for an authentication"); 2012 return -1; 2013 } 2014 rdata->passwd = strdup(passwd); 2015 if (rdata->passwd == NULL) 2016 goto no_mem; 2017 2018 rdata->cnonce = saslc__mech_digestmd5_nonce(NONCE_LEN); 2019 if (rdata->cnonce == NULL) { 2020 saslc__error_set(ERR(sess), ERROR_MECH, 2021 "failed to create cnonce"); 2022 return -1; 2023 } 2024 #ifdef SASLC_DIGESTMD5_CNONCE /* XXX: for debugging! */ 2025 { 2026 const char *cnonce; 2027 cnonce = saslc_sess_getprop(sess, SASLC_DIGESTMD5_CNONCE); 2028 if (cnonce != NULL) { 2029 rdata->cnonce = strdup(cnonce); 2030 if (rdata->cnonce == NULL) 2031 goto no_mem; 2032 } 2033 } 2034 #endif 2035 if (ms->mech_sess.qop != QOP_NONE) { 2036 maxbuf = saslc_sess_getprop(sess, SASLC_DIGESTMD5_MAXBUF); 2037 if (maxbuf != NULL) 2038 rdata->maxbuf = (size_t)strtoul(maxbuf, NULL, 10); 2039 if (rdata->maxbuf == 0) 2040 rdata->maxbuf = cdata->maxbuf; 2041 if (INVALID_MAXBUF(rdata->maxbuf)) { 2042 saslc__error_set(ERR(sess), ERROR_MECH, 2043 "maxbuf out of range"); 2044 return -1; 2045 } 2046 } 2047 return 0; 2048 2049 no_mem: 2050 saslc__error_set_errno(ERR(sess), ERROR_NOMEM); 2051 return -1; 2052 } 2053 2054 /** 2055 * @brief compute the maximum payload that can go into an integrity or 2056 * confidentiality packet. 2057 * @param maxbuf the server's maxbuf size. 2058 * @param blksize the ciphers block size. 0 or 1 if there is no blocking. 2059 * @return the payload size 2060 * 2061 * The packet (not including the leading uint32_t packet length field) 2062 * has this structure: 2063 * 2064 * struct { 2065 * uint8_t payload[]; // packet payload 2066 * uint8_t padding[]; // padding to block size 2067 * uint8_t hmac_0_9[10]; // the first 10 bytes of the hmac 2068 * uint8_t version[2]; // version number (1) in BE format 2069 * uint8_t seqnum[4]; // sequence number in BE format 2070 * } __packed 2071 * 2072 * NOTE: if the block size is > 1, then padding is required to make 2073 * the {payload[], padding[], and hmac_0_9[]} a multiple of the block 2074 * size. Furthermore there must be at least one byte of padding! The 2075 * padding bytes are all set to the padding length and one byte of 2076 * padding is necessary to recover the padding length. 2077 */ 2078 static size_t 2079 maxpayload(size_t maxbuf, size_t blksize) 2080 { 2081 size_t l; 2082 2083 if (blksize <= 1) { /* no padding used */ 2084 if (maxbuf <= sizeof(md5hash_t)) 2085 return 0; 2086 2087 return maxbuf - sizeof(md5hash_t); 2088 } 2089 if (maxbuf < 2 * blksize + 6) 2090 return 0; 2091 2092 l = rounddown(maxbuf - 6, blksize); 2093 if (l <= 10 + 1) /* we need at least one byte of padding */ 2094 return 0; 2095 2096 return l - 10 - 1; 2097 } 2098 2099 /** 2100 * @brief initialize the encode and decode coder contexts for the session 2101 * @param sess the current session 2102 * @return 0 on success, -1 on failure. 2103 */ 2104 static int 2105 init_coder_context(saslc_sess_t *sess) 2106 { 2107 saslc__mech_digestmd5_sess_t *ms; 2108 size_t blksize; 2109 #ifdef SASLC_DIGESTMD5_SELFTEST 2110 int selftest; /* XXX: allow for testing against ourselves */ 2111 #endif 2112 2113 ms = sess->mech_sess; 2114 #ifdef SASLC_DIGESTMD5_SELFTEST 2115 selftest = saslc_sess_getprop(sess, SASLC_DIGESTMD5_SELFTEST) != NULL; 2116 #endif 2117 blksize = 0; 2118 switch (ms->mech_sess.qop) { 2119 case QOP_NONE: 2120 return 0; 2121 case QOP_INT: 2122 #ifdef SASLC_DIGESTMD5_SELFTEST 2123 ms->dec_ctx.key = selftest ? ms->keys.kic : ms->keys.kis; 2124 #else 2125 ms->dec_ctx.key = ms->keys.kis; 2126 #endif 2127 ms->enc_ctx.key = ms->keys.kic; 2128 ms->dec_ctx.cph_ctx = NULL; 2129 ms->enc_ctx.cph_ctx = NULL; 2130 break; 2131 case QOP_CONF: 2132 #ifdef SASLC_DIGESTMD5_SELFTEST 2133 ms->dec_ctx.key = selftest ? ms->keys.kcc : ms->keys.kcs; 2134 #else 2135 ms->dec_ctx.key = ms->keys.kcs; 2136 #endif 2137 ms->enc_ctx.key = ms->keys.kcc; 2138 ms->dec_ctx.cph_ctx = cipher_context_create(sess, 2139 ms->rdata.cipher, 0, ms->dec_ctx.key); 2140 if (ms->dec_ctx.cph_ctx == NULL) 2141 return -1; 2142 2143 ms->enc_ctx.cph_ctx = cipher_context_create(sess, 2144 ms->rdata.cipher, 1, ms->enc_ctx.key); 2145 if (ms->enc_ctx.cph_ctx == NULL) 2146 return -1; 2147 2148 blksize = ms->enc_ctx.cph_ctx->blksize; 2149 break; 2150 } 2151 ms->dec_ctx.sess = sess; 2152 ms->enc_ctx.sess = sess; 2153 ms->dec_ctx.buf_ctx = saslc__buffer32_create(sess, ms->rdata.maxbuf); 2154 if (ms->cdata.maxbuf < 2 * blksize + 6) { 2155 saslc__error_set(ERR(sess), ERROR_MECH, 2156 "server buffer too small for packet"); 2157 return -1; 2158 } 2159 ms->enc_ctx.buf_ctx = saslc__buffer_create(sess, 2160 maxpayload(ms->cdata.maxbuf, blksize)); 2161 2162 if (ms->dec_ctx.buf_ctx == NULL || ms->enc_ctx.buf_ctx == NULL) { 2163 saslc__error_set_errno(ERR(sess), ERROR_NOMEM); 2164 return -1; 2165 } 2166 return 0; 2167 } 2168 2169 /** 2170 * @brief construct the reply string. 2171 * @param sess session context 2172 * @param response string 2173 * @return reply string or NULL on failure. 2174 */ 2175 static char * 2176 saslc__mech_digestmd5_reply(saslc_sess_t *sess, char *response) 2177 { 2178 saslc__mech_digestmd5_sess_t *ms; 2179 char *out; 2180 char *cipher, *maxbuf, *realm; 2181 2182 ms = sess->mech_sess; 2183 2184 out = NULL; 2185 cipher = __UNCONST(""); 2186 maxbuf = __UNCONST(""); 2187 realm = __UNCONST(""); 2188 2189 switch (ms->mech_sess.qop) { 2190 case QOP_CONF: 2191 if (asprintf(&cipher, "cipher=\"%s\",", 2192 cipher_name(ms->rdata.cipher)) == -1) { 2193 saslc__error_set_errno(ERR(sess), ERROR_NOMEM); 2194 goto done; 2195 } 2196 /*FALLTHROUGH*/ 2197 case QOP_INT: 2198 if (asprintf(&maxbuf, "maxbuf=%zu,", ms->rdata.maxbuf) == -1) { 2199 saslc__error_set_errno(ERR(sess), ERROR_NOMEM); 2200 goto done; 2201 } 2202 break; 2203 case QOP_NONE: 2204 break; 2205 default: 2206 assert(/*CONSTCOND*/0); 2207 return NULL; 2208 } 2209 if (ms->rdata.realm != NULL && 2210 asprintf(&realm, "realm=\"%s\",", ms->rdata.realm) == -1) { 2211 saslc__error_set_errno(ERR(sess), ERROR_NOMEM); 2212 goto done; 2213 } 2214 2215 if (asprintf(&out, 2216 "username=\"%s\"," 2217 "%s" /* realm= */ 2218 "nonce=\"%s\"," 2219 "cnonce=\"%s\"," 2220 "nc=%08d," 2221 "qop=%s," 2222 "%s" /* cipher= */ 2223 "%s" /* maxbuf= */ 2224 "digest-uri=\"%s\"," 2225 "response=%s", 2226 ms->rdata.authcid, 2227 realm, 2228 ms->cdata.nonce, 2229 ms->rdata.cnonce, 2230 ms->rdata.nonce_cnt, 2231 saslc__mech_qop_name(ms->mech_sess.qop), 2232 cipher, 2233 maxbuf, 2234 ms->rdata.digesturi, 2235 response 2236 ) == -1) { 2237 saslc__error_set_errno(ERR(sess), ERROR_NOMEM); 2238 out = NULL; 2239 } 2240 done: 2241 if (realm[0] != '\0') 2242 free(realm); 2243 if (maxbuf[0] != '\0') 2244 free(maxbuf); 2245 if (cipher[0] != '\0') 2246 free(cipher); 2247 2248 return out; 2249 } 2250 2251 /** 2252 * @brief do one step of the sasl authentication 2253 * @param sess sasl session 2254 * @param in input data 2255 * @param inlen input data length 2256 * @param out place to store output data 2257 * @param outlen output data length 2258 * @return MECH_OK - authentication successful, 2259 * MECH_STEP - more steps are needed, 2260 * MECH_ERROR - error 2261 */ 2262 static int 2263 saslc__mech_digestmd5_cont(saslc_sess_t *sess, const void *in, size_t inlen, 2264 void **out, size_t *outlen) 2265 { 2266 saslc__mech_digestmd5_sess_t *ms; 2267 char *response; 2268 const char *p; 2269 2270 ms = sess->mech_sess; 2271 2272 switch(ms->mech_sess.step) { 2273 case 0: 2274 /* in case we are called before getting data from server */ 2275 if (inlen == 0) { 2276 *out = NULL; 2277 *outlen = 0; 2278 return MECH_STEP; 2279 } 2280 /* if input data was provided, then doing the first step */ 2281 ms->mech_sess.step++; 2282 /*FALLTHROUGH*/ 2283 case 1: 2284 if (saslc__mech_digestmd5_parse_challenge(sess, in) == -1) 2285 return MECH_ERROR; 2286 2287 if (saslc__mech_digestmd5_response_data(sess) == -1) 2288 return MECH_ERROR; 2289 2290 if ((response = saslc__mech_digestmd5_response(ms, 2291 "AUTHENTICATE")) == NULL) { 2292 saslc__error_set(ERR(sess), ERROR_MECH, 2293 "unable to construct response"); 2294 return MECH_ERROR; 2295 } 2296 *out = saslc__mech_digestmd5_reply(sess, response); 2297 free(response); 2298 if (*out == NULL) 2299 return MECH_ERROR; 2300 2301 *outlen = strlen(*out); 2302 return MECH_STEP; 2303 case 2: 2304 if ((response = saslc__mech_digestmd5_response(ms, "")) 2305 == NULL) { 2306 saslc__error_set(ERR(sess), ERROR_MECH, 2307 "unable to construct rspauth"); 2308 return MECH_ERROR; 2309 } 2310 p = in; 2311 if (strncmp(p, "rspauth=", 8) != 0 || 2312 strcmp(response, p + 8) != 0) { 2313 saslc__msg_dbg("rspauth='%s'\n", response); 2314 saslc__error_set(ERR(sess), ERROR_MECH, 2315 "failed to validate rspauth response"); 2316 free(response); 2317 return MECH_ERROR; 2318 } 2319 free(response); 2320 if (init_coder_context(sess) == -1) 2321 return MECH_ERROR; 2322 *out = NULL; 2323 *outlen = 0; 2324 return MECH_OK; 2325 default: 2326 assert(/*CONSTCOND*/0); /* impossible */ 2327 return MECH_ERROR; 2328 } 2329 } 2330 2331 /* mechanism definition */ 2332 const saslc__mech_t saslc__mech_digestmd5 = { 2333 .name = "DIGEST-MD5", 2334 .flags = FLAG_MUTUAL | FLAG_DICTIONARY, 2335 .create = saslc__mech_digestmd5_create, 2336 .cont = saslc__mech_digestmd5_cont, 2337 .encode = saslc__mech_digestmd5_encode, 2338 .decode = saslc__mech_digestmd5_decode, 2339 .destroy = saslc__mech_digestmd5_destroy 2340 }; 2341