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