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