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    _gss_mg_encode_le_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   }
138    HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
139    if (ret)
140	goto out;
141    if (key == NULL) {
142	_gsskrb5_set_status(EINVAL, "have no subkey of type %d", keytype);
143	ret = EINVAL;
144	goto out;
145    }
146
147    ret = krb5_store_keyblock(sp, *key);
148    krb5_free_keyblock (context, key);
149    if (ret)
150	goto out;
151
152    ret = krb5_storage_to_data(sp, &data);
153    if (ret)
154	goto out;
155
156    {
157	gss_buffer_desc value;
158
159	value.length = data.length;
160	value.value = data.data;
161
162	maj_stat = gss_add_buffer_set_member(minor_status,
163					     &value,
164					     data_set);
165    }
166
167out:
168    krb5_data_free(&data);
169    if (sp)
170	krb5_storage_free(sp);
171    if (ret) {
172	*minor_status = ret;
173	maj_stat = GSS_S_FAILURE;
174    }
175    return maj_stat;
176}
177
178static OM_uint32 inquire_sec_context_get_sspi_session_key
179            (OM_uint32 *minor_status,
180             const gsskrb5_ctx context_handle,
181             krb5_context context,
182             gss_buffer_set_t *data_set)
183{
184    krb5_keyblock *key;
185    OM_uint32 maj_stat = GSS_S_COMPLETE;
186    krb5_error_code ret;
187    gss_buffer_desc value;
188
189    HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
190    ret = _gsskrb5i_get_token_key(context_handle, context, &key);
191    HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
192
193    if (ret)
194        goto out;
195    if (key == NULL) {
196        ret = EINVAL;
197        goto out;
198    }
199
200    value.length = key->keyvalue.length;
201    value.value = key->keyvalue.data;
202
203    maj_stat = gss_add_buffer_set_member(minor_status,
204                                         &value,
205                                         data_set);
206    krb5_free_keyblock(context, key);
207
208    /* MIT also returns the enctype encoded as an OID in data_set[1] */
209
210out:
211    if (ret) {
212        *minor_status = ret;
213        maj_stat = GSS_S_FAILURE;
214    }
215    return maj_stat;
216}
217
218static OM_uint32 inquire_sec_context_authz_data
219           (OM_uint32 *minor_status,
220            const gsskrb5_ctx context_handle,
221	    krb5_context context,
222            unsigned ad_type,
223            gss_buffer_set_t *data_set)
224{
225    krb5_data data;
226    gss_buffer_desc ad_data;
227    OM_uint32 ret;
228
229    *minor_status = 0;
230    *data_set = GSS_C_NO_BUFFER_SET;
231
232    HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
233    if (context_handle->ticket == NULL) {
234	HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
235	*minor_status = EINVAL;
236	_gsskrb5_set_status(EINVAL, "No ticket to obtain authz data from");
237	return GSS_S_NO_CONTEXT;
238    }
239
240    if (ad_type == KRB5_AUTHDATA_WIN2K_PAC && (context_handle->more_flags & PAC_VALID) == 0){
241	HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
242	*minor_status = EINVAL;
243	_gsskrb5_set_status(EINVAL, "pac not valid");
244	return GSS_S_NO_CONTEXT;
245    }
246
247    ret = krb5_ticket_get_authorization_data_type(context,
248						  context_handle->ticket,
249						  ad_type,
250						  &data);
251    HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
252    if (ret) {
253	*minor_status = ret;
254	return GSS_S_FAILURE;
255    }
256
257    ad_data.value = data.data;
258    ad_data.length = data.length;
259
260    ret = gss_add_buffer_set_member(minor_status,
261				    &ad_data,
262				    data_set);
263
264    krb5_data_free(&data);
265
266    return ret;
267}
268
269static OM_uint32 inquire_sec_context_has_updated_spnego
270           (OM_uint32 *minor_status,
271            const gsskrb5_ctx context_handle,
272            gss_buffer_set_t *data_set)
273{
274    int is_updated = 0;
275
276    *minor_status = 0;
277    *data_set = GSS_C_NO_BUFFER_SET;
278
279    /*
280     * For Windows SPNEGO implementations, both the initiator and the
281     * acceptor are assumed to have been updated if a "newer" [CLAR] or
282     * different enctype is negotiated for use by the Kerberos GSS-API
283     * mechanism.
284     */
285    HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
286    is_updated = (context_handle->more_flags & IS_CFX);
287    if (is_updated == 0) {
288	krb5_keyblock *acceptor_subkey;
289
290	if (context_handle->more_flags & LOCAL)
291	    acceptor_subkey = context_handle->auth_context->remote_subkey;
292	else
293	    acceptor_subkey = context_handle->auth_context->local_subkey;
294
295	if (acceptor_subkey != NULL)
296	    is_updated = (acceptor_subkey->keytype !=
297			  context_handle->auth_context->keyblock->keytype);
298    }
299    HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
300
301    return is_updated ? GSS_S_COMPLETE : GSS_S_FAILURE;
302}
303
304/*
305 *
306 */
307
308static OM_uint32
309export_lucid_sec_context_v1(OM_uint32 *minor_status,
310			    gsskrb5_ctx context_handle,
311			    krb5_context context,
312			    gss_buffer_set_t *data_set)
313{
314    krb5_storage *sp = NULL;
315    OM_uint32 major_status = GSS_S_COMPLETE;
316    krb5_error_code ret;
317    krb5_keyblock *key = NULL;
318    int32_t number;
319    int is_cfx;
320    krb5_data data;
321
322    *minor_status = 0;
323
324    HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
325
326    is_cfx = (context_handle->more_flags & IS_CFX);
327
328    sp = krb5_storage_emem();
329    if (sp == NULL) {
330	_gsskrb5_clear_status();
331	ret = ENOMEM;
332	goto out;
333    }
334
335    ret = krb5_store_int32(sp, 1);
336    if (ret) goto out;
337    ret = krb5_store_int32(sp, (context_handle->more_flags & LOCAL) ? 1 : 0);
338    if (ret) goto out;
339    ret = krb5_store_int32(sp, (int32_t)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->gk5c.flags & GK5C_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 = (OM_uint32)ctx->ticket->ticket.authtime;
455
456    HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
457
458    _gss_mg_encode_le_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 */
533
534OM_uint32 GSSAPI_CALLCONV _gsskrb5_inquire_sec_context_by_oid
535           (OM_uint32 *minor_status,
536            const gss_ctx_id_t context_handle,
537            const gss_OID desired_object,
538            gss_buffer_set_t *data_set)
539{
540    krb5_context context;
541    const gsskrb5_ctx ctx = (const gsskrb5_ctx) context_handle;
542    unsigned suffix;
543
544    if (ctx == NULL) {
545	*minor_status = EINVAL;
546	return GSS_S_NO_CONTEXT;
547    }
548
549    GSSAPI_KRB5_INIT (&context);
550
551    if (gss_oid_equal(desired_object, GSS_KRB5_GET_TKT_FLAGS_X)) {
552	return inquire_sec_context_tkt_flags(minor_status,
553					     ctx,
554					     data_set);
555    } else if (gss_oid_equal(desired_object, GSS_C_PEER_HAS_UPDATED_SPNEGO)) {
556	return inquire_sec_context_has_updated_spnego(minor_status,
557						      ctx,
558						      data_set);
559    } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_SUBKEY_X)) {
560	return inquire_sec_context_get_subkey(minor_status,
561					      ctx,
562					      context,
563					      TOKEN_KEY,
564					      data_set);
565    } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_INITIATOR_SUBKEY_X)) {
566	return inquire_sec_context_get_subkey(minor_status,
567					      ctx,
568					      context,
569					      INITIATOR_KEY,
570					      data_set);
571    } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_ACCEPTOR_SUBKEY_X)) {
572	return inquire_sec_context_get_subkey(minor_status,
573					      ctx,
574					      context,
575					      ACCEPTOR_KEY,
576					      data_set);
577    } else if (gss_oid_equal(desired_object, GSS_C_INQ_SSPI_SESSION_KEY)) {
578        return inquire_sec_context_get_sspi_session_key(minor_status,
579                                                        ctx,
580                                                        context,
581                                                        data_set);
582    } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_AUTHTIME_X)) {
583	return get_authtime(minor_status, ctx, data_set);
584    } else if (oid_prefix_equal(desired_object,
585				GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X,
586				&suffix)) {
587	return inquire_sec_context_authz_data(minor_status,
588					      ctx,
589					      context,
590					      suffix,
591					      data_set);
592    } else if (oid_prefix_equal(desired_object,
593				GSS_KRB5_EXPORT_LUCID_CONTEXT_X,
594				&suffix)) {
595	if (suffix == 1)
596	    return export_lucid_sec_context_v1(minor_status,
597					       ctx,
598					       context,
599					       data_set);
600	*minor_status = 0;
601	return GSS_S_FAILURE;
602    } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_SERVICE_KEYBLOCK_X)) {
603	return get_service_keyblock(minor_status, ctx, data_set);
604    } else {
605	*minor_status = 0;
606	return GSS_S_FAILURE;
607    }
608}
609
610