1 /* 2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 7 /* 8 * lib/gssapi/krb5/k5sealv3.c 9 * 10 * Copyright 2003,2004 by the Massachusetts Institute of Technology. 11 * All Rights Reserved. 12 * 13 * Export of this software from the United States of America may 14 * require a specific license from the United States Government. 15 * It is the responsibility of any person or organization contemplating 16 * export to obtain such a license before exporting. 17 * 18 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 19 * distribute this software and its documentation for any purpose and 20 * without fee is hereby granted, provided that the above copyright 21 * notice appear in all copies and that both that copyright notice and 22 * this permission notice appear in supporting documentation, and that 23 * the name of M.I.T. not be used in advertising or publicity pertaining 24 * to distribution of the software without specific, written prior 25 * permission. Furthermore if you modify this software you must label 26 * your software as modified software and not distribute it in such a 27 * fashion that it might be confused with the original M.I.T. software. 28 * M.I.T. makes no representations about the suitability of 29 * this software for any purpose. It is provided "as is" without express 30 * or implied warranty. 31 * 32 * 33 */ 34 /* draft-ietf-krb-wg-gssapi-cfx-05 */ 35 36 #ifndef _KERNEL 37 #include <assert.h> 38 #include <stdarg.h> 39 40 #define ASSERT assert 41 #endif 42 43 /* Solaris Kerberos */ 44 #include "k5-int.h" /* for zap() */ 45 #include "k5-platform.h" 46 47 /* Solaris Kerberos */ 48 #include "k5-platform-store_16.h" 49 #include "k5-platform-store_64.h" 50 #include "k5-platform-load_16.h" 51 #include "k5-platform-load_64.h" 52 53 #include "gssapiP_krb5.h" 54 #include <sys/int_limits.h> 55 56 static int 57 rotate_left (void *ptr, size_t bufsiz, size_t rc) 58 { 59 /* Optimize for receiving. After some debugging is done, the MIT 60 implementation won't do any rotates on sending, and while 61 debugging, they'll be randomly chosen. 62 63 Return 1 for success, 0 for failure (ENOMEM). */ 64 void *tbuf; 65 66 if (bufsiz == 0) 67 return 1; 68 rc = rc % bufsiz; 69 if (rc == 0) 70 return 1; 71 72 tbuf = MALLOC(rc); 73 if (tbuf == 0) 74 return 0; 75 (void) memcpy(tbuf, ptr, rc); 76 (void) memmove(ptr, (char *)ptr + rc, bufsiz - rc); 77 (void) memcpy((char *)ptr + bufsiz - rc, tbuf, rc); 78 FREE(tbuf, rc); 79 return 1; 80 } 81 82 static const gss_buffer_desc empty_message = { 0, 0 }; 83 84 #define FLAG_SENDER_IS_ACCEPTOR 0x01 85 #define FLAG_WRAP_CONFIDENTIAL 0x02 86 #define FLAG_ACCEPTOR_SUBKEY 0x04 87 88 krb5_error_code 89 gss_krb5int_make_seal_token_v3 (krb5_context context, 90 krb5_gss_ctx_id_rec *ctx, 91 const gss_buffer_desc * message, 92 gss_buffer_t token, 93 int conf_req_flag, int toktype) 94 { 95 size_t bufsize = 16; 96 unsigned char *outbuf = 0; 97 krb5_error_code err; 98 int key_usage; 99 unsigned char acceptor_flag; 100 const gss_buffer_desc *message2 = message; 101 #ifdef CFX_EXERCISE 102 size_t rrc; 103 #endif 104 size_t ec; 105 unsigned short tok_id; 106 krb5_checksum sum; 107 krb5_keyblock *key; 108 109 ASSERT(toktype != KG_TOK_SEAL_MSG || ctx->enc != 0); 110 ASSERT(ctx->big_endian == 0); 111 112 acceptor_flag = ctx->initiate ? 0 : FLAG_SENDER_IS_ACCEPTOR; 113 key_usage = (toktype == KG_TOK_WRAP_MSG 114 ? (ctx->initiate 115 ? KG_USAGE_INITIATOR_SEAL 116 : KG_USAGE_ACCEPTOR_SEAL) 117 : (ctx->initiate 118 ? KG_USAGE_INITIATOR_SIGN 119 : KG_USAGE_ACCEPTOR_SIGN)); 120 if (ctx->have_acceptor_subkey) { 121 key = ctx->acceptor_subkey; 122 } else { 123 key = ctx->enc; 124 } 125 126 #ifdef _KERNEL 127 context->kef_cipher_mt = get_cipher_mech_type(context, key); 128 context->kef_hash_mt = get_hash_mech_type(context, key); 129 130 if ((err = init_key_kef(context->kef_cipher_mt, key))) { 131 return (GSS_S_FAILURE); 132 } 133 134 #endif /* _KERNEL */ 135 136 #ifdef CFX_EXERCISE 137 { 138 static int initialized = 0; 139 if (!initialized) { 140 srand(time(0)); 141 initialized = 1; 142 } 143 } 144 #endif 145 146 if (toktype == KG_TOK_WRAP_MSG && conf_req_flag) { 147 krb5_data plain; 148 krb5_enc_data cipher; 149 size_t ec_max; 150 size_t tlen; 151 152 /* 300: Adds some slop. */ 153 if (SIZE_MAX - 300 < message->length) 154 return ENOMEM; 155 ec_max = SIZE_MAX - message->length - 300; 156 if (ec_max > 0xffff) 157 ec_max = 0xffff; 158 /* 159 * EC should really be a multiple (1) of the number of octets that 160 * the cryptosystem would pad by if we didn't have the filler. 161 * 162 * For AES-CTS this will always be 0 and we expect no further 163 * enctypes, so there should be no issue here. 164 */ 165 ec = 0; 166 plain.length = message->length + 16 + ec; 167 plain.data = MALLOC(plain.length); 168 if (plain.data == NULL) 169 return ENOMEM; 170 171 /* Get size of ciphertext. */ 172 if ((err = krb5_c_encrypt_length(context, 173 ctx->enc->enctype, plain.length, &tlen))) { 174 FREE(plain.data, plain.length); 175 return (err); 176 } 177 178 bufsize = 16 + tlen; 179 /* Allocate space for header plus encrypted data. */ 180 outbuf = MALLOC(bufsize); 181 if (outbuf == NULL) { 182 FREE(plain.data, plain.length); 183 return ENOMEM; 184 } 185 186 /* TOK_ID */ 187 store_16_be(0x0504, outbuf); 188 /* flags */ 189 outbuf[2] = (acceptor_flag 190 | (conf_req_flag ? FLAG_WRAP_CONFIDENTIAL : 0) 191 | (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0)); 192 /* filler */ 193 outbuf[3] = 0xff; 194 /* EC */ 195 store_16_be(ec, outbuf+4); 196 /* RRC */ 197 store_16_be(0, outbuf+6); 198 store_64_be(ctx->seq_send, outbuf+8); 199 200 (void) memcpy(plain.data, message->value, message->length); 201 (void) memset(plain.data + message->length, 'x', ec); 202 (void) memcpy(plain.data + message->length + ec, outbuf, 16); 203 204 /* Should really use scatter/gather crypto interfaces */ 205 cipher.ciphertext.data = (char *)outbuf + 16; 206 cipher.ciphertext.length = bufsize - 16; 207 cipher.enctype = key->enctype; 208 err = krb5_c_encrypt(context, key, key_usage, 0, &plain, &cipher); 209 (void) bzero(plain.data, plain.length); 210 FREE(plain.data, plain.length); 211 plain.data = 0; 212 if (err) 213 goto error; 214 215 /* Now that we know we're returning a valid token.... */ 216 ctx->seq_send++; 217 218 #ifdef CFX_EXERCISE 219 rrc = rand() & 0xffff; 220 if (rotate_left(outbuf+16, bufsize-16, 221 (bufsize-16) - (rrc % (bufsize - 16)))) 222 store_16_be(rrc, outbuf+6); 223 /* If the rotate fails, don't worry about it. */ 224 #endif 225 } else if (toktype == KG_TOK_WRAP_MSG && !conf_req_flag) { 226 krb5_data plain; 227 228 /* Here, message is the application-supplied data; message2 is 229 what goes into the output token. They may be the same, or 230 message2 may be empty (for MIC). */ 231 232 tok_id = 0x0504; 233 234 wrap_with_checksum: 235 plain.length = message->length + 16; 236 plain.data = MALLOC(message->length + 16); 237 if (plain.data == NULL) 238 return ENOMEM; 239 240 if (ctx->cksum_size > 0xffff) { 241 FREE(plain.data, plain.length); 242 return EINVAL; 243 } 244 245 bufsize = 16 + message2->length + ctx->cksum_size; 246 outbuf = MALLOC(bufsize); 247 if (outbuf == NULL) { 248 FREE(plain.data, plain.length); 249 plain.data = 0; 250 err = ENOMEM; 251 goto error; 252 } 253 254 /* TOK_ID */ 255 store_16_be(tok_id, outbuf); 256 /* flags */ 257 outbuf[2] = (acceptor_flag 258 | (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0)); 259 /* filler */ 260 outbuf[3] = 0xff; 261 if (toktype == KG_TOK_WRAP_MSG) { 262 /* Use 0 for checksum calculation, substitute 263 checksum length later. */ 264 /* EC */ 265 store_16_be(0, outbuf+4); 266 /* RRC */ 267 store_16_be(0, outbuf+6); 268 } else { 269 /* MIC and DEL store 0xFF in EC and RRC. */ 270 store_16_be(0xffff, outbuf+4); 271 store_16_be(0xffff, outbuf+6); 272 } 273 store_64_be(ctx->seq_send, outbuf+8); 274 275 (void) memcpy(plain.data, message->value, message->length); 276 (void) memcpy(plain.data + message->length, outbuf, 16); 277 278 /* Fill in the output token -- data contents, if any, and 279 space for the checksum. */ 280 if (message2->length) 281 (void) memcpy(outbuf + 16, message2->value, message2->length); 282 283 sum.contents = outbuf + 16 + message2->length; 284 sum.length = ctx->cksum_size; 285 286 err = krb5_c_make_checksum(context, ctx->cksumtype, key, 287 key_usage, &plain, &sum); 288 bzero(plain.data, plain.length); 289 FREE(plain.data, plain.length); 290 plain.data = 0; 291 if (err) { 292 bzero(outbuf,bufsize); 293 err = KRB5KRB_AP_ERR_BAD_INTEGRITY; 294 goto error; 295 } 296 if (sum.length != ctx->cksum_size) { 297 err = KRB5KRB_AP_ERR_BAD_INTEGRITY; 298 goto error; 299 } 300 (void) memcpy(outbuf + 16 + message2->length, sum.contents, 301 ctx->cksum_size); 302 krb5_free_checksum_contents(context, &sum); 303 sum.contents = 0; 304 /* Now that we know we're actually generating the token... */ 305 ctx->seq_send++; 306 307 if (toktype == KG_TOK_WRAP_MSG) { 308 #ifdef CFX_EXERCISE 309 rrc = rand() & 0xffff; 310 /* If the rotate fails, don't worry about it. */ 311 if (rotate_left(outbuf+16, bufsize-16, 312 (bufsize-16) - (rrc % (bufsize - 16)))) 313 store_16_be(rrc, outbuf+6); 314 #endif 315 /* Fix up EC field. */ 316 store_16_be(ctx->cksum_size, outbuf+4); 317 } else { 318 store_16_be(0xffff, outbuf+6); 319 } 320 } else if (toktype == KG_TOK_MIC_MSG) { 321 tok_id = 0x0404; 322 message2 = &empty_message; 323 goto wrap_with_checksum; 324 } else if (toktype == KG_TOK_DEL_CTX) { 325 /* 326 * Solaris Kerberos: 327 * No token should be generated for context deletion. Just 328 * return. 329 */ 330 return 0; 331 } else { 332 err = KRB5KRB_AP_ERR_BAD_INTEGRITY; 333 goto error; 334 } 335 336 token->value = outbuf; 337 token->length = bufsize; 338 return 0; 339 340 error: 341 FREE(outbuf, bufsize); 342 token->value = NULL; 343 token->length = 0; 344 return err; 345 } 346 347 /* message_buffer is an input if SIGN, output if SEAL, and ignored if DEL_CTX 348 conf_state is only valid if SEAL. */ 349 350 OM_uint32 351 gss_krb5int_unseal_token_v3(krb5_context *contextptr, 352 OM_uint32 *minor_status, 353 krb5_gss_ctx_id_rec *ctx, 354 unsigned char *ptr, int bodysize, 355 gss_buffer_t message_buffer, 356 int *conf_state, int *qop_state, int toktype) 357 { 358 krb5_context context = *contextptr; 359 krb5_data plain; 360 gssint_uint64 seqnum; 361 size_t ec, rrc; 362 int key_usage; 363 unsigned char acceptor_flag; 364 krb5_checksum sum; 365 krb5_error_code err; 366 krb5_boolean valid; 367 krb5_keyblock *key; 368 369 ASSERT(toktype != KG_TOK_SEAL_MSG || ctx->enc != 0); 370 ASSERT(ctx->big_endian == 0); 371 ASSERT(ctx->proto == 1); 372 373 if (qop_state) 374 *qop_state = GSS_C_QOP_DEFAULT; 375 376 acceptor_flag = ctx->initiate ? FLAG_SENDER_IS_ACCEPTOR : 0; 377 key_usage = (toktype == KG_TOK_WRAP_MSG 378 ? (!ctx->initiate 379 ? KG_USAGE_INITIATOR_SEAL 380 : KG_USAGE_ACCEPTOR_SEAL) 381 : (!ctx->initiate 382 ? KG_USAGE_INITIATOR_SIGN 383 : KG_USAGE_ACCEPTOR_SIGN)); 384 385 /* Oops. I wrote this code assuming ptr would be at the start of 386 the token header. */ 387 ptr -= 2; 388 bodysize += 2; 389 390 if (bodysize < 16) { 391 defective: 392 *minor_status = 0; 393 return GSS_S_DEFECTIVE_TOKEN; 394 } 395 if ((ptr[2] & FLAG_SENDER_IS_ACCEPTOR) != acceptor_flag) { 396 *minor_status = (OM_uint32)G_BAD_DIRECTION; 397 return GSS_S_BAD_SIG; 398 } 399 400 /* Two things to note here. 401 402 First, we can't really enforce the use of the acceptor's subkey, 403 if we're the acceptor; the initiator may have sent messages 404 before getting the subkey. We could probably enforce it if 405 we're the initiator. 406 407 Second, if someone tweaks the code to not set the flag telling 408 the krb5 library to generate a new subkey in the AP-REP 409 message, the MIT library may include a subkey anyways -- 410 namely, a copy of the AP-REQ subkey, if it was provided. So 411 the initiator may think we wanted a subkey, and set the flag, 412 even though we weren't trying to set the subkey. The "other" 413 key, the one not asserted by the acceptor, will have the same 414 value in that case, though, so we can just ignore the flag. */ 415 if (ctx->have_acceptor_subkey && (ptr[2] & FLAG_ACCEPTOR_SUBKEY)) { 416 key = ctx->acceptor_subkey; 417 } else { 418 key = ctx->enc; 419 } 420 421 #ifdef _KERNEL 422 context->kef_cipher_mt = get_cipher_mech_type(context, key); 423 context->kef_hash_mt = get_hash_mech_type(context, key); 424 425 if ((err = init_key_kef(context->kef_cipher_mt, key))) { 426 return (GSS_S_FAILURE); 427 } 428 #endif /* _KERNEL */ 429 430 if (toktype == KG_TOK_WRAP_MSG) { 431 if (load_16_be(ptr) != 0x0504) 432 goto defective; 433 if (ptr[3] != 0xff) 434 goto defective; 435 ec = load_16_be(ptr+4); 436 rrc = load_16_be(ptr+6); 437 seqnum = load_64_be(ptr+8); 438 if (!rotate_left(ptr+16, bodysize-16, rrc)) { 439 no_mem: 440 *minor_status = ENOMEM; 441 return GSS_S_FAILURE; 442 } 443 if (ptr[2] & FLAG_WRAP_CONFIDENTIAL) { 444 /* confidentiality */ 445 krb5_enc_data cipher; 446 unsigned char *althdr; 447 size_t plainlen; 448 449 if (conf_state) 450 *conf_state = 1; 451 /* Do we have no decrypt_size function? 452 453 For all current cryptosystems, the ciphertext size will 454 be larger than the plaintext size. */ 455 cipher.enctype = key->enctype; 456 cipher.ciphertext.length = bodysize - 16; 457 cipher.ciphertext.data = (char *)ptr + 16; 458 plain.length = plainlen = bodysize - 16; 459 plain.data = MALLOC(plain.length); 460 if (plain.data == NULL) 461 goto no_mem; 462 err = krb5_c_decrypt(context, key, key_usage, 0, 463 &cipher, &plain); 464 if (err) { 465 goto error; 466 } 467 /* Don't use bodysize here! Use the fact that 468 plain.length has been adjusted to the 469 correct length. */ 470 althdr = (uchar_t *)plain.data + plain.length - 16; 471 if (load_16_be(althdr) != 0x0504 472 || althdr[2] != ptr[2] 473 || althdr[3] != ptr[3] 474 || memcmp(althdr+8, ptr+8, 8)) { 475 FREE(plain.data, plainlen); 476 goto defective; 477 } 478 message_buffer->length = plain.length - ec - 16; 479 message_buffer->value = MALLOC(message_buffer->length); 480 if (message_buffer->value == NULL) { 481 FREE(plain.data, plainlen); 482 goto no_mem; 483 } 484 (void) memcpy(message_buffer->value, plain.data, 485 message_buffer->length); 486 FREE(plain.data, plainlen); 487 } else { 488 /* no confidentiality */ 489 if (conf_state) 490 *conf_state = 0; 491 if (ec + 16 < ec) 492 /* overflow check */ 493 goto defective; 494 if (ec + 16 > bodysize) 495 goto defective; 496 /* We have: header | msg | cksum. 497 We need cksum(msg | header). 498 Rotate the first two. */ 499 store_16_be(0, ptr+4); 500 store_16_be(0, ptr+6); 501 plain.length = bodysize-ec; 502 plain.data = (char *)ptr; 503 if (!rotate_left(ptr, bodysize-ec, 16)) 504 goto no_mem; 505 sum.length = ec; 506 if (sum.length != ctx->cksum_size) { 507 *minor_status = 0; 508 return GSS_S_BAD_SIG; 509 } 510 sum.contents = ptr+bodysize-ec; 511 sum.checksum_type = ctx->cksumtype; 512 err = krb5_c_verify_checksum(context, key, key_usage, 513 &plain, &sum, &valid); 514 if (err) { 515 *minor_status = err; 516 return GSS_S_BAD_SIG; 517 } 518 if (!valid) { 519 *minor_status = 0; 520 return GSS_S_BAD_SIG; 521 } 522 message_buffer->length = plain.length - 16; 523 message_buffer->value = MALLOC(message_buffer->length); 524 if (message_buffer->value == NULL) 525 goto no_mem; 526 (void) memcpy(message_buffer->value, 527 plain.data, message_buffer->length); 528 529 /* 530 * Solaris Kerberos: Restore the original token. 531 * This allows the token to be detected as a duplicate if it 532 * is passed in to gss_unwrap() again. 533 */ 534 if (!rotate_left(ptr, bodysize-ec, bodysize - ec - 16)) 535 goto no_mem; 536 store_16_be(ec, ptr+4); 537 store_16_be(rrc, ptr+6); 538 } 539 err = g_order_check(&ctx->seqstate, seqnum); 540 *minor_status = 0; 541 return err; 542 } else if (toktype == KG_TOK_MIC_MSG) { 543 /* wrap token, no confidentiality */ 544 if (load_16_be(ptr) != 0x0404) 545 goto defective; 546 verify_mic_1: 547 if (ptr[3] != 0xff) 548 goto defective; 549 if (load_32_be(ptr+4) != (ulong_t)0xffffffffU) 550 goto defective; 551 seqnum = load_64_be(ptr+8); 552 plain.length = message_buffer->length + 16; 553 plain.data = MALLOC(plain.length); 554 if (plain.data == NULL) 555 goto no_mem; 556 if (message_buffer->length) 557 (void) memcpy(plain.data, 558 message_buffer->value, message_buffer->length); 559 (void) memcpy(plain.data + message_buffer->length, ptr, 16); 560 sum.length = bodysize - 16; 561 sum.contents = ptr + 16; 562 sum.checksum_type = ctx->cksumtype; 563 err = krb5_c_verify_checksum(context, key, key_usage, 564 &plain, &sum, &valid); 565 if (err) { 566 error: 567 FREE(plain.data, plain.length); 568 *minor_status = err; 569 return GSS_S_BAD_SIG; /* XXX */ 570 } 571 FREE(plain.data, plain.length); 572 if (!valid) { 573 *minor_status = 0; 574 return GSS_S_BAD_SIG; 575 } 576 err = g_order_check(&ctx->seqstate, seqnum); 577 *minor_status = 0; 578 return err; 579 } else if (toktype == KG_TOK_DEL_CTX) { 580 if (load_16_be(ptr) != 0x0405) 581 goto defective; 582 message_buffer = (gss_buffer_t)&empty_message; 583 goto verify_mic_1; 584 } else { 585 goto defective; 586 } 587 } 588