Home | History | Annotate | Line # | Download | only in mech
      1 /*	$NetBSD: gss_krb5.c,v 1.2 2017/01/28 21:31:46 christos Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2005 Doug Rabson
      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  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     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  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  * SUCH DAMAGE.
     27  *
     28  *	$FreeBSD: src/lib/libgssapi/gss_krb5.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
     29  */
     30 
     31 #include "mech_locl.h"
     32 
     33 #include <krb5/krb5.h>
     34 #include <krb5/roken.h>
     35 
     36 
     37 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
     38 gss_krb5_copy_ccache(OM_uint32 *minor_status,
     39 		     gss_cred_id_t cred,
     40 		     krb5_ccache out)
     41 {
     42     gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
     43     krb5_context context;
     44     krb5_error_code kret;
     45     krb5_ccache id;
     46     OM_uint32 ret;
     47     char *str = NULL;
     48 
     49     ret = gss_inquire_cred_by_oid(minor_status,
     50 				  cred,
     51 				  GSS_KRB5_COPY_CCACHE_X,
     52 				  &data_set);
     53     if (ret)
     54 	return ret;
     55 
     56     if (data_set == GSS_C_NO_BUFFER_SET || data_set->count < 1) {
     57 	gss_release_buffer_set(minor_status, &data_set);
     58 	*minor_status = EINVAL;
     59 	return GSS_S_FAILURE;
     60     }
     61 
     62     kret = krb5_init_context(&context);
     63     if (kret) {
     64 	*minor_status = kret;
     65 	gss_release_buffer_set(minor_status, &data_set);
     66 	return GSS_S_FAILURE;
     67     }
     68 
     69     kret = asprintf(&str, "%.*s", (int)data_set->elements[0].length,
     70 		    (char *)data_set->elements[0].value);
     71     gss_release_buffer_set(minor_status, &data_set);
     72     if (kret < 0 || str == NULL) {
     73 	*minor_status = ENOMEM;
     74 	return GSS_S_FAILURE;
     75     }
     76 
     77     kret = krb5_cc_resolve(context, str, &id);
     78     free(str);
     79     if (kret) {
     80 	*minor_status = kret;
     81 	return GSS_S_FAILURE;
     82     }
     83 
     84     kret = krb5_cc_copy_cache(context, id, out);
     85     krb5_cc_close(context, id);
     86     krb5_free_context(context);
     87     if (kret) {
     88 	*minor_status = kret;
     89 	return GSS_S_FAILURE;
     90     }
     91 
     92     return ret;
     93 }
     94 
     95 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
     96 gss_krb5_import_cred(OM_uint32 *minor_status,
     97 		     krb5_ccache id,
     98 		     krb5_principal keytab_principal,
     99 		     krb5_keytab keytab,
    100 		     gss_cred_id_t *cred)
    101 {
    102     gss_buffer_desc buffer;
    103     OM_uint32 major_status;
    104     krb5_context context;
    105     krb5_error_code ret;
    106     krb5_storage *sp;
    107     krb5_data data;
    108     char *str;
    109 
    110     *cred = GSS_C_NO_CREDENTIAL;
    111 
    112     ret = krb5_init_context(&context);
    113     if (ret) {
    114 	*minor_status = ret;
    115 	return GSS_S_FAILURE;
    116     }
    117 
    118     sp = krb5_storage_emem();
    119     if (sp == NULL) {
    120 	*minor_status = ENOMEM;
    121 	major_status = GSS_S_FAILURE;
    122 	goto out;
    123     }
    124 
    125     if (id) {
    126 	ret = krb5_cc_get_full_name(context, id, &str);
    127 	if (ret == 0) {
    128 	    ret = krb5_store_string(sp, str);
    129 	    free(str);
    130 	}
    131     } else
    132 	ret = krb5_store_string(sp, "");
    133     if (ret) {
    134 	*minor_status = ret;
    135 	major_status = GSS_S_FAILURE;
    136 	goto out;
    137     }
    138 
    139     if (keytab_principal) {
    140 	ret = krb5_unparse_name(context, keytab_principal, &str);
    141 	if (ret == 0) {
    142 	    ret = krb5_store_string(sp, str);
    143 	    free(str);
    144 	}
    145     } else
    146 	krb5_store_string(sp, "");
    147     if (ret) {
    148 	*minor_status = ret;
    149 	major_status = GSS_S_FAILURE;
    150 	goto out;
    151     }
    152 
    153 
    154     if (keytab) {
    155 	ret = krb5_kt_get_full_name(context, keytab, &str);
    156 	if (ret == 0) {
    157 	    ret = krb5_store_string(sp, str);
    158 	    free(str);
    159 	}
    160     } else
    161 	krb5_store_string(sp, "");
    162     if (ret) {
    163 	*minor_status = ret;
    164 	major_status = GSS_S_FAILURE;
    165 	goto out;
    166     }
    167 
    168     ret = krb5_storage_to_data(sp, &data);
    169     if (ret) {
    170 	*minor_status = ret;
    171 	major_status = GSS_S_FAILURE;
    172 	goto out;
    173     }
    174 
    175     buffer.value = data.data;
    176     buffer.length = data.length;
    177 
    178     major_status = gss_set_cred_option(minor_status,
    179 				       cred,
    180 				       GSS_KRB5_IMPORT_CRED_X,
    181 				       &buffer);
    182     krb5_data_free(&data);
    183 out:
    184     if (sp)
    185 	krb5_storage_free(sp);
    186     krb5_free_context(context);
    187     return major_status;
    188 }
    189 
    190 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
    191 gsskrb5_register_acceptor_identity(const char *identity)
    192 {
    193 	gssapi_mech_interface m;
    194 	gss_buffer_desc buffer;
    195 	OM_uint32 junk;
    196 
    197 	_gss_load_mech();
    198 
    199 	buffer.value = rk_UNCONST(identity);
    200 	buffer.length = strlen(identity);
    201 
    202 	m = __gss_get_mechanism(GSS_KRB5_MECHANISM);
    203 	if (m == NULL || m->gm_set_sec_context_option == NULL)
    204 	    return GSS_S_FAILURE;
    205 
    206 	return m->gm_set_sec_context_option(&junk, NULL,
    207 	        GSS_KRB5_REGISTER_ACCEPTOR_IDENTITY_X, &buffer);
    208 }
    209 
    210 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
    211 krb5_gss_register_acceptor_identity(const char *identity)
    212 {
    213     return gsskrb5_register_acceptor_identity(identity);
    214 }
    215 
    216 
    217 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
    218 gsskrb5_set_dns_canonicalize(int flag)
    219 {
    220         struct _gss_mech_switch	*m;
    221 	gss_buffer_desc buffer;
    222 	OM_uint32 junk;
    223 	char b = (flag != 0);
    224 
    225 	_gss_load_mech();
    226 
    227 	buffer.value = &b;
    228 	buffer.length = sizeof(b);
    229 
    230 	HEIM_SLIST_FOREACH(m, &_gss_mechs, gm_link) {
    231 		if (m->gm_mech.gm_set_sec_context_option == NULL)
    232 			continue;
    233 		m->gm_mech.gm_set_sec_context_option(&junk, NULL,
    234 		    GSS_KRB5_SET_DNS_CANONICALIZE_X, &buffer);
    235 	}
    236 
    237 	return (GSS_S_COMPLETE);
    238 }
    239 
    240 
    241 
    242 static krb5_error_code
    243 set_key(krb5_keyblock *keyblock, gss_krb5_lucid_key_t *key)
    244 {
    245     key->type = keyblock->keytype;
    246     key->length = keyblock->keyvalue.length;
    247     key->data = malloc(key->length);
    248     if (key->data == NULL && key->length != 0)
    249 	return ENOMEM;
    250     memcpy(key->data, keyblock->keyvalue.data, key->length);
    251     return 0;
    252 }
    253 
    254 static void
    255 free_key(gss_krb5_lucid_key_t *key)
    256 {
    257     memset(key->data, 0, key->length);
    258     free(key->data);
    259     memset(key, 0, sizeof(*key));
    260 }
    261 
    262 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
    263 gss_krb5_export_lucid_sec_context(OM_uint32 *minor_status,
    264 				  gss_ctx_id_t *context_handle,
    265 				  OM_uint32 version,
    266 				  void **rctx)
    267 {
    268     krb5_context context = NULL;
    269     krb5_error_code ret;
    270     gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
    271     OM_uint32 major_status;
    272     gss_krb5_lucid_context_v1_t *ctx = NULL;
    273     krb5_storage *sp = NULL;
    274     uint32_t num;
    275 
    276     if (context_handle == NULL
    277 	|| *context_handle == GSS_C_NO_CONTEXT
    278 	|| version != 1)
    279     {
    280 	*minor_status = EINVAL;
    281 	return GSS_S_FAILURE;
    282     }
    283 
    284     major_status =
    285 	gss_inquire_sec_context_by_oid (minor_status,
    286 					*context_handle,
    287 					GSS_KRB5_EXPORT_LUCID_CONTEXT_V1_X,
    288 					&data_set);
    289     if (major_status)
    290 	return major_status;
    291 
    292     if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1) {
    293 	gss_release_buffer_set(minor_status, &data_set);
    294 	*minor_status = EINVAL;
    295 	return GSS_S_FAILURE;
    296     }
    297 
    298     ret = krb5_init_context(&context);
    299     if (ret)
    300 	goto out;
    301 
    302     ctx = calloc(1, sizeof(*ctx));
    303     if (ctx == NULL) {
    304 	ret = ENOMEM;
    305 	goto out;
    306     }
    307 
    308     sp = krb5_storage_from_mem(data_set->elements[0].value,
    309 			       data_set->elements[0].length);
    310     if (sp == NULL) {
    311 	ret = ENOMEM;
    312 	goto out;
    313     }
    314 
    315     ret = krb5_ret_uint32(sp, &num);
    316     if (ret) goto out;
    317     if (num != 1) {
    318 	ret = EINVAL;
    319 	goto out;
    320     }
    321     ctx->version = 1;
    322     /* initiator */
    323     ret = krb5_ret_uint32(sp, &ctx->initiate);
    324     if (ret) goto out;
    325     /* endtime */
    326     ret = krb5_ret_uint32(sp, &ctx->endtime);
    327     if (ret) goto out;
    328     /* send_seq */
    329     ret = krb5_ret_uint32(sp, &num);
    330     if (ret) goto out;
    331     ctx->send_seq = ((uint64_t)num) << 32;
    332     ret = krb5_ret_uint32(sp, &num);
    333     if (ret) goto out;
    334     ctx->send_seq |= num;
    335     /* recv_seq */
    336     ret = krb5_ret_uint32(sp, &num);
    337     if (ret) goto out;
    338     ctx->recv_seq = ((uint64_t)num) << 32;
    339     ret = krb5_ret_uint32(sp, &num);
    340     if (ret) goto out;
    341     ctx->recv_seq |= num;
    342     /* protocol */
    343     ret = krb5_ret_uint32(sp, &ctx->protocol);
    344     if (ret) goto out;
    345     if (ctx->protocol == 0) {
    346 	krb5_keyblock key;
    347 
    348 	/* sign_alg */
    349 	ret = krb5_ret_uint32(sp, &ctx->rfc1964_kd.sign_alg);
    350 	if (ret) goto out;
    351 	/* seal_alg */
    352 	ret = krb5_ret_uint32(sp, &ctx->rfc1964_kd.seal_alg);
    353 	if (ret) goto out;
    354 	/* ctx_key */
    355 	ret = krb5_ret_keyblock(sp, &key);
    356 	if (ret) goto out;
    357 	ret = set_key(&key, &ctx->rfc1964_kd.ctx_key);
    358 	krb5_free_keyblock_contents(context, &key);
    359 	if (ret) goto out;
    360     } else if (ctx->protocol == 1) {
    361 	krb5_keyblock key;
    362 
    363 	/* acceptor_subkey */
    364 	ret = krb5_ret_uint32(sp, &ctx->cfx_kd.have_acceptor_subkey);
    365 	if (ret) goto out;
    366 	/* ctx_key */
    367 	ret = krb5_ret_keyblock(sp, &key);
    368 	if (ret) goto out;
    369 	ret = set_key(&key, &ctx->cfx_kd.ctx_key);
    370 	krb5_free_keyblock_contents(context, &key);
    371 	if (ret) goto out;
    372 	/* acceptor_subkey */
    373 	if (ctx->cfx_kd.have_acceptor_subkey) {
    374 	    ret = krb5_ret_keyblock(sp, &key);
    375 	    if (ret) goto out;
    376 	    ret = set_key(&key, &ctx->cfx_kd.acceptor_subkey);
    377 	    krb5_free_keyblock_contents(context, &key);
    378 	    if (ret) goto out;
    379 	}
    380     } else {
    381 	ret = EINVAL;
    382 	goto out;
    383     }
    384 
    385     *rctx = ctx;
    386 
    387 out:
    388     gss_release_buffer_set(minor_status, &data_set);
    389     if (sp)
    390 	krb5_storage_free(sp);
    391     if (context)
    392 	krb5_free_context(context);
    393 
    394     if (ret) {
    395 	if (ctx)
    396 	    gss_krb5_free_lucid_sec_context(NULL, ctx);
    397 
    398 	*minor_status = ret;
    399 	return GSS_S_FAILURE;
    400     }
    401     *minor_status = 0;
    402     return GSS_S_COMPLETE;
    403 }
    404 
    405 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
    406 gss_krb5_free_lucid_sec_context(OM_uint32 *minor_status, void *c)
    407 {
    408     gss_krb5_lucid_context_v1_t *ctx = c;
    409 
    410     if (ctx->version != 1) {
    411 	if (minor_status)
    412 	    *minor_status = 0;
    413 	return GSS_S_FAILURE;
    414     }
    415 
    416     if (ctx->protocol == 0) {
    417 	free_key(&ctx->rfc1964_kd.ctx_key);
    418     } else if (ctx->protocol == 1) {
    419 	free_key(&ctx->cfx_kd.ctx_key);
    420 	if (ctx->cfx_kd.have_acceptor_subkey)
    421 	    free_key(&ctx->cfx_kd.acceptor_subkey);
    422     }
    423     free(ctx);
    424     if (minor_status)
    425 	*minor_status = 0;
    426     return GSS_S_COMPLETE;
    427 }
    428 
    429 /*
    430  *
    431  */
    432 
    433 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
    434 gss_krb5_set_allowable_enctypes(OM_uint32 *minor_status,
    435 				gss_cred_id_t cred,
    436 				OM_uint32 num_enctypes,
    437 				int32_t *enctypes)
    438 {
    439     krb5_error_code ret;
    440     OM_uint32 maj_status;
    441     gss_buffer_desc buffer;
    442     krb5_storage *sp;
    443     krb5_data data;
    444     size_t i;
    445 
    446     sp = krb5_storage_emem();
    447     if (sp == NULL) {
    448 	*minor_status = ENOMEM;
    449 	maj_status = GSS_S_FAILURE;
    450 	goto out;
    451     }
    452 
    453     for (i = 0; i < num_enctypes; i++) {
    454 	ret = krb5_store_int32(sp, enctypes[i]);
    455 	if (ret) {
    456 	    *minor_status = ret;
    457 	    maj_status = GSS_S_FAILURE;
    458 	    goto out;
    459 	}
    460     }
    461 
    462     ret = krb5_storage_to_data(sp, &data);
    463     if (ret) {
    464 	*minor_status = ret;
    465 	maj_status = GSS_S_FAILURE;
    466 	goto out;
    467     }
    468 
    469     buffer.value = data.data;
    470     buffer.length = data.length;
    471 
    472     maj_status = gss_set_cred_option(minor_status,
    473 				     &cred,
    474 				     GSS_KRB5_SET_ALLOWABLE_ENCTYPES_X,
    475 				     &buffer);
    476     krb5_data_free(&data);
    477 out:
    478     if (sp)
    479 	krb5_storage_free(sp);
    480     return maj_status;
    481 }
    482 
    483 /*
    484  *
    485  */
    486 
    487 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
    488 gsskrb5_set_send_to_kdc(struct gsskrb5_send_to_kdc *c)
    489 {
    490     struct _gss_mech_switch *m;
    491     gss_buffer_desc buffer;
    492     OM_uint32 junk;
    493 
    494     _gss_load_mech();
    495 
    496     if (c) {
    497 	buffer.value = c;
    498 	buffer.length = sizeof(*c);
    499     } else {
    500 	buffer.value = NULL;
    501 	buffer.length = 0;
    502     }
    503 
    504     HEIM_SLIST_FOREACH(m, &_gss_mechs, gm_link) {
    505 	if (m->gm_mech.gm_set_sec_context_option == NULL)
    506 	    continue;
    507 	m->gm_mech.gm_set_sec_context_option(&junk, NULL,
    508 	    GSS_KRB5_SEND_TO_KDC_X, &buffer);
    509     }
    510 
    511     return (GSS_S_COMPLETE);
    512 }
    513 
    514 /*
    515  *
    516  */
    517 
    518 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
    519 gss_krb5_ccache_name(OM_uint32 *minor_status,
    520 		     const char *name,
    521 		     const char **out_name)
    522 {
    523     struct _gss_mech_switch *m;
    524     gss_buffer_desc buffer;
    525     OM_uint32 junk;
    526 
    527     _gss_load_mech();
    528 
    529     if (out_name)
    530 	*out_name = NULL;
    531 
    532     buffer.value = rk_UNCONST(name);
    533     buffer.length = strlen(name);
    534 
    535     HEIM_SLIST_FOREACH(m, &_gss_mechs, gm_link) {
    536 	if (m->gm_mech.gm_set_sec_context_option == NULL)
    537 	    continue;
    538 	m->gm_mech.gm_set_sec_context_option(&junk, NULL,
    539 	    GSS_KRB5_CCACHE_NAME_X, &buffer);
    540     }
    541 
    542     return (GSS_S_COMPLETE);
    543 }
    544 
    545 
    546 /*
    547  *
    548  */
    549 
    550 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
    551 gsskrb5_extract_authtime_from_sec_context(OM_uint32 *minor_status,
    552 					  gss_ctx_id_t context_handle,
    553 					  time_t *authtime)
    554 {
    555     gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
    556     OM_uint32 maj_stat;
    557 
    558     if (context_handle == GSS_C_NO_CONTEXT) {
    559 	*minor_status = EINVAL;
    560 	return GSS_S_FAILURE;
    561     }
    562 
    563     maj_stat =
    564 	gss_inquire_sec_context_by_oid (minor_status,
    565 					context_handle,
    566 					GSS_KRB5_GET_AUTHTIME_X,
    567 					&data_set);
    568     if (maj_stat)
    569 	return maj_stat;
    570 
    571     if (data_set == GSS_C_NO_BUFFER_SET) {
    572 	gss_release_buffer_set(minor_status, &data_set);
    573 	*minor_status = EINVAL;
    574 	return GSS_S_FAILURE;
    575     }
    576 
    577     if (data_set->count != 1) {
    578 	gss_release_buffer_set(minor_status, &data_set);
    579 	*minor_status = EINVAL;
    580 	return GSS_S_FAILURE;
    581     }
    582 
    583     if (data_set->elements[0].length != 4) {
    584 	gss_release_buffer_set(minor_status, &data_set);
    585 	*minor_status = EINVAL;
    586 	return GSS_S_FAILURE;
    587     }
    588 
    589     {
    590 	unsigned char *buf = data_set->elements[0].value;
    591 	*authtime = (buf[3] <<24) | (buf[2] << 16) |
    592 	    (buf[1] << 8) | (buf[0] << 0);
    593     }
    594 
    595     gss_release_buffer_set(minor_status, &data_set);
    596 
    597     *minor_status = 0;
    598     return GSS_S_COMPLETE;
    599 }
    600 
    601 /*
    602  *
    603  */
    604 
    605 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
    606 gsskrb5_extract_authz_data_from_sec_context(OM_uint32 *minor_status,
    607 					    gss_ctx_id_t context_handle,
    608 					    int ad_type,
    609 					    gss_buffer_t ad_data)
    610 {
    611     gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
    612     OM_uint32 maj_stat;
    613     gss_OID_desc oid_flat;
    614     heim_oid baseoid, oid;
    615     size_t size;
    616 
    617     if (context_handle == GSS_C_NO_CONTEXT) {
    618 	*minor_status = EINVAL;
    619 	return GSS_S_FAILURE;
    620     }
    621 
    622     /* All this to append an integer to an oid... */
    623 
    624     if (der_get_oid(GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X->elements,
    625 		    GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X->length,
    626 		    &baseoid, NULL) != 0) {
    627 	*minor_status = EINVAL;
    628 	return GSS_S_FAILURE;
    629     }
    630 
    631     oid.length = baseoid.length + 1;
    632     oid.components = calloc(oid.length, sizeof(*oid.components));
    633     if (oid.components == NULL) {
    634 	der_free_oid(&baseoid);
    635 
    636 	*minor_status = ENOMEM;
    637 	return GSS_S_FAILURE;
    638     }
    639 
    640     memcpy(oid.components, baseoid.components,
    641 	   baseoid.length * sizeof(*baseoid.components));
    642 
    643     der_free_oid(&baseoid);
    644 
    645     oid.components[oid.length - 1] = ad_type;
    646 
    647     oid_flat.length = der_length_oid(&oid);
    648     oid_flat.elements = malloc(oid_flat.length);
    649     if (oid_flat.elements == NULL) {
    650 	free(oid.components);
    651 	*minor_status = ENOMEM;
    652 	return GSS_S_FAILURE;
    653     }
    654 
    655     if (der_put_oid((unsigned char *)oid_flat.elements + oid_flat.length - 1,
    656 		    oid_flat.length, &oid, &size) != 0) {
    657 	free(oid.components);
    658 	free(oid_flat.elements);
    659 	*minor_status = EINVAL;
    660 	return GSS_S_FAILURE;
    661     }
    662     if (oid_flat.length != size)
    663 	abort();
    664 
    665     free(oid.components);
    666 
    667     /* FINALLY, we have the OID */
    668 
    669     maj_stat = gss_inquire_sec_context_by_oid (minor_status,
    670 					       context_handle,
    671 					       &oid_flat,
    672 					       &data_set);
    673 
    674     free(oid_flat.elements);
    675 
    676     if (maj_stat)
    677 	return maj_stat;
    678 
    679     if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1) {
    680 	gss_release_buffer_set(minor_status, &data_set);
    681 	*minor_status = EINVAL;
    682 	return GSS_S_FAILURE;
    683     }
    684 
    685     ad_data->value = malloc(data_set->elements[0].length);
    686     if (ad_data->value == NULL) {
    687 	gss_release_buffer_set(minor_status, &data_set);
    688 	*minor_status = ENOMEM;
    689 	return GSS_S_FAILURE;
    690     }
    691 
    692     ad_data->length = data_set->elements[0].length;
    693     memcpy(ad_data->value, data_set->elements[0].value, ad_data->length);
    694     gss_release_buffer_set(minor_status, &data_set);
    695 
    696     *minor_status = 0;
    697     return GSS_S_COMPLETE;
    698 }
    699 
    700 /*
    701  *
    702  */
    703 
    704 static OM_uint32
    705 gsskrb5_extract_key(OM_uint32 *minor_status,
    706 		    gss_ctx_id_t context_handle,
    707 		    const gss_OID oid,
    708 		    krb5_keyblock **keyblock)
    709 {
    710     krb5_error_code ret;
    711     gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
    712     OM_uint32 major_status;
    713     krb5_context context = NULL;
    714     krb5_storage *sp = NULL;
    715 
    716     if (context_handle == GSS_C_NO_CONTEXT) {
    717 	*minor_status = EINVAL;
    718 	return GSS_S_FAILURE;
    719     }
    720 
    721     ret = krb5_init_context(&context);
    722     if(ret) {
    723 	*minor_status = ret;
    724 	return GSS_S_FAILURE;
    725     }
    726 
    727     major_status =
    728 	gss_inquire_sec_context_by_oid (minor_status,
    729 					context_handle,
    730 					oid,
    731 					&data_set);
    732     if (major_status)
    733 	return major_status;
    734 
    735     if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1) {
    736 	gss_release_buffer_set(minor_status, &data_set);
    737 	*minor_status = EINVAL;
    738 	return GSS_S_FAILURE;
    739     }
    740 
    741     sp = krb5_storage_from_mem(data_set->elements[0].value,
    742 			       data_set->elements[0].length);
    743     if (sp == NULL) {
    744 	ret = ENOMEM;
    745 	goto out;
    746     }
    747 
    748     *keyblock = calloc(1, sizeof(**keyblock));
    749     if (keyblock == NULL) {
    750 	ret = ENOMEM;
    751 	goto out;
    752     }
    753 
    754     ret = krb5_ret_keyblock(sp, *keyblock);
    755 
    756 out:
    757     gss_release_buffer_set(minor_status, &data_set);
    758     if (sp)
    759 	krb5_storage_free(sp);
    760     if (ret && keyblock) {
    761 	krb5_free_keyblock(context, *keyblock);
    762 	*keyblock = NULL;
    763     }
    764     if (context)
    765 	krb5_free_context(context);
    766 
    767     *minor_status = ret;
    768     if (ret)
    769 	return GSS_S_FAILURE;
    770 
    771     return GSS_S_COMPLETE;
    772 }
    773 
    774 /*
    775  *
    776  */
    777 
    778 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
    779 gsskrb5_extract_service_keyblock(OM_uint32 *minor_status,
    780 				 gss_ctx_id_t context_handle,
    781 				 krb5_keyblock **keyblock)
    782 {
    783     return gsskrb5_extract_key(minor_status,
    784 			       context_handle,
    785 			       GSS_KRB5_GET_SERVICE_KEYBLOCK_X,
    786 			       keyblock);
    787 }
    788 
    789 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
    790 gsskrb5_get_initiator_subkey(OM_uint32 *minor_status,
    791 			     gss_ctx_id_t context_handle,
    792 			     krb5_keyblock **keyblock)
    793 {
    794     return gsskrb5_extract_key(minor_status,
    795 			       context_handle,
    796 			       GSS_KRB5_GET_INITIATOR_SUBKEY_X,
    797 			       keyblock);
    798 }
    799 
    800 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
    801 gsskrb5_get_subkey(OM_uint32 *minor_status,
    802 		   gss_ctx_id_t context_handle,
    803 		   krb5_keyblock **keyblock)
    804 {
    805     return gsskrb5_extract_key(minor_status,
    806 			       context_handle,
    807 			       GSS_KRB5_GET_SUBKEY_X,
    808 			       keyblock);
    809 }
    810 
    811 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
    812 gsskrb5_set_default_realm(const char *realm)
    813 {
    814         struct _gss_mech_switch	*m;
    815 	gss_buffer_desc buffer;
    816 	OM_uint32 junk;
    817 
    818 	_gss_load_mech();
    819 
    820 	buffer.value = rk_UNCONST(realm);
    821 	buffer.length = strlen(realm);
    822 
    823 	HEIM_SLIST_FOREACH(m, &_gss_mechs, gm_link) {
    824 		if (m->gm_mech.gm_set_sec_context_option == NULL)
    825 			continue;
    826 		m->gm_mech.gm_set_sec_context_option(&junk, NULL,
    827 		    GSS_KRB5_SET_DEFAULT_REALM_X, &buffer);
    828 	}
    829 
    830 	return (GSS_S_COMPLETE);
    831 }
    832 
    833 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
    834 gss_krb5_get_tkt_flags(OM_uint32 *minor_status,
    835 		       gss_ctx_id_t context_handle,
    836 		       OM_uint32 *tkt_flags)
    837 {
    838 
    839     OM_uint32 major_status;
    840     gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
    841 
    842     if (context_handle == GSS_C_NO_CONTEXT) {
    843 	*minor_status = EINVAL;
    844 	return GSS_S_FAILURE;
    845     }
    846 
    847     major_status =
    848 	gss_inquire_sec_context_by_oid (minor_status,
    849 					context_handle,
    850 					GSS_KRB5_GET_TKT_FLAGS_X,
    851 					&data_set);
    852     if (major_status)
    853 	return major_status;
    854 
    855     if (data_set == GSS_C_NO_BUFFER_SET ||
    856 	data_set->count != 1 ||
    857 	data_set->elements[0].length < 4) {
    858 	gss_release_buffer_set(minor_status, &data_set);
    859 	*minor_status = EINVAL;
    860 	return GSS_S_FAILURE;
    861     }
    862 
    863     {
    864 	const u_char *p = data_set->elements[0].value;
    865 	*tkt_flags = (p[0] << 0) | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
    866     }
    867 
    868     gss_release_buffer_set(minor_status, &data_set);
    869     return GSS_S_COMPLETE;
    870 }
    871 
    872 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
    873 gsskrb5_set_time_offset(int offset)
    874 {
    875         struct _gss_mech_switch	*m;
    876 	gss_buffer_desc buffer;
    877 	OM_uint32 junk;
    878 	int32_t o = offset;
    879 
    880 	_gss_load_mech();
    881 
    882 	buffer.value = &o;
    883 	buffer.length = sizeof(o);
    884 
    885 	HEIM_SLIST_FOREACH(m, &_gss_mechs, gm_link) {
    886 		if (m->gm_mech.gm_set_sec_context_option == NULL)
    887 			continue;
    888 		m->gm_mech.gm_set_sec_context_option(&junk, NULL,
    889 		    GSS_KRB5_SET_TIME_OFFSET_X, &buffer);
    890 	}
    891 
    892 	return (GSS_S_COMPLETE);
    893 }
    894 
    895 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
    896 gsskrb5_get_time_offset(int *offset)
    897 {
    898         struct _gss_mech_switch	*m;
    899 	gss_buffer_desc buffer;
    900 	OM_uint32 maj_stat, junk;
    901 	int32_t o;
    902 
    903 	_gss_load_mech();
    904 
    905 	buffer.value = &o;
    906 	buffer.length = sizeof(o);
    907 
    908 	HEIM_SLIST_FOREACH(m, &_gss_mechs, gm_link) {
    909 		if (m->gm_mech.gm_set_sec_context_option == NULL)
    910 			continue;
    911 		maj_stat = m->gm_mech.gm_set_sec_context_option(&junk, NULL,
    912 		    GSS_KRB5_GET_TIME_OFFSET_X, &buffer);
    913 
    914 		if (maj_stat == GSS_S_COMPLETE) {
    915 			*offset = o;
    916 			return maj_stat;
    917 		}
    918 	}
    919 
    920 	return (GSS_S_UNAVAILABLE);
    921 }
    922 
    923 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
    924 gsskrb5_plugin_register(struct gsskrb5_krb5_plugin *c)
    925 {
    926     struct _gss_mech_switch *m;
    927     gss_buffer_desc buffer;
    928     OM_uint32 junk;
    929 
    930     _gss_load_mech();
    931 
    932     buffer.value = c;
    933     buffer.length = sizeof(*c);
    934 
    935     HEIM_SLIST_FOREACH(m, &_gss_mechs, gm_link) {
    936 	if (m->gm_mech.gm_set_sec_context_option == NULL)
    937 	    continue;
    938 	m->gm_mech.gm_set_sec_context_option(&junk, NULL,
    939 	    GSS_KRB5_PLUGIN_REGISTER_X, &buffer);
    940     }
    941 
    942     return (GSS_S_COMPLETE);
    943 }
    944