xref: /netbsd-src/crypto/external/bsd/heimdal/dist/lib/gssapi/krb5/inquire_sec_context_by_oid.c (revision d3273b5b76f5afaafe308cead5511dbb8df8c5e9)
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 
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     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 
172 out:
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 
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     /* 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 
423 out:
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 
436 static OM_uint32
get_authtime(OM_uint32 * minor_status,gsskrb5_ctx ctx,gss_buffer_set_t * data_set)437 get_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 
468 static OM_uint32
get_service_keyblock(OM_uint32 * minor_status,gsskrb5_ctx ctx,gss_buffer_set_t * data_set)469 get_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 
519 out:
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 
_gsskrb5_inquire_sec_context_by_oid(OM_uint32 * minor_status,gss_const_ctx_id_t context_handle,const gss_OID desired_object,gss_buffer_set_t * data_set)533 OM_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