1/* 2 * Copyright (c) 2004, PADL Software Pty Ltd. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * 3. Neither the name of PADL Software nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33#include "gsskrb5_locl.h" 34 35static int 36oid_prefix_equal(gss_OID oid_enc, gss_OID prefix_enc, unsigned *suffix) 37{ 38 int ret; 39 heim_oid oid; 40 heim_oid prefix; 41 42 *suffix = 0; 43 44 ret = der_get_oid(oid_enc->elements, oid_enc->length, 45 &oid, NULL); 46 if (ret) { 47 return 0; 48 } 49 50 ret = der_get_oid(prefix_enc->elements, prefix_enc->length, 51 &prefix, NULL); 52 if (ret) { 53 der_free_oid(&oid); 54 return 0; 55 } 56 57 ret = 0; 58 59 if (oid.length - 1 == prefix.length) { 60 *suffix = oid.components[oid.length - 1]; 61 oid.length--; 62 ret = (der_heim_oid_cmp(&oid, &prefix) == 0); 63 oid.length++; 64 } 65 66 der_free_oid(&oid); 67 der_free_oid(&prefix); 68 69 return ret; 70} 71 72static OM_uint32 inquire_sec_context_tkt_flags 73 (OM_uint32 *minor_status, 74 const gsskrb5_ctx context_handle, 75 gss_buffer_set_t *data_set) 76{ 77 OM_uint32 tkt_flags; 78 unsigned char buf[4]; 79 gss_buffer_desc value; 80 81 HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex); 82 83 if (context_handle->ticket == NULL) { 84 HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); 85 _gsskrb5_set_status(EINVAL, "No ticket from which to obtain flags"); 86 *minor_status = EINVAL; 87 return GSS_S_BAD_MECH; 88 } 89 90 tkt_flags = TicketFlags2int(context_handle->ticket->ticket.flags); 91 HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); 92 93 _gsskrb5_encode_om_uint32(tkt_flags, buf); 94 value.length = sizeof(buf); 95 value.value = buf; 96 97 return gss_add_buffer_set_member(minor_status, 98 &value, 99 data_set); 100} 101 102enum keytype { ACCEPTOR_KEY, INITIATOR_KEY, TOKEN_KEY }; 103 104static OM_uint32 inquire_sec_context_get_subkey 105 (OM_uint32 *minor_status, 106 const gsskrb5_ctx context_handle, 107 krb5_context context, 108 enum keytype keytype, 109 gss_buffer_set_t *data_set) 110{ 111 krb5_keyblock *key = NULL; 112 krb5_storage *sp = NULL; 113 krb5_data data; 114 OM_uint32 maj_stat = GSS_S_COMPLETE; 115 krb5_error_code ret; 116 117 krb5_data_zero(&data); 118 119 sp = krb5_storage_emem(); 120 if (sp == NULL) { 121 _gsskrb5_clear_status(); 122 ret = ENOMEM; 123 goto out; 124 } 125 126 HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex); 127 switch(keytype) { 128 case ACCEPTOR_KEY: 129 ret = _gsskrb5i_get_acceptor_subkey(context_handle, context, &key); 130 break; 131 case INITIATOR_KEY: 132 ret = _gsskrb5i_get_initiator_subkey(context_handle, context, &key); 133 break; 134 case TOKEN_KEY: 135 ret = _gsskrb5i_get_token_key(context_handle, context, &key); 136 break; 137 default: 138 _gsskrb5_set_status(EINVAL, "%d is not a valid subkey type", keytype); 139 ret = EINVAL; 140 break; 141 } 142 HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); 143 if (ret) 144 goto out; 145 if (key == NULL) { 146 _gsskrb5_set_status(EINVAL, "have no subkey of type %d", keytype); 147 ret = EINVAL; 148 goto out; 149 } 150 151 ret = krb5_store_keyblock(sp, *key); 152 krb5_free_keyblock (context, key); 153 if (ret) 154 goto out; 155 156 ret = krb5_storage_to_data(sp, &data); 157 if (ret) 158 goto out; 159 160 { 161 gss_buffer_desc value; 162 163 value.length = data.length; 164 value.value = data.data; 165 166 maj_stat = gss_add_buffer_set_member(minor_status, 167 &value, 168 data_set); 169 } 170 171out: 172 krb5_data_free(&data); 173 if (sp) 174 krb5_storage_free(sp); 175 if (ret) { 176 *minor_status = ret; 177 maj_stat = GSS_S_FAILURE; 178 } 179 return maj_stat; 180} 181 182static OM_uint32 inquire_sec_context_get_sspi_session_key 183 (OM_uint32 *minor_status, 184 const gsskrb5_ctx context_handle, 185 krb5_context context, 186 gss_buffer_set_t *data_set) 187{ 188 krb5_keyblock *key; 189 OM_uint32 maj_stat = GSS_S_COMPLETE; 190 krb5_error_code ret; 191 gss_buffer_desc value; 192 193 HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex); 194 ret = _gsskrb5i_get_token_key(context_handle, context, &key); 195 HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); 196 197 if (ret) 198 goto out; 199 if (key == NULL) { 200 ret = EINVAL; 201 goto out; 202 } 203 204 value.length = key->keyvalue.length; 205 value.value = key->keyvalue.data; 206 207 maj_stat = gss_add_buffer_set_member(minor_status, 208 &value, 209 data_set); 210 krb5_free_keyblock(context, key); 211 212 /* MIT also returns the enctype encoded as an OID in data_set[1] */ 213 214out: 215 if (ret) { 216 *minor_status = ret; 217 maj_stat = GSS_S_FAILURE; 218 } 219 return maj_stat; 220} 221 222static OM_uint32 inquire_sec_context_authz_data 223 (OM_uint32 *minor_status, 224 const gsskrb5_ctx context_handle, 225 krb5_context context, 226 unsigned ad_type, 227 gss_buffer_set_t *data_set) 228{ 229 krb5_data data; 230 gss_buffer_desc ad_data; 231 OM_uint32 ret; 232 233 *minor_status = 0; 234 *data_set = GSS_C_NO_BUFFER_SET; 235 236 HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex); 237 if (context_handle->ticket == NULL) { 238 HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); 239 *minor_status = EINVAL; 240 _gsskrb5_set_status(EINVAL, "No ticket to obtain authz data from"); 241 return GSS_S_NO_CONTEXT; 242 } 243 244 ret = krb5_ticket_get_authorization_data_type(context, 245 context_handle->ticket, 246 ad_type, 247 &data); 248 HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); 249 if (ret) { 250 *minor_status = ret; 251 return GSS_S_FAILURE; 252 } 253 254 ad_data.value = data.data; 255 ad_data.length = data.length; 256 257 ret = gss_add_buffer_set_member(minor_status, 258 &ad_data, 259 data_set); 260 261 krb5_data_free(&data); 262 263 return ret; 264} 265 266static OM_uint32 inquire_sec_context_has_updated_spnego 267 (OM_uint32 *minor_status, 268 const gsskrb5_ctx context_handle, 269 gss_buffer_set_t *data_set) 270{ 271 int is_updated = 0; 272 273 *minor_status = 0; 274 *data_set = GSS_C_NO_BUFFER_SET; 275 276 /* 277 * For Windows SPNEGO implementations, both the initiator and the 278 * acceptor are assumed to have been updated if a "newer" [CLAR] or 279 * different enctype is negotiated for use by the Kerberos GSS-API 280 * mechanism. 281 */ 282 HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex); 283 is_updated = (context_handle->more_flags & IS_CFX); 284 if (is_updated == 0) { 285 krb5_keyblock *acceptor_subkey; 286 287 if (context_handle->more_flags & LOCAL) 288 acceptor_subkey = context_handle->auth_context->remote_subkey; 289 else 290 acceptor_subkey = context_handle->auth_context->local_subkey; 291 292 if (acceptor_subkey != NULL) 293 is_updated = (acceptor_subkey->keytype != 294 context_handle->auth_context->keyblock->keytype); 295 } 296 HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); 297 298 return is_updated ? GSS_S_COMPLETE : GSS_S_FAILURE; 299} 300 301/* 302 * 303 */ 304 305static OM_uint32 306export_lucid_sec_context_v1(OM_uint32 *minor_status, 307 gsskrb5_ctx context_handle, 308 krb5_context context, 309 gss_buffer_set_t *data_set) 310{ 311 krb5_storage *sp = NULL; 312 OM_uint32 major_status = GSS_S_COMPLETE; 313 krb5_error_code ret; 314 krb5_keyblock *key = NULL; 315 int32_t number; 316 int is_cfx; 317 krb5_data data; 318 319 *minor_status = 0; 320 321 HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex); 322 323 is_cfx = (context_handle->more_flags & IS_CFX); 324 325 sp = krb5_storage_emem(); 326 if (sp == NULL) { 327 _gsskrb5_clear_status(); 328 ret = ENOMEM; 329 goto out; 330 } 331 332 ret = krb5_store_int32(sp, 1); 333 if (ret) goto out; 334 ret = krb5_store_int32(sp, (context_handle->more_flags & LOCAL) ? 1 : 0); 335 if (ret) goto out; 336 ret = krb5_store_int32(sp, context_handle->lifetime); 337 if (ret) goto out; 338 krb5_auth_con_getlocalseqnumber (context, 339 context_handle->auth_context, 340 &number); 341 ret = krb5_store_uint32(sp, (uint32_t)0); /* store top half as zero */ 342 if (ret) goto out; 343 ret = krb5_store_uint32(sp, (uint32_t)number); 344 if (ret) goto out; 345 krb5_auth_con_getremoteseqnumber (context, 346 context_handle->auth_context, 347 &number); 348 ret = krb5_store_uint32(sp, (uint32_t)0); /* store top half as zero */ 349 if (ret) goto out; 350 ret = krb5_store_uint32(sp, (uint32_t)number); 351 if (ret) goto out; 352 ret = krb5_store_int32(sp, (is_cfx) ? 1 : 0); 353 if (ret) goto out; 354 355 ret = _gsskrb5i_get_token_key(context_handle, context, &key); 356 if (ret) goto out; 357 358 if (is_cfx == 0) { 359 int sign_alg, seal_alg; 360 361 switch (key->keytype) { 362 case ETYPE_DES_CBC_CRC: 363 case ETYPE_DES_CBC_MD4: 364 case ETYPE_DES_CBC_MD5: 365 sign_alg = 0; 366 seal_alg = 0; 367 break; 368 case ETYPE_DES3_CBC_MD5: 369 case ETYPE_DES3_CBC_SHA1: 370 sign_alg = 4; 371 seal_alg = 2; 372 break; 373 case ETYPE_ARCFOUR_HMAC_MD5: 374 case ETYPE_ARCFOUR_HMAC_MD5_56: 375 sign_alg = 17; 376 seal_alg = 16; 377 break; 378 default: 379 sign_alg = -1; 380 seal_alg = -1; 381 break; 382 } 383 ret = krb5_store_int32(sp, sign_alg); 384 if (ret) goto out; 385 ret = krb5_store_int32(sp, seal_alg); 386 if (ret) goto out; 387 /* ctx_key */ 388 ret = krb5_store_keyblock(sp, *key); 389 if (ret) goto out; 390 } else { 391 int subkey_p = (context_handle->more_flags & ACCEPTOR_SUBKEY) ? 1 : 0; 392 393 /* have_acceptor_subkey */ 394 ret = krb5_store_int32(sp, subkey_p); 395 if (ret) goto out; 396 /* ctx_key */ 397 ret = krb5_store_keyblock(sp, *key); 398 if (ret) goto out; 399 /* acceptor_subkey */ 400 if (subkey_p) { 401 ret = krb5_store_keyblock(sp, *key); 402 if (ret) goto out; 403 } 404 } 405 ret = krb5_storage_to_data(sp, &data); 406 if (ret) goto out; 407 408 { 409 gss_buffer_desc ad_data; 410 411 ad_data.value = data.data; 412 ad_data.length = data.length; 413 414 ret = gss_add_buffer_set_member(minor_status, &ad_data, data_set); 415 krb5_data_free(&data); 416 if (ret) 417 goto out; 418 } 419 420out: 421 if (key) 422 krb5_free_keyblock (context, key); 423 if (sp) 424 krb5_storage_free(sp); 425 if (ret) { 426 *minor_status = ret; 427 major_status = GSS_S_FAILURE; 428 } 429 HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); 430 return major_status; 431} 432 433static OM_uint32 434get_authtime(OM_uint32 *minor_status, 435 gsskrb5_ctx ctx, 436 gss_buffer_set_t *data_set) 437 438{ 439 gss_buffer_desc value; 440 unsigned char buf[4]; 441 OM_uint32 authtime; 442 443 HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); 444 if (ctx->ticket == NULL) { 445 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 446 _gsskrb5_set_status(EINVAL, "No ticket to obtain auth time from"); 447 *minor_status = EINVAL; 448 return GSS_S_FAILURE; 449 } 450 451 authtime = ctx->ticket->ticket.authtime; 452 453 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 454 455 _gsskrb5_encode_om_uint32(authtime, buf); 456 value.length = sizeof(buf); 457 value.value = buf; 458 459 return gss_add_buffer_set_member(minor_status, 460 &value, 461 data_set); 462} 463 464 465static OM_uint32 466get_service_keyblock 467 (OM_uint32 *minor_status, 468 gsskrb5_ctx ctx, 469 gss_buffer_set_t *data_set) 470{ 471 krb5_storage *sp = NULL; 472 krb5_data data; 473 OM_uint32 maj_stat = GSS_S_COMPLETE; 474 krb5_error_code ret = EINVAL; 475 476 sp = krb5_storage_emem(); 477 if (sp == NULL) { 478 _gsskrb5_clear_status(); 479 *minor_status = ENOMEM; 480 return GSS_S_FAILURE; 481 } 482 483 HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); 484 if (ctx->service_keyblock == NULL) { 485 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 486 krb5_storage_free(sp); 487 _gsskrb5_set_status(EINVAL, "No service keyblock on gssapi context"); 488 *minor_status = EINVAL; 489 return GSS_S_FAILURE; 490 } 491 492 krb5_data_zero(&data); 493 494 ret = krb5_store_keyblock(sp, *ctx->service_keyblock); 495 496 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 497 498 if (ret) 499 goto out; 500 501 ret = krb5_storage_to_data(sp, &data); 502 if (ret) 503 goto out; 504 505 { 506 gss_buffer_desc value; 507 508 value.length = data.length; 509 value.value = data.data; 510 511 maj_stat = gss_add_buffer_set_member(minor_status, 512 &value, 513 data_set); 514 } 515 516out: 517 krb5_data_free(&data); 518 if (sp) 519 krb5_storage_free(sp); 520 if (ret) { 521 *minor_status = ret; 522 maj_stat = GSS_S_FAILURE; 523 } 524 return maj_stat; 525} 526/* 527 * 528 */ 529 530OM_uint32 GSSAPI_CALLCONV _gsskrb5_inquire_sec_context_by_oid 531 (OM_uint32 *minor_status, 532 const gss_ctx_id_t context_handle, 533 const gss_OID desired_object, 534 gss_buffer_set_t *data_set) 535{ 536 krb5_context context; 537 const gsskrb5_ctx ctx = (const gsskrb5_ctx) context_handle; 538 unsigned suffix; 539 540 if (ctx == NULL) { 541 *minor_status = EINVAL; 542 return GSS_S_NO_CONTEXT; 543 } 544 545 GSSAPI_KRB5_INIT (&context); 546 547 if (gss_oid_equal(desired_object, GSS_KRB5_GET_TKT_FLAGS_X)) { 548 return inquire_sec_context_tkt_flags(minor_status, 549 ctx, 550 data_set); 551 } else if (gss_oid_equal(desired_object, GSS_C_PEER_HAS_UPDATED_SPNEGO)) { 552 return inquire_sec_context_has_updated_spnego(minor_status, 553 ctx, 554 data_set); 555 } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_SUBKEY_X)) { 556 return inquire_sec_context_get_subkey(minor_status, 557 ctx, 558 context, 559 TOKEN_KEY, 560 data_set); 561 } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_INITIATOR_SUBKEY_X)) { 562 return inquire_sec_context_get_subkey(minor_status, 563 ctx, 564 context, 565 INITIATOR_KEY, 566 data_set); 567 } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_ACCEPTOR_SUBKEY_X)) { 568 return inquire_sec_context_get_subkey(minor_status, 569 ctx, 570 context, 571 ACCEPTOR_KEY, 572 data_set); 573 } else if (gss_oid_equal(desired_object, GSS_C_INQ_SSPI_SESSION_KEY)) { 574 return inquire_sec_context_get_sspi_session_key(minor_status, 575 ctx, 576 context, 577 data_set); 578 } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_AUTHTIME_X)) { 579 return get_authtime(minor_status, ctx, data_set); 580 } else if (oid_prefix_equal(desired_object, 581 GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X, 582 &suffix)) { 583 return inquire_sec_context_authz_data(minor_status, 584 ctx, 585 context, 586 suffix, 587 data_set); 588 } else if (oid_prefix_equal(desired_object, 589 GSS_KRB5_EXPORT_LUCID_CONTEXT_X, 590 &suffix)) { 591 if (suffix == 1) 592 return export_lucid_sec_context_v1(minor_status, 593 ctx, 594 context, 595 data_set); 596 *minor_status = 0; 597 return GSS_S_FAILURE; 598 } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_SERVICE_KEYBLOCK_X)) { 599 return get_service_keyblock(minor_status, ctx, data_set); 600 } else { 601 *minor_status = 0; 602 return GSS_S_FAILURE; 603 } 604} 605 606