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