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