Home | History | Annotate | Line # | Download | only in krb5
      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 
     37 static int
     38 oid_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 
     74 static 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 
    104 enum keytype { ACCEPTOR_KEY, INITIATOR_KEY, TOKEN_KEY };
    105 
    106 static 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 
    172 out:
    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 
    184 static 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 
    216 out:
    217     if (ret) {
    218         *minor_status = ret;
    219         maj_stat = GSS_S_FAILURE;
    220     }
    221     return maj_stat;
    222 }
    223 
    224 static 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 
    268 static 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 
    307 static OM_uint32
    308 export_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 
    423 out:
    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 
    436 static OM_uint32
    437 get_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 
    468 static OM_uint32
    469 get_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 
    519 out:
    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 
    533 OM_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