1/* $NetBSD: inquire_sec_context_by_oid.c,v 1.1.1.1 2011/04/13 18:14:45 elric 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 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(EINVAL, "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 is_updated = (context_handle->more_flags & IS_CFX); 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 is_cfx = (context_handle->more_flags & 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 if (ret) goto out; 305 ret = krb5_store_uint32(sp, (uint32_t)number); 306 if (ret) goto out; 307 krb5_auth_con_getremoteseqnumber (context, 308 context_handle->auth_context, 309 &number); 310 ret = krb5_store_uint32(sp, (uint32_t)0); /* store top half as zero */ 311 if (ret) goto out; 312 ret = krb5_store_uint32(sp, (uint32_t)number); 313 if (ret) goto out; 314 ret = krb5_store_int32(sp, (is_cfx) ? 1 : 0); 315 if (ret) goto out; 316 317 ret = _gsskrb5i_get_token_key(context_handle, context, &key); 318 if (ret) goto out; 319 320 if (is_cfx == 0) { 321 int sign_alg, seal_alg; 322 323 switch (key->keytype) { 324 case ETYPE_DES_CBC_CRC: 325 case ETYPE_DES_CBC_MD4: 326 case ETYPE_DES_CBC_MD5: 327 sign_alg = 0; 328 seal_alg = 0; 329 break; 330 case ETYPE_DES3_CBC_MD5: 331 case ETYPE_DES3_CBC_SHA1: 332 sign_alg = 4; 333 seal_alg = 2; 334 break; 335 case ETYPE_ARCFOUR_HMAC_MD5: 336 case ETYPE_ARCFOUR_HMAC_MD5_56: 337 sign_alg = 17; 338 seal_alg = 16; 339 break; 340 default: 341 sign_alg = -1; 342 seal_alg = -1; 343 break; 344 } 345 ret = krb5_store_int32(sp, sign_alg); 346 if (ret) goto out; 347 ret = krb5_store_int32(sp, seal_alg); 348 if (ret) goto out; 349 /* ctx_key */ 350 ret = krb5_store_keyblock(sp, *key); 351 if (ret) goto out; 352 } else { 353 int subkey_p = (context_handle->more_flags & ACCEPTOR_SUBKEY) ? 1 : 0; 354 355 /* have_acceptor_subkey */ 356 ret = krb5_store_int32(sp, subkey_p); 357 if (ret) goto out; 358 /* ctx_key */ 359 ret = krb5_store_keyblock(sp, *key); 360 if (ret) goto out; 361 /* acceptor_subkey */ 362 if (subkey_p) { 363 ret = krb5_store_keyblock(sp, *key); 364 if (ret) goto out; 365 } 366 } 367 ret = krb5_storage_to_data(sp, &data); 368 if (ret) goto out; 369 370 { 371 gss_buffer_desc ad_data; 372 373 ad_data.value = data.data; 374 ad_data.length = data.length; 375 376 ret = gss_add_buffer_set_member(minor_status, &ad_data, data_set); 377 krb5_data_free(&data); 378 if (ret) 379 goto out; 380 } 381 382out: 383 if (key) 384 krb5_free_keyblock (context, key); 385 if (sp) 386 krb5_storage_free(sp); 387 if (ret) { 388 *minor_status = ret; 389 major_status = GSS_S_FAILURE; 390 } 391 HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); 392 return major_status; 393} 394 395static OM_uint32 396get_authtime(OM_uint32 *minor_status, 397 gsskrb5_ctx ctx, 398 gss_buffer_set_t *data_set) 399 400{ 401 gss_buffer_desc value; 402 unsigned char buf[4]; 403 OM_uint32 authtime; 404 405 HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); 406 if (ctx->ticket == NULL) { 407 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 408 _gsskrb5_set_status(EINVAL, "No ticket to obtain auth time from"); 409 *minor_status = EINVAL; 410 return GSS_S_FAILURE; 411 } 412 413 authtime = ctx->ticket->ticket.authtime; 414 415 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 416 417 _gsskrb5_encode_om_uint32(authtime, buf); 418 value.length = sizeof(buf); 419 value.value = buf; 420 421 return gss_add_buffer_set_member(minor_status, 422 &value, 423 data_set); 424} 425 426 427static OM_uint32 428get_service_keyblock 429 (OM_uint32 *minor_status, 430 gsskrb5_ctx ctx, 431 gss_buffer_set_t *data_set) 432{ 433 krb5_storage *sp = NULL; 434 krb5_data data; 435 OM_uint32 maj_stat = GSS_S_COMPLETE; 436 krb5_error_code ret = EINVAL; 437 438 sp = krb5_storage_emem(); 439 if (sp == NULL) { 440 _gsskrb5_clear_status(); 441 *minor_status = ENOMEM; 442 return GSS_S_FAILURE; 443 } 444 445 HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); 446 if (ctx->service_keyblock == NULL) { 447 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 448 krb5_storage_free(sp); 449 _gsskrb5_set_status(EINVAL, "No service keyblock on gssapi context"); 450 *minor_status = EINVAL; 451 return GSS_S_FAILURE; 452 } 453 454 krb5_data_zero(&data); 455 456 ret = krb5_store_keyblock(sp, *ctx->service_keyblock); 457 458 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 459 460 if (ret) 461 goto out; 462 463 ret = krb5_storage_to_data(sp, &data); 464 if (ret) 465 goto out; 466 467 { 468 gss_buffer_desc value; 469 470 value.length = data.length; 471 value.value = data.data; 472 473 maj_stat = gss_add_buffer_set_member(minor_status, 474 &value, 475 data_set); 476 } 477 478out: 479 krb5_data_free(&data); 480 if (sp) 481 krb5_storage_free(sp); 482 if (ret) { 483 *minor_status = ret; 484 maj_stat = GSS_S_FAILURE; 485 } 486 return maj_stat; 487} 488/* 489 * 490 */ 491 492OM_uint32 GSSAPI_CALLCONV _gsskrb5_inquire_sec_context_by_oid 493 (OM_uint32 *minor_status, 494 const gss_ctx_id_t context_handle, 495 const gss_OID desired_object, 496 gss_buffer_set_t *data_set) 497{ 498 krb5_context context; 499 const gsskrb5_ctx ctx = (const gsskrb5_ctx) context_handle; 500 unsigned suffix; 501 502 if (ctx == NULL) { 503 *minor_status = EINVAL; 504 return GSS_S_NO_CONTEXT; 505 } 506 507 GSSAPI_KRB5_INIT (&context); 508 509 if (gss_oid_equal(desired_object, GSS_KRB5_GET_TKT_FLAGS_X)) { 510 return inquire_sec_context_tkt_flags(minor_status, 511 ctx, 512 data_set); 513 } else if (gss_oid_equal(desired_object, GSS_C_PEER_HAS_UPDATED_SPNEGO)) { 514 return inquire_sec_context_has_updated_spnego(minor_status, 515 ctx, 516 data_set); 517 } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_SUBKEY_X)) { 518 return inquire_sec_context_get_subkey(minor_status, 519 ctx, 520 context, 521 TOKEN_KEY, 522 data_set); 523 } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_INITIATOR_SUBKEY_X)) { 524 return inquire_sec_context_get_subkey(minor_status, 525 ctx, 526 context, 527 INITIATOR_KEY, 528 data_set); 529 } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_ACCEPTOR_SUBKEY_X)) { 530 return inquire_sec_context_get_subkey(minor_status, 531 ctx, 532 context, 533 ACCEPTOR_KEY, 534 data_set); 535 } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_AUTHTIME_X)) { 536 return get_authtime(minor_status, ctx, data_set); 537 } else if (oid_prefix_equal(desired_object, 538 GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X, 539 &suffix)) { 540 return inquire_sec_context_authz_data(minor_status, 541 ctx, 542 context, 543 suffix, 544 data_set); 545 } else if (oid_prefix_equal(desired_object, 546 GSS_KRB5_EXPORT_LUCID_CONTEXT_X, 547 &suffix)) { 548 if (suffix == 1) 549 return export_lucid_sec_context_v1(minor_status, 550 ctx, 551 context, 552 data_set); 553 *minor_status = 0; 554 return GSS_S_FAILURE; 555 } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_SERVICE_KEYBLOCK_X)) { 556 return get_service_keyblock(minor_status, ctx, data_set); 557 } else { 558 *minor_status = 0; 559 return GSS_S_FAILURE; 560 } 561} 562 563