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 "gsskrb5_locl.h"
34
35static int
36oid_prefix_equal(gss_OID oid_enc, gss_OID prefix_enc, unsigned *suffix)
37{
38    int ret;
39    heim_oid oid;
40    heim_oid prefix;
41
42    *suffix = 0;
43
44    ret = der_get_oid(oid_enc->elements, oid_enc->length,
45		      &oid, NULL);
46    if (ret) {
47	return 0;
48    }
49
50    ret = der_get_oid(prefix_enc->elements, prefix_enc->length,
51		      &prefix, NULL);
52    if (ret) {
53	der_free_oid(&oid);
54	return 0;
55    }
56
57    ret = 0;
58
59    if (oid.length - 1 == prefix.length) {
60	*suffix = oid.components[oid.length - 1];
61	oid.length--;
62	ret = (der_heim_oid_cmp(&oid, &prefix) == 0);
63	oid.length++;
64    }
65
66    der_free_oid(&oid);
67    der_free_oid(&prefix);
68
69    return ret;
70}
71
72static OM_uint32 inquire_sec_context_tkt_flags
73           (OM_uint32 *minor_status,
74            const gsskrb5_ctx context_handle,
75            gss_buffer_set_t *data_set)
76{
77    OM_uint32 tkt_flags;
78    unsigned char buf[4];
79    gss_buffer_desc value;
80
81    HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
82
83    if (context_handle->ticket == NULL) {
84	HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
85	_gsskrb5_set_status(EINVAL, "No ticket from which to obtain flags");
86	*minor_status = EINVAL;
87	return GSS_S_BAD_MECH;
88    }
89
90    tkt_flags = TicketFlags2int(context_handle->ticket->ticket.flags);
91    HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
92
93    _gsskrb5_encode_om_uint32(tkt_flags, buf);
94    value.length = sizeof(buf);
95    value.value = buf;
96
97    return gss_add_buffer_set_member(minor_status,
98				     &value,
99				     data_set);
100}
101
102enum keytype { ACCEPTOR_KEY, INITIATOR_KEY, TOKEN_KEY };
103
104static OM_uint32 inquire_sec_context_get_subkey
105           (OM_uint32 *minor_status,
106            const gsskrb5_ctx context_handle,
107	    krb5_context context,
108	    enum keytype keytype,
109            gss_buffer_set_t *data_set)
110{
111    krb5_keyblock *key = NULL;
112    krb5_storage *sp = NULL;
113    krb5_data data;
114    OM_uint32 maj_stat = GSS_S_COMPLETE;
115    krb5_error_code ret;
116
117    krb5_data_zero(&data);
118
119    sp = krb5_storage_emem();
120    if (sp == NULL) {
121	_gsskrb5_clear_status();
122	ret = ENOMEM;
123	goto out;
124    }
125
126    HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
127    switch(keytype) {
128    case ACCEPTOR_KEY:
129	ret = _gsskrb5i_get_acceptor_subkey(context_handle, context, &key);
130	break;
131    case INITIATOR_KEY:
132	ret = _gsskrb5i_get_initiator_subkey(context_handle, context, &key);
133	break;
134    case TOKEN_KEY:
135	ret = _gsskrb5i_get_token_key(context_handle, context, &key);
136	break;
137    default:
138	_gsskrb5_set_status(EINVAL, "%d is not a valid subkey type", keytype);
139	ret = EINVAL;
140	break;
141   }
142    HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
143    if (ret)
144	goto out;
145    if (key == NULL) {
146	_gsskrb5_set_status(EINVAL, "have no subkey of type %d", keytype);
147	ret = EINVAL;
148	goto out;
149    }
150
151    ret = krb5_store_keyblock(sp, *key);
152    krb5_free_keyblock (context, key);
153    if (ret)
154	goto out;
155
156    ret = krb5_storage_to_data(sp, &data);
157    if (ret)
158	goto out;
159
160    {
161	gss_buffer_desc value;
162
163	value.length = data.length;
164	value.value = data.data;
165
166	maj_stat = gss_add_buffer_set_member(minor_status,
167					     &value,
168					     data_set);
169    }
170
171out:
172    krb5_data_free(&data);
173    if (sp)
174	krb5_storage_free(sp);
175    if (ret) {
176	*minor_status = ret;
177	maj_stat = GSS_S_FAILURE;
178    }
179    return maj_stat;
180}
181
182static OM_uint32 inquire_sec_context_get_sspi_session_key
183            (OM_uint32 *minor_status,
184             const gsskrb5_ctx context_handle,
185             krb5_context context,
186             gss_buffer_set_t *data_set)
187{
188    krb5_keyblock *key;
189    OM_uint32 maj_stat = GSS_S_COMPLETE;
190    krb5_error_code ret;
191    gss_buffer_desc value;
192
193    HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
194    ret = _gsskrb5i_get_token_key(context_handle, context, &key);
195    HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
196
197    if (ret)
198        goto out;
199    if (key == NULL) {
200        ret = EINVAL;
201        goto out;
202    }
203
204    value.length = key->keyvalue.length;
205    value.value = key->keyvalue.data;
206
207    maj_stat = gss_add_buffer_set_member(minor_status,
208                                         &value,
209                                         data_set);
210    krb5_free_keyblock(context, key);
211
212    /* MIT also returns the enctype encoded as an OID in data_set[1] */
213
214out:
215    if (ret) {
216        *minor_status = ret;
217        maj_stat = GSS_S_FAILURE;
218    }
219    return maj_stat;
220}
221
222static OM_uint32 inquire_sec_context_authz_data
223           (OM_uint32 *minor_status,
224            const gsskrb5_ctx context_handle,
225	    krb5_context context,
226            unsigned ad_type,
227            gss_buffer_set_t *data_set)
228{
229    krb5_data data;
230    gss_buffer_desc ad_data;
231    OM_uint32 ret;
232
233    *minor_status = 0;
234    *data_set = GSS_C_NO_BUFFER_SET;
235
236    HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
237    if (context_handle->ticket == NULL) {
238	HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
239	*minor_status = EINVAL;
240	_gsskrb5_set_status(EINVAL, "No ticket to obtain authz data from");
241	return GSS_S_NO_CONTEXT;
242    }
243
244    ret = krb5_ticket_get_authorization_data_type(context,
245						  context_handle->ticket,
246						  ad_type,
247						  &data);
248    HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
249    if (ret) {
250	*minor_status = ret;
251	return GSS_S_FAILURE;
252    }
253
254    ad_data.value = data.data;
255    ad_data.length = data.length;
256
257    ret = gss_add_buffer_set_member(minor_status,
258				    &ad_data,
259				    data_set);
260
261    krb5_data_free(&data);
262
263    return ret;
264}
265
266static OM_uint32 inquire_sec_context_has_updated_spnego
267           (OM_uint32 *minor_status,
268            const gsskrb5_ctx context_handle,
269            gss_buffer_set_t *data_set)
270{
271    int is_updated = 0;
272
273    *minor_status = 0;
274    *data_set = GSS_C_NO_BUFFER_SET;
275
276    /*
277     * For Windows SPNEGO implementations, both the initiator and the
278     * acceptor are assumed to have been updated if a "newer" [CLAR] or
279     * different enctype is negotiated for use by the Kerberos GSS-API
280     * mechanism.
281     */
282    HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
283    is_updated = (context_handle->more_flags & IS_CFX);
284    if (is_updated == 0) {
285	krb5_keyblock *acceptor_subkey;
286
287	if (context_handle->more_flags & LOCAL)
288	    acceptor_subkey = context_handle->auth_context->remote_subkey;
289	else
290	    acceptor_subkey = context_handle->auth_context->local_subkey;
291
292	if (acceptor_subkey != NULL)
293	    is_updated = (acceptor_subkey->keytype !=
294			  context_handle->auth_context->keyblock->keytype);
295    }
296    HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
297
298    return is_updated ? GSS_S_COMPLETE : GSS_S_FAILURE;
299}
300
301/*
302 *
303 */
304
305static OM_uint32
306export_lucid_sec_context_v1(OM_uint32 *minor_status,
307			    gsskrb5_ctx context_handle,
308			    krb5_context context,
309			    gss_buffer_set_t *data_set)
310{
311    krb5_storage *sp = NULL;
312    OM_uint32 major_status = GSS_S_COMPLETE;
313    krb5_error_code ret;
314    krb5_keyblock *key = NULL;
315    int32_t number;
316    int is_cfx;
317    krb5_data data;
318
319    *minor_status = 0;
320
321    HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
322
323    is_cfx = (context_handle->more_flags & IS_CFX);
324
325    sp = krb5_storage_emem();
326    if (sp == NULL) {
327	_gsskrb5_clear_status();
328	ret = ENOMEM;
329	goto out;
330    }
331
332    ret = krb5_store_int32(sp, 1);
333    if (ret) goto out;
334    ret = krb5_store_int32(sp, (context_handle->more_flags & LOCAL) ? 1 : 0);
335    if (ret) goto out;
336    ret = krb5_store_int32(sp, context_handle->lifetime);
337    if (ret) goto out;
338    krb5_auth_con_getlocalseqnumber (context,
339				     context_handle->auth_context,
340				     &number);
341    ret = krb5_store_uint32(sp, (uint32_t)0); /* store top half as zero */
342    if (ret) goto out;
343    ret = krb5_store_uint32(sp, (uint32_t)number);
344    if (ret) goto out;
345    krb5_auth_con_getremoteseqnumber (context,
346				      context_handle->auth_context,
347				      &number);
348    ret = krb5_store_uint32(sp, (uint32_t)0); /* store top half as zero */
349    if (ret) goto out;
350    ret = krb5_store_uint32(sp, (uint32_t)number);
351    if (ret) goto out;
352    ret = krb5_store_int32(sp, (is_cfx) ? 1 : 0);
353    if (ret) goto out;
354
355    ret = _gsskrb5i_get_token_key(context_handle, context, &key);
356    if (ret) goto out;
357
358    if (is_cfx == 0) {
359	int sign_alg, seal_alg;
360
361	switch (key->keytype) {
362	case ETYPE_DES_CBC_CRC:
363	case ETYPE_DES_CBC_MD4:
364	case ETYPE_DES_CBC_MD5:
365	    sign_alg = 0;
366	    seal_alg = 0;
367	    break;
368	case ETYPE_DES3_CBC_MD5:
369	case ETYPE_DES3_CBC_SHA1:
370	    sign_alg = 4;
371	    seal_alg = 2;
372	    break;
373	case ETYPE_ARCFOUR_HMAC_MD5:
374	case ETYPE_ARCFOUR_HMAC_MD5_56:
375	    sign_alg = 17;
376	    seal_alg = 16;
377	    break;
378	default:
379	    sign_alg = -1;
380	    seal_alg = -1;
381	    break;
382	}
383	ret = krb5_store_int32(sp, sign_alg);
384	if (ret) goto out;
385	ret = krb5_store_int32(sp, seal_alg);
386	if (ret) goto out;
387	/* ctx_key */
388	ret = krb5_store_keyblock(sp, *key);
389	if (ret) goto out;
390    } else {
391	int subkey_p = (context_handle->more_flags & ACCEPTOR_SUBKEY) ? 1 : 0;
392
393	/* have_acceptor_subkey */
394	ret = krb5_store_int32(sp, subkey_p);
395	if (ret) goto out;
396	/* ctx_key */
397	ret = krb5_store_keyblock(sp, *key);
398	if (ret) goto out;
399	/* acceptor_subkey */
400	if (subkey_p) {
401	    ret = krb5_store_keyblock(sp, *key);
402	    if (ret) goto out;
403	}
404    }
405    ret = krb5_storage_to_data(sp, &data);
406    if (ret) goto out;
407
408    {
409	gss_buffer_desc ad_data;
410
411	ad_data.value = data.data;
412	ad_data.length = data.length;
413
414	ret = gss_add_buffer_set_member(minor_status, &ad_data, data_set);
415	krb5_data_free(&data);
416	if (ret)
417	    goto out;
418    }
419
420out:
421    if (key)
422	krb5_free_keyblock (context, key);
423    if (sp)
424	krb5_storage_free(sp);
425    if (ret) {
426	*minor_status = ret;
427	major_status = GSS_S_FAILURE;
428    }
429    HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
430    return major_status;
431}
432
433static OM_uint32
434get_authtime(OM_uint32 *minor_status,
435	     gsskrb5_ctx ctx,
436	     gss_buffer_set_t *data_set)
437
438{
439    gss_buffer_desc value;
440    unsigned char buf[4];
441    OM_uint32 authtime;
442
443    HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
444    if (ctx->ticket == NULL) {
445	HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
446	_gsskrb5_set_status(EINVAL, "No ticket to obtain auth time from");
447	*minor_status = EINVAL;
448	return GSS_S_FAILURE;
449    }
450
451    authtime = ctx->ticket->ticket.authtime;
452
453    HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
454
455    _gsskrb5_encode_om_uint32(authtime, buf);
456    value.length = sizeof(buf);
457    value.value = buf;
458
459    return gss_add_buffer_set_member(minor_status,
460				     &value,
461				     data_set);
462}
463
464
465static OM_uint32
466get_service_keyblock
467        (OM_uint32 *minor_status,
468	 gsskrb5_ctx ctx,
469	 gss_buffer_set_t *data_set)
470{
471    krb5_storage *sp = NULL;
472    krb5_data data;
473    OM_uint32 maj_stat = GSS_S_COMPLETE;
474    krb5_error_code ret = EINVAL;
475
476    sp = krb5_storage_emem();
477    if (sp == NULL) {
478	_gsskrb5_clear_status();
479	*minor_status = ENOMEM;
480	return GSS_S_FAILURE;
481    }
482
483    HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
484    if (ctx->service_keyblock == NULL) {
485	HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
486	krb5_storage_free(sp);
487	_gsskrb5_set_status(EINVAL, "No service keyblock on gssapi context");
488	*minor_status = EINVAL;
489	return GSS_S_FAILURE;
490    }
491
492    krb5_data_zero(&data);
493
494    ret = krb5_store_keyblock(sp, *ctx->service_keyblock);
495
496    HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
497
498    if (ret)
499	goto out;
500
501    ret = krb5_storage_to_data(sp, &data);
502    if (ret)
503	goto out;
504
505    {
506	gss_buffer_desc value;
507
508	value.length = data.length;
509	value.value = data.data;
510
511	maj_stat = gss_add_buffer_set_member(minor_status,
512					     &value,
513					     data_set);
514    }
515
516out:
517    krb5_data_free(&data);
518    if (sp)
519	krb5_storage_free(sp);
520    if (ret) {
521	*minor_status = ret;
522	maj_stat = GSS_S_FAILURE;
523    }
524    return maj_stat;
525}
526/*
527 *
528 */
529
530OM_uint32 GSSAPI_CALLCONV _gsskrb5_inquire_sec_context_by_oid
531           (OM_uint32 *minor_status,
532            const gss_ctx_id_t context_handle,
533            const gss_OID desired_object,
534            gss_buffer_set_t *data_set)
535{
536    krb5_context context;
537    const gsskrb5_ctx ctx = (const gsskrb5_ctx) context_handle;
538    unsigned suffix;
539
540    if (ctx == NULL) {
541	*minor_status = EINVAL;
542	return GSS_S_NO_CONTEXT;
543    }
544
545    GSSAPI_KRB5_INIT (&context);
546
547    if (gss_oid_equal(desired_object, GSS_KRB5_GET_TKT_FLAGS_X)) {
548	return inquire_sec_context_tkt_flags(minor_status,
549					     ctx,
550					     data_set);
551    } else if (gss_oid_equal(desired_object, GSS_C_PEER_HAS_UPDATED_SPNEGO)) {
552	return inquire_sec_context_has_updated_spnego(minor_status,
553						      ctx,
554						      data_set);
555    } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_SUBKEY_X)) {
556	return inquire_sec_context_get_subkey(minor_status,
557					      ctx,
558					      context,
559					      TOKEN_KEY,
560					      data_set);
561    } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_INITIATOR_SUBKEY_X)) {
562	return inquire_sec_context_get_subkey(minor_status,
563					      ctx,
564					      context,
565					      INITIATOR_KEY,
566					      data_set);
567    } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_ACCEPTOR_SUBKEY_X)) {
568	return inquire_sec_context_get_subkey(minor_status,
569					      ctx,
570					      context,
571					      ACCEPTOR_KEY,
572					      data_set);
573    } else if (gss_oid_equal(desired_object, GSS_C_INQ_SSPI_SESSION_KEY)) {
574        return inquire_sec_context_get_sspi_session_key(minor_status,
575                                                        ctx,
576                                                        context,
577                                                        data_set);
578    } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_AUTHTIME_X)) {
579	return get_authtime(minor_status, ctx, data_set);
580    } else if (oid_prefix_equal(desired_object,
581				GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X,
582				&suffix)) {
583	return inquire_sec_context_authz_data(minor_status,
584					      ctx,
585					      context,
586					      suffix,
587					      data_set);
588    } else if (oid_prefix_equal(desired_object,
589				GSS_KRB5_EXPORT_LUCID_CONTEXT_X,
590				&suffix)) {
591	if (suffix == 1)
592	    return export_lucid_sec_context_v1(minor_status,
593					       ctx,
594					       context,
595					       data_set);
596	*minor_status = 0;
597	return GSS_S_FAILURE;
598    } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_SERVICE_KEYBLOCK_X)) {
599	return get_service_keyblock(minor_status, ctx, data_set);
600    } else {
601	*minor_status = 0;
602	return GSS_S_FAILURE;
603    }
604}
605
606