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
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    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
172out:
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
184static 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
216out:
217    if (ret) {
218        *minor_status = ret;
219        maj_stat = GSS_S_FAILURE;
220    }
221    return maj_stat;
222}
223
224static 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
268static 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
307static OM_uint32
308export_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
423out:
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
436static OM_uint32
437get_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
468static OM_uint32
469get_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
519out:
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
533OM_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