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 "krb5/gsskrb5_locl.h" 34 35RCSID("$Id: inquire_sec_context_by_oid.c 19031 2006-11-13 18:02:57Z lha $"); 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("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("%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("have no subkey of type %d", keytype); 149 ret = EINVAL; 150 goto out; 151 } 152 153 ret = krb5_store_keyblock(sp, *key); 154 krb5_free_keyblock (context, key); 155 if (ret) 156 goto out; 157 158 ret = krb5_storage_to_data(sp, &data); 159 if (ret) 160 goto out; 161 162 { 163 gss_buffer_desc value; 164 165 value.length = data.length; 166 value.value = data.data; 167 168 maj_stat = gss_add_buffer_set_member(minor_status, 169 &value, 170 data_set); 171 } 172 173out: 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_authz_data 185 (OM_uint32 *minor_status, 186 const gsskrb5_ctx context_handle, 187 krb5_context context, 188 unsigned ad_type, 189 gss_buffer_set_t *data_set) 190{ 191 krb5_data data; 192 gss_buffer_desc ad_data; 193 OM_uint32 ret; 194 195 *minor_status = 0; 196 *data_set = GSS_C_NO_BUFFER_SET; 197 198 HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex); 199 if (context_handle->ticket == NULL) { 200 HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); 201 *minor_status = EINVAL; 202 _gsskrb5_set_status("No ticket to obtain authz data from"); 203 return GSS_S_NO_CONTEXT; 204 } 205 206 ret = krb5_ticket_get_authorization_data_type(context, 207 context_handle->ticket, 208 ad_type, 209 &data); 210 HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); 211 if (ret) { 212 *minor_status = ret; 213 return GSS_S_FAILURE; 214 } 215 216 ad_data.value = data.data; 217 ad_data.length = data.length; 218 219 ret = gss_add_buffer_set_member(minor_status, 220 &ad_data, 221 data_set); 222 223 krb5_data_free(&data); 224 225 return ret; 226} 227 228static OM_uint32 inquire_sec_context_has_updated_spnego 229 (OM_uint32 *minor_status, 230 const gsskrb5_ctx context_handle, 231 gss_buffer_set_t *data_set) 232{ 233 int is_updated = 0; 234 235 *minor_status = 0; 236 *data_set = GSS_C_NO_BUFFER_SET; 237 238 /* 239 * For Windows SPNEGO implementations, both the initiator and the 240 * acceptor are assumed to have been updated if a "newer" [CLAR] or 241 * different enctype is negotiated for use by the Kerberos GSS-API 242 * mechanism. 243 */ 244 HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex); 245 _gsskrb5i_is_cfx(context_handle, &is_updated); 246 if (is_updated == 0) { 247 krb5_keyblock *acceptor_subkey; 248 249 if (context_handle->more_flags & LOCAL) 250 acceptor_subkey = context_handle->auth_context->remote_subkey; 251 else 252 acceptor_subkey = context_handle->auth_context->local_subkey; 253 254 if (acceptor_subkey != NULL) 255 is_updated = (acceptor_subkey->keytype != 256 context_handle->auth_context->keyblock->keytype); 257 } 258 HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); 259 260 return is_updated ? GSS_S_COMPLETE : GSS_S_FAILURE; 261} 262 263/* 264 * 265 */ 266 267static OM_uint32 268export_lucid_sec_context_v1(OM_uint32 *minor_status, 269 gsskrb5_ctx context_handle, 270 krb5_context context, 271 gss_buffer_set_t *data_set) 272{ 273 krb5_storage *sp = NULL; 274 OM_uint32 major_status = GSS_S_COMPLETE; 275 krb5_error_code ret; 276 krb5_keyblock *key = NULL; 277 int32_t number; 278 int is_cfx; 279 krb5_data data; 280 281 *minor_status = 0; 282 283 HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex); 284 285 _gsskrb5i_is_cfx(context_handle, &is_cfx); 286 287 sp = krb5_storage_emem(); 288 if (sp == NULL) { 289 _gsskrb5_clear_status(); 290 ret = ENOMEM; 291 goto out; 292 } 293 294 ret = krb5_store_int32(sp, 1); 295 if (ret) goto out; 296 ret = krb5_store_int32(sp, (context_handle->more_flags & LOCAL) ? 1 : 0); 297 if (ret) goto out; 298 ret = krb5_store_int32(sp, context_handle->lifetime); 299 if (ret) goto out; 300 krb5_auth_con_getlocalseqnumber (context, 301 context_handle->auth_context, 302 &number); 303 ret = krb5_store_uint32(sp, (uint32_t)0); /* store top half as zero */ 304 ret = krb5_store_uint32(sp, (uint32_t)number); 305 krb5_auth_getremoteseqnumber (context, 306 context_handle->auth_context, 307 &number); 308 ret = krb5_store_uint32(sp, (uint32_t)0); /* store top half as zero */ 309 ret = krb5_store_uint32(sp, (uint32_t)number); 310 ret = krb5_store_int32(sp, (is_cfx) ? 1 : 0); 311 if (ret) goto out; 312 313 ret = _gsskrb5i_get_token_key(context_handle, context, &key); 314 if (ret) goto out; 315 316 if (is_cfx == 0) { 317 int sign_alg, seal_alg; 318 319 switch (key->keytype) { 320 case ETYPE_DES_CBC_CRC: 321 case ETYPE_DES_CBC_MD4: 322 case ETYPE_DES_CBC_MD5: 323 sign_alg = 0; 324 seal_alg = 0; 325 break; 326 case ETYPE_DES3_CBC_MD5: 327 case ETYPE_DES3_CBC_SHA1: 328 sign_alg = 4; 329 seal_alg = 2; 330 break; 331 case ETYPE_ARCFOUR_HMAC_MD5: 332 case ETYPE_ARCFOUR_HMAC_MD5_56: 333 sign_alg = 17; 334 seal_alg = 16; 335 break; 336 default: 337 sign_alg = -1; 338 seal_alg = -1; 339 break; 340 } 341 ret = krb5_store_int32(sp, sign_alg); 342 if (ret) goto out; 343 ret = krb5_store_int32(sp, seal_alg); 344 if (ret) goto out; 345 /* ctx_key */ 346 ret = krb5_store_keyblock(sp, *key); 347 if (ret) goto out; 348 } else { 349 int subkey_p = (context_handle->more_flags & ACCEPTOR_SUBKEY) ? 1 : 0; 350 351 /* have_acceptor_subkey */ 352 ret = krb5_store_int32(sp, subkey_p); 353 if (ret) goto out; 354 /* ctx_key */ 355 ret = krb5_store_keyblock(sp, *key); 356 if (ret) goto out; 357 /* acceptor_subkey */ 358 if (subkey_p) { 359 ret = krb5_store_keyblock(sp, *key); 360 if (ret) goto out; 361 } 362 } 363 ret = krb5_storage_to_data(sp, &data); 364 if (ret) goto out; 365 366 { 367 gss_buffer_desc ad_data; 368 369 ad_data.value = data.data; 370 ad_data.length = data.length; 371 372 ret = gss_add_buffer_set_member(minor_status, &ad_data, data_set); 373 krb5_data_free(&data); 374 if (ret) 375 goto out; 376 } 377 378out: 379 if (key) 380 krb5_free_keyblock (context, key); 381 if (sp) 382 krb5_storage_free(sp); 383 if (ret) { 384 *minor_status = ret; 385 major_status = GSS_S_FAILURE; 386 } 387 HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); 388 return major_status; 389} 390 391static OM_uint32 392get_authtime(OM_uint32 *minor_status, 393 gsskrb5_ctx ctx, 394 gss_buffer_set_t *data_set) 395 396{ 397 gss_buffer_desc value; 398 unsigned char buf[4]; 399 OM_uint32 authtime; 400 401 HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); 402 if (ctx->ticket == NULL) { 403 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 404 _gsskrb5_set_status("No ticket to obtain auth time from"); 405 *minor_status = EINVAL; 406 return GSS_S_FAILURE; 407 } 408 409 authtime = ctx->ticket->ticket.authtime; 410 411 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 412 413 _gsskrb5_encode_om_uint32(authtime, buf); 414 value.length = sizeof(buf); 415 value.value = buf; 416 417 return gss_add_buffer_set_member(minor_status, 418 &value, 419 data_set); 420} 421 422 423static OM_uint32 424get_service_keyblock 425 (OM_uint32 *minor_status, 426 gsskrb5_ctx ctx, 427 gss_buffer_set_t *data_set) 428{ 429 krb5_storage *sp = NULL; 430 krb5_data data; 431 OM_uint32 maj_stat = GSS_S_COMPLETE; 432 krb5_error_code ret = EINVAL; 433 434 sp = krb5_storage_emem(); 435 if (sp == NULL) { 436 _gsskrb5_clear_status(); 437 *minor_status = ENOMEM; 438 return GSS_S_FAILURE; 439 } 440 441 HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); 442 if (ctx->service_keyblock == NULL) { 443 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 444 _gsskrb5_set_status("No service keyblock on gssapi context"); 445 *minor_status = EINVAL; 446 return GSS_S_FAILURE; 447 } 448 449 krb5_data_zero(&data); 450 451 ret = krb5_store_keyblock(sp, *ctx->service_keyblock); 452 453 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 454 455 if (ret) 456 goto out; 457 458 ret = krb5_storage_to_data(sp, &data); 459 if (ret) 460 goto out; 461 462 { 463 gss_buffer_desc value; 464 465 value.length = data.length; 466 value.value = data.data; 467 468 maj_stat = gss_add_buffer_set_member(minor_status, 469 &value, 470 data_set); 471 } 472 473out: 474 krb5_data_free(&data); 475 if (sp) 476 krb5_storage_free(sp); 477 if (ret) { 478 *minor_status = ret; 479 maj_stat = GSS_S_FAILURE; 480 } 481 return maj_stat; 482} 483/* 484 * 485 */ 486 487OM_uint32 _gsskrb5_inquire_sec_context_by_oid 488 (OM_uint32 *minor_status, 489 const gss_ctx_id_t context_handle, 490 const gss_OID desired_object, 491 gss_buffer_set_t *data_set) 492{ 493 krb5_context context; 494 const gsskrb5_ctx ctx = (const gsskrb5_ctx) context_handle; 495 unsigned suffix; 496 497 if (ctx == NULL) { 498 *minor_status = EINVAL; 499 return GSS_S_NO_CONTEXT; 500 } 501 502 GSSAPI_KRB5_INIT (&context); 503 504 if (gss_oid_equal(desired_object, GSS_KRB5_GET_TKT_FLAGS_X)) { 505 return inquire_sec_context_tkt_flags(minor_status, 506 ctx, 507 data_set); 508 } else if (gss_oid_equal(desired_object, GSS_C_PEER_HAS_UPDATED_SPNEGO)) { 509 return inquire_sec_context_has_updated_spnego(minor_status, 510 ctx, 511 data_set); 512 } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_SUBKEY_X)) { 513 return inquire_sec_context_get_subkey(minor_status, 514 ctx, 515 context, 516 TOKEN_KEY, 517 data_set); 518 } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_INITIATOR_SUBKEY_X)) { 519 return inquire_sec_context_get_subkey(minor_status, 520 ctx, 521 context, 522 INITIATOR_KEY, 523 data_set); 524 } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_ACCEPTOR_SUBKEY_X)) { 525 return inquire_sec_context_get_subkey(minor_status, 526 ctx, 527 context, 528 ACCEPTOR_KEY, 529 data_set); 530 } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_AUTHTIME_X)) { 531 return get_authtime(minor_status, ctx, data_set); 532 } else if (oid_prefix_equal(desired_object, 533 GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X, 534 &suffix)) { 535 return inquire_sec_context_authz_data(minor_status, 536 ctx, 537 context, 538 suffix, 539 data_set); 540 } else if (oid_prefix_equal(desired_object, 541 GSS_KRB5_EXPORT_LUCID_CONTEXT_X, 542 &suffix)) { 543 if (suffix == 1) 544 return export_lucid_sec_context_v1(minor_status, 545 ctx, 546 context, 547 data_set); 548 *minor_status = 0; 549 return GSS_S_FAILURE; 550 } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_SERVICE_KEYBLOCK_X)) { 551 return get_service_keyblock(minor_status, ctx, data_set); 552 } else { 553 *minor_status = 0; 554 return GSS_S_FAILURE; 555 } 556} 557 558