1 /*	$NetBSD: inquire_sec_context_by_oid.c,v 1.1.1.2 2014/04/24 12:45:29 pettai 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 
37 static int
oid_prefix_equal(gss_OID oid_enc,gss_OID prefix_enc,unsigned * suffix)38 oid_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 
inquire_sec_context_tkt_flags(OM_uint32 * minor_status,const gsskrb5_ctx context_handle,gss_buffer_set_t * data_set)74 static 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 
104 enum keytype { ACCEPTOR_KEY, INITIATOR_KEY, TOKEN_KEY };
105 
inquire_sec_context_get_subkey(OM_uint32 * minor_status,const gsskrb5_ctx context_handle,krb5_context context,enum keytype keytype,gss_buffer_set_t * data_set)106 static 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 
173 out:
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 
inquire_sec_context_get_sspi_session_key(OM_uint32 * minor_status,const gsskrb5_ctx context_handle,krb5_context context,gss_buffer_set_t * data_set)184 static 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 
216 out:
217     if (ret) {
218         *minor_status = ret;
219         maj_stat = GSS_S_FAILURE;
220     }
221     return maj_stat;
222 }
223 
inquire_sec_context_authz_data(OM_uint32 * minor_status,const gsskrb5_ctx context_handle,krb5_context context,unsigned ad_type,gss_buffer_set_t * data_set)224 static 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 
inquire_sec_context_has_updated_spnego(OM_uint32 * minor_status,const gsskrb5_ctx context_handle,gss_buffer_set_t * data_set)268 static 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 
307 static OM_uint32
export_lucid_sec_context_v1(OM_uint32 * minor_status,gsskrb5_ctx context_handle,krb5_context context,gss_buffer_set_t * data_set)308 export_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     ret = krb5_store_int32(sp, context_handle->lifetime);
339     if (ret) goto out;
340     krb5_auth_con_getlocalseqnumber (context,
341 				     context_handle->auth_context,
342 				     &number);
343     ret = krb5_store_uint32(sp, (uint32_t)0); /* store top half as zero */
344     if (ret) goto out;
345     ret = krb5_store_uint32(sp, (uint32_t)number);
346     if (ret) goto out;
347     krb5_auth_con_getremoteseqnumber (context,
348 				      context_handle->auth_context,
349 				      &number);
350     ret = krb5_store_uint32(sp, (uint32_t)0); /* store top half as zero */
351     if (ret) goto out;
352     ret = krb5_store_uint32(sp, (uint32_t)number);
353     if (ret) goto out;
354     ret = krb5_store_int32(sp, (is_cfx) ? 1 : 0);
355     if (ret) goto out;
356 
357     ret = _gsskrb5i_get_token_key(context_handle, context, &key);
358     if (ret) goto out;
359 
360     if (is_cfx == 0) {
361 	int sign_alg, seal_alg;
362 
363 	switch (key->keytype) {
364 	case ETYPE_DES_CBC_CRC:
365 	case ETYPE_DES_CBC_MD4:
366 	case ETYPE_DES_CBC_MD5:
367 	    sign_alg = 0;
368 	    seal_alg = 0;
369 	    break;
370 	case ETYPE_DES3_CBC_MD5:
371 	case ETYPE_DES3_CBC_SHA1:
372 	    sign_alg = 4;
373 	    seal_alg = 2;
374 	    break;
375 	case ETYPE_ARCFOUR_HMAC_MD5:
376 	case ETYPE_ARCFOUR_HMAC_MD5_56:
377 	    sign_alg = 17;
378 	    seal_alg = 16;
379 	    break;
380 	default:
381 	    sign_alg = -1;
382 	    seal_alg = -1;
383 	    break;
384 	}
385 	ret = krb5_store_int32(sp, sign_alg);
386 	if (ret) goto out;
387 	ret = krb5_store_int32(sp, seal_alg);
388 	if (ret) goto out;
389 	/* ctx_key */
390 	ret = krb5_store_keyblock(sp, *key);
391 	if (ret) goto out;
392     } else {
393 	int subkey_p = (context_handle->more_flags & ACCEPTOR_SUBKEY) ? 1 : 0;
394 
395 	/* have_acceptor_subkey */
396 	ret = krb5_store_int32(sp, subkey_p);
397 	if (ret) goto out;
398 	/* ctx_key */
399 	ret = krb5_store_keyblock(sp, *key);
400 	if (ret) goto out;
401 	/* acceptor_subkey */
402 	if (subkey_p) {
403 	    ret = krb5_store_keyblock(sp, *key);
404 	    if (ret) goto out;
405 	}
406     }
407     ret = krb5_storage_to_data(sp, &data);
408     if (ret) goto out;
409 
410     {
411 	gss_buffer_desc ad_data;
412 
413 	ad_data.value = data.data;
414 	ad_data.length = data.length;
415 
416 	ret = gss_add_buffer_set_member(minor_status, &ad_data, data_set);
417 	krb5_data_free(&data);
418 	if (ret)
419 	    goto out;
420     }
421 
422 out:
423     if (key)
424 	krb5_free_keyblock (context, key);
425     if (sp)
426 	krb5_storage_free(sp);
427     if (ret) {
428 	*minor_status = ret;
429 	major_status = GSS_S_FAILURE;
430     }
431     HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
432     return major_status;
433 }
434 
435 static OM_uint32
get_authtime(OM_uint32 * minor_status,gsskrb5_ctx ctx,gss_buffer_set_t * data_set)436 get_authtime(OM_uint32 *minor_status,
437 	     gsskrb5_ctx ctx,
438 	     gss_buffer_set_t *data_set)
439 
440 {
441     gss_buffer_desc value;
442     unsigned char buf[4];
443     OM_uint32 authtime;
444 
445     HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
446     if (ctx->ticket == NULL) {
447 	HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
448 	_gsskrb5_set_status(EINVAL, "No ticket to obtain auth time from");
449 	*minor_status = EINVAL;
450 	return GSS_S_FAILURE;
451     }
452 
453     authtime = ctx->ticket->ticket.authtime;
454 
455     HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
456 
457     _gsskrb5_encode_om_uint32(authtime, buf);
458     value.length = sizeof(buf);
459     value.value = buf;
460 
461     return gss_add_buffer_set_member(minor_status,
462 				     &value,
463 				     data_set);
464 }
465 
466 
467 static OM_uint32
get_service_keyblock(OM_uint32 * minor_status,gsskrb5_ctx ctx,gss_buffer_set_t * data_set)468 get_service_keyblock
469         (OM_uint32 *minor_status,
470 	 gsskrb5_ctx ctx,
471 	 gss_buffer_set_t *data_set)
472 {
473     krb5_storage *sp = NULL;
474     krb5_data data;
475     OM_uint32 maj_stat = GSS_S_COMPLETE;
476     krb5_error_code ret = EINVAL;
477 
478     sp = krb5_storage_emem();
479     if (sp == NULL) {
480 	_gsskrb5_clear_status();
481 	*minor_status = ENOMEM;
482 	return GSS_S_FAILURE;
483     }
484 
485     HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
486     if (ctx->service_keyblock == NULL) {
487 	HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
488 	krb5_storage_free(sp);
489 	_gsskrb5_set_status(EINVAL, "No service keyblock on gssapi context");
490 	*minor_status = EINVAL;
491 	return GSS_S_FAILURE;
492     }
493 
494     krb5_data_zero(&data);
495 
496     ret = krb5_store_keyblock(sp, *ctx->service_keyblock);
497 
498     HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
499 
500     if (ret)
501 	goto out;
502 
503     ret = krb5_storage_to_data(sp, &data);
504     if (ret)
505 	goto out;
506 
507     {
508 	gss_buffer_desc value;
509 
510 	value.length = data.length;
511 	value.value = data.data;
512 
513 	maj_stat = gss_add_buffer_set_member(minor_status,
514 					     &value,
515 					     data_set);
516     }
517 
518 out:
519     krb5_data_free(&data);
520     if (sp)
521 	krb5_storage_free(sp);
522     if (ret) {
523 	*minor_status = ret;
524 	maj_stat = GSS_S_FAILURE;
525     }
526     return maj_stat;
527 }
528 /*
529  *
530  */
531 
_gsskrb5_inquire_sec_context_by_oid(OM_uint32 * minor_status,const gss_ctx_id_t context_handle,const gss_OID desired_object,gss_buffer_set_t * data_set)532 OM_uint32 GSSAPI_CALLCONV _gsskrb5_inquire_sec_context_by_oid
533            (OM_uint32 *minor_status,
534             const gss_ctx_id_t context_handle,
535             const gss_OID desired_object,
536             gss_buffer_set_t *data_set)
537 {
538     krb5_context context;
539     const gsskrb5_ctx ctx = (const gsskrb5_ctx) context_handle;
540     unsigned suffix;
541 
542     if (ctx == NULL) {
543 	*minor_status = EINVAL;
544 	return GSS_S_NO_CONTEXT;
545     }
546 
547     GSSAPI_KRB5_INIT (&context);
548 
549     if (gss_oid_equal(desired_object, GSS_KRB5_GET_TKT_FLAGS_X)) {
550 	return inquire_sec_context_tkt_flags(minor_status,
551 					     ctx,
552 					     data_set);
553     } else if (gss_oid_equal(desired_object, GSS_C_PEER_HAS_UPDATED_SPNEGO)) {
554 	return inquire_sec_context_has_updated_spnego(minor_status,
555 						      ctx,
556 						      data_set);
557     } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_SUBKEY_X)) {
558 	return inquire_sec_context_get_subkey(minor_status,
559 					      ctx,
560 					      context,
561 					      TOKEN_KEY,
562 					      data_set);
563     } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_INITIATOR_SUBKEY_X)) {
564 	return inquire_sec_context_get_subkey(minor_status,
565 					      ctx,
566 					      context,
567 					      INITIATOR_KEY,
568 					      data_set);
569     } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_ACCEPTOR_SUBKEY_X)) {
570 	return inquire_sec_context_get_subkey(minor_status,
571 					      ctx,
572 					      context,
573 					      ACCEPTOR_KEY,
574 					      data_set);
575     } else if (gss_oid_equal(desired_object, GSS_C_INQ_SSPI_SESSION_KEY)) {
576         return inquire_sec_context_get_sspi_session_key(minor_status,
577                                                         ctx,
578                                                         context,
579                                                         data_set);
580     } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_AUTHTIME_X)) {
581 	return get_authtime(minor_status, ctx, data_set);
582     } else if (oid_prefix_equal(desired_object,
583 				GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X,
584 				&suffix)) {
585 	return inquire_sec_context_authz_data(minor_status,
586 					      ctx,
587 					      context,
588 					      suffix,
589 					      data_set);
590     } else if (oid_prefix_equal(desired_object,
591 				GSS_KRB5_EXPORT_LUCID_CONTEXT_X,
592 				&suffix)) {
593 	if (suffix == 1)
594 	    return export_lucid_sec_context_v1(minor_status,
595 					       ctx,
596 					       context,
597 					       data_set);
598 	*minor_status = 0;
599 	return GSS_S_FAILURE;
600     } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_SERVICE_KEYBLOCK_X)) {
601 	return get_service_keyblock(minor_status, ctx, data_set);
602     } else {
603 	*minor_status = 0;
604 	return GSS_S_FAILURE;
605     }
606 }
607 
608