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