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 size_t ec; 95 unsigned short tok_id; 96 krb5_checksum sum; 97 krb5_keyblock *key; 98 99 ASSERT(toktype != KG_TOK_SEAL_MSG || ctx->enc != 0); 100 ASSERT(ctx->big_endian == 0); 101 102 acceptor_flag = ctx->initiate ? 0 : FLAG_SENDER_IS_ACCEPTOR; 103 key_usage = (toktype == KG_TOK_WRAP_MSG 104 ? (ctx->initiate 105 ? KG_USAGE_INITIATOR_SEAL 106 : KG_USAGE_ACCEPTOR_SEAL) 107 : (ctx->initiate 108 ? KG_USAGE_INITIATOR_SIGN 109 : KG_USAGE_ACCEPTOR_SIGN)); 110 if (ctx->have_acceptor_subkey) { 111 key = ctx->acceptor_subkey; 112 } else { 113 key = ctx->enc; 114 } 115 116 #ifdef _KERNEL 117 context->kef_cipher_mt = get_cipher_mech_type(context, key); 118 context->kef_hash_mt = get_hash_mech_type(context, key); 119 120 if ((err = init_key_kef(context->kef_cipher_mt, key))) { 121 return (GSS_S_FAILURE); 122 } 123 124 #endif /* _KERNEL */ 125 126 #ifdef CFX_EXERCISE 127 { 128 static int initialized = 0; 129 if (!initialized) { 130 srand(time(0)); 131 initialized = 1; 132 } 133 } 134 #endif 135 136 if (toktype == KG_TOK_WRAP_MSG && conf_req_flag) { 137 krb5_data plain; 138 krb5_enc_data cipher; 139 size_t ec_max; 140 size_t tlen; 141 142 /* 300: Adds some slop. */ 143 if (SIZE_MAX - 300 < message->length) 144 return ENOMEM; 145 ec_max = SIZE_MAX - message->length - 300; 146 if (ec_max > 0xffff) 147 ec_max = 0xffff; 148 /* 149 * EC should really be a multiple (1) of the number of octets that 150 * the cryptosystem would pad by if we didn't have the filler. 151 * 152 * For AES-CTS this will always be 0 and we expect no further 153 * enctypes, so there should be no issue here. 154 */ 155 ec = 0; 156 plain.length = message->length + 16 + ec; 157 plain.data = MALLOC(plain.length); 158 if (plain.data == NULL) 159 return ENOMEM; 160 161 /* Get size of ciphertext. */ 162 if ((err = krb5_c_encrypt_length(context, 163 ctx->enc->enctype, plain.length, &tlen))) { 164 FREE(plain.data, plain.length); 165 return (err); 166 } 167 168 bufsize = 16 + tlen; 169 /* Allocate space for header plus encrypted data. */ 170 outbuf = MALLOC(bufsize); 171 if (outbuf == NULL) { 172 FREE(plain.data, plain.length); 173 return ENOMEM; 174 } 175 176 /* TOK_ID */ 177 store_16_be(0x0504, outbuf); 178 /* flags */ 179 outbuf[2] = (acceptor_flag 180 | (conf_req_flag ? FLAG_WRAP_CONFIDENTIAL : 0) 181 | (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0)); 182 /* filler */ 183 outbuf[3] = 0xff; 184 /* EC */ 185 store_16_be(ec, outbuf+4); 186 /* RRC */ 187 store_16_be(0, outbuf+6); 188 store_64_be(ctx->seq_send, outbuf+8); 189 190 (void) memcpy(plain.data, message->value, message->length); 191 (void) memset(plain.data + message->length, 'x', ec); 192 (void) memcpy(plain.data + message->length + ec, outbuf, 16); 193 194 /* Should really use scatter/gather crypto interfaces */ 195 cipher.ciphertext.data = (char *)outbuf + 16; 196 cipher.ciphertext.length = bufsize - 16; 197 cipher.enctype = key->enctype; 198 err = krb5_c_encrypt(context, key, key_usage, 0, &plain, &cipher); 199 (void) bzero(plain.data, plain.length); 200 FREE(plain.data, plain.length); 201 plain.data = 0; 202 if (err) 203 goto error; 204 205 /* Now that we know we're returning a valid token.... */ 206 ctx->seq_send++; 207 208 #ifdef CFX_EXERCISE 209 rrc = rand() & 0xffff; 210 if (rotate_left(outbuf+16, bufsize-16, 211 (bufsize-16) - (rrc % (bufsize - 16)))) 212 store_16_be(rrc, outbuf+6); 213 /* If the rotate fails, don't worry about it. */ 214 #endif 215 } else if (toktype == KG_TOK_WRAP_MSG && !conf_req_flag) { 216 krb5_data plain; 217 218 /* Here, message is the application-supplied data; message2 is 219 what goes into the output token. They may be the same, or 220 message2 may be empty (for MIC). */ 221 222 tok_id = 0x0504; 223 224 wrap_with_checksum: 225 plain.length = message->length + 16; 226 plain.data = MALLOC(message->length + 16); 227 if (plain.data == NULL) 228 return ENOMEM; 229 230 if (ctx->cksum_size > 0xffff) { 231 FREE(plain.data, plain.length); 232 return EINVAL; 233 } 234 235 bufsize = 16 + message2->length + ctx->cksum_size; 236 outbuf = MALLOC(bufsize); 237 if (outbuf == NULL) { 238 FREE(plain.data, plain.length); 239 plain.data = 0; 240 err = ENOMEM; 241 goto error; 242 } 243 244 /* TOK_ID */ 245 store_16_be(tok_id, outbuf); 246 /* flags */ 247 outbuf[2] = (acceptor_flag 248 | (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0)); 249 /* filler */ 250 outbuf[3] = 0xff; 251 if (toktype == KG_TOK_WRAP_MSG) { 252 /* Use 0 for checksum calculation, substitute 253 checksum length later. */ 254 /* EC */ 255 store_16_be(0, outbuf+4); 256 /* RRC */ 257 store_16_be(0, outbuf+6); 258 } else { 259 /* MIC and DEL store 0xFF in EC and RRC. */ 260 store_16_be(0xffff, outbuf+4); 261 store_16_be(0xffff, outbuf+6); 262 } 263 store_64_be(ctx->seq_send, outbuf+8); 264 265 (void) memcpy(plain.data, message->value, message->length); 266 (void) memcpy(plain.data + message->length, outbuf, 16); 267 268 /* Fill in the output token -- data contents, if any, and 269 space for the checksum. */ 270 if (message2->length) 271 (void) memcpy(outbuf + 16, message2->value, message2->length); 272 273 sum.contents = outbuf + 16 + message2->length; 274 sum.length = ctx->cksum_size; 275 276 err = krb5_c_make_checksum(context, ctx->cksumtype, key, 277 key_usage, &plain, &sum); 278 279 bzero(plain.data, plain.length); 280 FREE(plain.data, plain.length); 281 plain.data = 0; 282 if (err) { 283 bzero(outbuf,bufsize); 284 err = KRB5KRB_AP_ERR_BAD_INTEGRITY; 285 goto error; 286 } 287 if (sum.length != ctx->cksum_size) { 288 err = KRB5KRB_AP_ERR_BAD_INTEGRITY; 289 goto error; 290 } 291 (void) memcpy(outbuf + 16 + message2->length, sum.contents, 292 ctx->cksum_size); 293 krb5_free_checksum_contents(context, &sum); 294 sum.contents = 0; 295 /* Now that we know we're actually generating the token... */ 296 ctx->seq_send++; 297 298 if (toktype == KG_TOK_WRAP_MSG) { 299 #ifdef CFX_EXERCISE 300 rrc = rand() & 0xffff; 301 /* If the rotate fails, don't worry about it. */ 302 if (rotate_left(outbuf+16, bufsize-16, 303 (bufsize-16) - (rrc % (bufsize - 16)))) 304 store_16_be(rrc, outbuf+6); 305 #endif 306 /* Fix up EC field. */ 307 store_16_be(ctx->cksum_size, outbuf+4); 308 } else { 309 store_16_be(0xffff, outbuf+6); 310 } 311 } else if (toktype == KG_TOK_MIC_MSG) { 312 tok_id = 0x0404; 313 message2 = &empty_message; 314 goto wrap_with_checksum; 315 } else if (toktype == KG_TOK_DEL_CTX) { 316 /* 317 * Solaris Kerberos: 318 * No token should be generated for context deletion. Just 319 * return. 320 */ 321 return 0; 322 } else { 323 err = KRB5KRB_AP_ERR_BAD_INTEGRITY; 324 goto error; 325 } 326 327 token->value = outbuf; 328 token->length = bufsize; 329 return 0; 330 331 error: 332 FREE(outbuf, bufsize); 333 token->value = NULL; 334 token->length = 0; 335 return err; 336 } 337 338 /* message_buffer is an input if SIGN, output if SEAL, and ignored if DEL_CTX 339 conf_state is only valid if SEAL. */ 340 341 OM_uint32 342 gss_krb5int_unseal_token_v3(krb5_context context, 343 OM_uint32 *minor_status, 344 krb5_gss_ctx_id_rec *ctx, 345 unsigned char *ptr, int bodysize, 346 gss_buffer_t message_buffer, 347 int *conf_state, int *qop_state, int toktype) 348 { 349 krb5_data plain; 350 gssint_uint64 seqnum; 351 size_t ec, rrc; 352 int key_usage; 353 unsigned char acceptor_flag; 354 krb5_checksum sum; 355 krb5_error_code err; 356 krb5_boolean valid; 357 krb5_keyblock *key; 358 359 ASSERT(toktype != KG_TOK_SEAL_MSG || ctx->enc != 0); 360 ASSERT(ctx->big_endian == 0); 361 ASSERT(ctx->proto == 1); 362 363 if (qop_state) 364 *qop_state = GSS_C_QOP_DEFAULT; 365 366 acceptor_flag = ctx->initiate ? FLAG_SENDER_IS_ACCEPTOR : 0; 367 key_usage = (toktype == KG_TOK_WRAP_MSG 368 ? (!ctx->initiate 369 ? KG_USAGE_INITIATOR_SEAL 370 : KG_USAGE_ACCEPTOR_SEAL) 371 : (!ctx->initiate 372 ? KG_USAGE_INITIATOR_SIGN 373 : KG_USAGE_ACCEPTOR_SIGN)); 374 375 /* Oops. I wrote this code assuming ptr would be at the start of 376 the token header. */ 377 ptr -= 2; 378 bodysize += 2; 379 380 if (bodysize < 16) { 381 defective: 382 *minor_status = 0; 383 return GSS_S_DEFECTIVE_TOKEN; 384 } 385 if ((ptr[2] & FLAG_SENDER_IS_ACCEPTOR) != acceptor_flag) { 386 *minor_status = (OM_uint32)G_BAD_DIRECTION; 387 return GSS_S_BAD_SIG; 388 } 389 390 /* Two things to note here. 391 392 First, we can't really enforce the use of the acceptor's subkey, 393 if we're the acceptor; the initiator may have sent messages 394 before getting the subkey. We could probably enforce it if 395 we're the initiator. 396 397 Second, if someone tweaks the code to not set the flag telling 398 the krb5 library to generate a new subkey in the AP-REP 399 message, the MIT library may include a subkey anyways -- 400 namely, a copy of the AP-REQ subkey, if it was provided. So 401 the initiator may think we wanted a subkey, and set the flag, 402 even though we weren't trying to set the subkey. The "other" 403 key, the one not ASSERTed by the acceptor, will have the same 404 value in that case, though, so we can just ignore the flag. */ 405 if (ctx->have_acceptor_subkey && (ptr[2] & FLAG_ACCEPTOR_SUBKEY)) { 406 key = ctx->acceptor_subkey; 407 } else { 408 key = ctx->enc; 409 } 410 411 #ifdef _KERNEL 412 context->kef_cipher_mt = get_cipher_mech_type(context, key); 413 context->kef_hash_mt = get_hash_mech_type(context, key); 414 415 if ((err = init_key_kef(context->kef_cipher_mt, key))) { 416 return (GSS_S_FAILURE); 417 } 418 #endif /* _KERNEL */ 419 420 if (toktype == KG_TOK_WRAP_MSG) { 421 if (load_16_be(ptr) != 0x0504) 422 goto defective; 423 if (ptr[3] != 0xff) 424 goto defective; 425 ec = load_16_be(ptr+4); 426 rrc = load_16_be(ptr+6); 427 seqnum = load_64_be(ptr+8); 428 if (!rotate_left(ptr+16, bodysize-16, rrc)) { 429 no_mem: 430 *minor_status = ENOMEM; 431 return GSS_S_FAILURE; 432 } 433 if (ptr[2] & FLAG_WRAP_CONFIDENTIAL) { 434 /* confidentiality */ 435 krb5_enc_data cipher; 436 unsigned char *althdr; 437 size_t plainlen; 438 439 if (conf_state) 440 *conf_state = 1; 441 /* Do we have no decrypt_size function? 442 443 For all current cryptosystems, the ciphertext size will 444 be larger than the plaintext size. */ 445 cipher.enctype = key->enctype; 446 cipher.ciphertext.length = bodysize - 16; 447 cipher.ciphertext.data = (char *)ptr + 16; 448 plain.length = plainlen = bodysize - 16; 449 plain.data = MALLOC(plain.length); 450 if (plain.data == NULL) 451 goto no_mem; 452 err = krb5_c_decrypt(context, key, key_usage, 0, 453 &cipher, &plain); 454 if (err) { 455 goto error; 456 } 457 /* Don't use bodysize here! Use the fact that 458 plain.length has been adjusted to the 459 correct length. */ 460 althdr = (uchar_t *)plain.data + plain.length - 16; 461 if (load_16_be(althdr) != 0x0504 462 || althdr[2] != ptr[2] 463 || althdr[3] != ptr[3] 464 || memcmp(althdr+8, ptr+8, 8)) { 465 FREE(plain.data, plainlen); 466 goto defective; 467 } 468 message_buffer->length = plain.length - ec - 16; 469 message_buffer->value = MALLOC(message_buffer->length); 470 if (message_buffer->value == NULL) { 471 FREE(plain.data, plainlen); 472 goto no_mem; 473 } 474 (void) memcpy(message_buffer->value, plain.data, 475 message_buffer->length); 476 FREE(plain.data, plainlen); 477 } else { 478 /* no confidentiality */ 479 if (conf_state) 480 *conf_state = 0; 481 if (ec + 16 < ec) 482 /* overflow check */ 483 goto defective; 484 if (ec + 16 > bodysize) 485 goto defective; 486 /* We have: header | msg | cksum. 487 We need cksum(msg | header). 488 Rotate the first two. */ 489 store_16_be(0, ptr+4); 490 store_16_be(0, ptr+6); 491 plain.length = bodysize - ec; 492 plain.data = (char *)ptr; 493 if (!rotate_left(ptr, bodysize-ec, 16)) 494 goto no_mem; 495 sum.length = ec; 496 if (sum.length != ctx->cksum_size) { 497 *minor_status = 0; 498 return GSS_S_BAD_SIG; 499 } 500 sum.contents = ptr+bodysize-ec; 501 sum.checksum_type = ctx->cksumtype; 502 err = krb5_c_verify_checksum(context, key, key_usage, 503 &plain, &sum, &valid); 504 if (err) { 505 *minor_status = err; 506 return GSS_S_BAD_SIG; 507 } 508 if (!valid) { 509 *minor_status = 0; 510 return GSS_S_BAD_SIG; 511 } 512 message_buffer->length = plain.length - 16; 513 message_buffer->value = MALLOC(message_buffer->length); 514 if (message_buffer->value == NULL) 515 goto no_mem; 516 (void) memcpy(message_buffer->value, 517 plain.data, message_buffer->length); 518 519 /* 520 * Solaris Kerberos: Restore the original token. 521 * This allows the token to be detected as a duplicate if it 522 * is passed in to gss_unwrap() again. 523 */ 524 if (!rotate_left(ptr, bodysize-ec, bodysize - ec - 16)) 525 goto no_mem; 526 store_16_be(ec, ptr+4); 527 store_16_be(rrc, ptr+6); 528 } 529 err = g_order_check(&ctx->seqstate, seqnum); 530 *minor_status = 0; 531 return err; 532 } else if (toktype == KG_TOK_MIC_MSG) { 533 /* wrap token, no confidentiality */ 534 if (load_16_be(ptr) != 0x0404) 535 goto defective; 536 verify_mic_1: 537 if (ptr[3] != 0xff) 538 goto defective; 539 if (load_32_be(ptr+4) != (ulong_t)0xffffffffU) 540 goto defective; 541 seqnum = load_64_be(ptr+8); 542 plain.length = message_buffer->length + 16; 543 plain.data = MALLOC(plain.length); 544 if (plain.data == NULL) 545 goto no_mem; 546 if (message_buffer->length) 547 (void) memcpy(plain.data, 548 message_buffer->value, message_buffer->length); 549 (void) memcpy(plain.data + message_buffer->length, ptr, 16); 550 sum.length = bodysize - 16; 551 sum.contents = ptr + 16; 552 sum.checksum_type = ctx->cksumtype; 553 err = krb5_c_verify_checksum(context, key, key_usage, 554 &plain, &sum, &valid); 555 if (err) { 556 error: 557 FREE(plain.data, plain.length); 558 *minor_status = err; 559 return GSS_S_BAD_SIG; /* XXX */ 560 } 561 FREE(plain.data, plain.length); 562 if (!valid) { 563 *minor_status = 0; 564 return GSS_S_BAD_SIG; 565 } 566 err = g_order_check(&ctx->seqstate, seqnum); 567 *minor_status = 0; 568 return err; 569 } else if (toktype == KG_TOK_DEL_CTX) { 570 if (load_16_be(ptr) != 0x0405) 571 goto defective; 572 message_buffer = (gss_buffer_t)&empty_message; 573 goto verify_mic_1; 574 } else { 575 goto defective; 576 } 577 } 578