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 "krb5/gsskrb5_locl.h"
34 
35 RCSID("$Id: inquire_sec_context_by_oid.c 19031 2006-11-13 18:02:57Z lha $");
36 
37 static int
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 
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("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 
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("%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("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 
184 static OM_uint32 inquire_sec_context_authz_data
185            (OM_uint32 *minor_status,
186             const gsskrb5_ctx context_handle,
187 	    krb5_context context,
188             unsigned ad_type,
189             gss_buffer_set_t *data_set)
190 {
191     krb5_data data;
192     gss_buffer_desc ad_data;
193     OM_uint32 ret;
194 
195     *minor_status = 0;
196     *data_set = GSS_C_NO_BUFFER_SET;
197 
198     HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
199     if (context_handle->ticket == NULL) {
200 	HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
201 	*minor_status = EINVAL;
202 	_gsskrb5_set_status("No ticket to obtain authz data from");
203 	return GSS_S_NO_CONTEXT;
204     }
205 
206     ret = krb5_ticket_get_authorization_data_type(context,
207 						  context_handle->ticket,
208 						  ad_type,
209 						  &data);
210     HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
211     if (ret) {
212 	*minor_status = ret;
213 	return GSS_S_FAILURE;
214     }
215 
216     ad_data.value = data.data;
217     ad_data.length = data.length;
218 
219     ret = gss_add_buffer_set_member(minor_status,
220 				    &ad_data,
221 				    data_set);
222 
223     krb5_data_free(&data);
224 
225     return ret;
226 }
227 
228 static OM_uint32 inquire_sec_context_has_updated_spnego
229            (OM_uint32 *minor_status,
230             const gsskrb5_ctx context_handle,
231             gss_buffer_set_t *data_set)
232 {
233     int is_updated = 0;
234 
235     *minor_status = 0;
236     *data_set = GSS_C_NO_BUFFER_SET;
237 
238     /*
239      * For Windows SPNEGO implementations, both the initiator and the
240      * acceptor are assumed to have been updated if a "newer" [CLAR] or
241      * different enctype is negotiated for use by the Kerberos GSS-API
242      * mechanism.
243      */
244     HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
245     _gsskrb5i_is_cfx(context_handle, &is_updated);
246     if (is_updated == 0) {
247 	krb5_keyblock *acceptor_subkey;
248 
249 	if (context_handle->more_flags & LOCAL)
250 	    acceptor_subkey = context_handle->auth_context->remote_subkey;
251 	else
252 	    acceptor_subkey = context_handle->auth_context->local_subkey;
253 
254 	if (acceptor_subkey != NULL)
255 	    is_updated = (acceptor_subkey->keytype !=
256 			  context_handle->auth_context->keyblock->keytype);
257     }
258     HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
259 
260     return is_updated ? GSS_S_COMPLETE : GSS_S_FAILURE;
261 }
262 
263 /*
264  *
265  */
266 
267 static OM_uint32
268 export_lucid_sec_context_v1(OM_uint32 *minor_status,
269 			    gsskrb5_ctx context_handle,
270 			    krb5_context context,
271 			    gss_buffer_set_t *data_set)
272 {
273     krb5_storage *sp = NULL;
274     OM_uint32 major_status = GSS_S_COMPLETE;
275     krb5_error_code ret;
276     krb5_keyblock *key = NULL;
277     int32_t number;
278     int is_cfx;
279     krb5_data data;
280 
281     *minor_status = 0;
282 
283     HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
284 
285     _gsskrb5i_is_cfx(context_handle, &is_cfx);
286 
287     sp = krb5_storage_emem();
288     if (sp == NULL) {
289 	_gsskrb5_clear_status();
290 	ret = ENOMEM;
291 	goto out;
292     }
293 
294     ret = krb5_store_int32(sp, 1);
295     if (ret) goto out;
296     ret = krb5_store_int32(sp, (context_handle->more_flags & LOCAL) ? 1 : 0);
297     if (ret) goto out;
298     ret = krb5_store_int32(sp, context_handle->lifetime);
299     if (ret) goto out;
300     krb5_auth_con_getlocalseqnumber (context,
301 				     context_handle->auth_context,
302 				     &number);
303     ret = krb5_store_uint32(sp, (uint32_t)0); /* store top half as zero */
304     ret = krb5_store_uint32(sp, (uint32_t)number);
305     krb5_auth_getremoteseqnumber (context,
306 				  context_handle->auth_context,
307 				  &number);
308     ret = krb5_store_uint32(sp, (uint32_t)0); /* store top half as zero */
309     ret = krb5_store_uint32(sp, (uint32_t)number);
310     ret = krb5_store_int32(sp, (is_cfx) ? 1 : 0);
311     if (ret) goto out;
312 
313     ret = _gsskrb5i_get_token_key(context_handle, context, &key);
314     if (ret) goto out;
315 
316     if (is_cfx == 0) {
317 	int sign_alg, seal_alg;
318 
319 	switch (key->keytype) {
320 	case ETYPE_DES_CBC_CRC:
321 	case ETYPE_DES_CBC_MD4:
322 	case ETYPE_DES_CBC_MD5:
323 	    sign_alg = 0;
324 	    seal_alg = 0;
325 	    break;
326 	case ETYPE_DES3_CBC_MD5:
327 	case ETYPE_DES3_CBC_SHA1:
328 	    sign_alg = 4;
329 	    seal_alg = 2;
330 	    break;
331 	case ETYPE_ARCFOUR_HMAC_MD5:
332 	case ETYPE_ARCFOUR_HMAC_MD5_56:
333 	    sign_alg = 17;
334 	    seal_alg = 16;
335 	    break;
336 	default:
337 	    sign_alg = -1;
338 	    seal_alg = -1;
339 	    break;
340 	}
341 	ret = krb5_store_int32(sp, sign_alg);
342 	if (ret) goto out;
343 	ret = krb5_store_int32(sp, seal_alg);
344 	if (ret) goto out;
345 	/* ctx_key */
346 	ret = krb5_store_keyblock(sp, *key);
347 	if (ret) goto out;
348     } else {
349 	int subkey_p = (context_handle->more_flags & ACCEPTOR_SUBKEY) ? 1 : 0;
350 
351 	/* have_acceptor_subkey */
352 	ret = krb5_store_int32(sp, subkey_p);
353 	if (ret) goto out;
354 	/* ctx_key */
355 	ret = krb5_store_keyblock(sp, *key);
356 	if (ret) goto out;
357 	/* acceptor_subkey */
358 	if (subkey_p) {
359 	    ret = krb5_store_keyblock(sp, *key);
360 	    if (ret) goto out;
361 	}
362     }
363     ret = krb5_storage_to_data(sp, &data);
364     if (ret) goto out;
365 
366     {
367 	gss_buffer_desc ad_data;
368 
369 	ad_data.value = data.data;
370 	ad_data.length = data.length;
371 
372 	ret = gss_add_buffer_set_member(minor_status, &ad_data, data_set);
373 	krb5_data_free(&data);
374 	if (ret)
375 	    goto out;
376     }
377 
378 out:
379     if (key)
380 	krb5_free_keyblock (context, key);
381     if (sp)
382 	krb5_storage_free(sp);
383     if (ret) {
384 	*minor_status = ret;
385 	major_status = GSS_S_FAILURE;
386     }
387     HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
388     return major_status;
389 }
390 
391 static OM_uint32
392 get_authtime(OM_uint32 *minor_status,
393 	     gsskrb5_ctx ctx,
394 	     gss_buffer_set_t *data_set)
395 
396 {
397     gss_buffer_desc value;
398     unsigned char buf[4];
399     OM_uint32 authtime;
400 
401     HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
402     if (ctx->ticket == NULL) {
403 	HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
404 	_gsskrb5_set_status("No ticket to obtain auth time from");
405 	*minor_status = EINVAL;
406 	return GSS_S_FAILURE;
407     }
408 
409     authtime = ctx->ticket->ticket.authtime;
410 
411     HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
412 
413     _gsskrb5_encode_om_uint32(authtime, buf);
414     value.length = sizeof(buf);
415     value.value = buf;
416 
417     return gss_add_buffer_set_member(minor_status,
418 				     &value,
419 				     data_set);
420 }
421 
422 
423 static OM_uint32
424 get_service_keyblock
425         (OM_uint32 *minor_status,
426 	 gsskrb5_ctx ctx,
427 	 gss_buffer_set_t *data_set)
428 {
429     krb5_storage *sp = NULL;
430     krb5_data data;
431     OM_uint32 maj_stat = GSS_S_COMPLETE;
432     krb5_error_code ret = EINVAL;
433 
434     sp = krb5_storage_emem();
435     if (sp == NULL) {
436 	_gsskrb5_clear_status();
437 	*minor_status = ENOMEM;
438 	return GSS_S_FAILURE;
439     }
440 
441     HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
442     if (ctx->service_keyblock == NULL) {
443 	HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
444 	_gsskrb5_set_status("No service keyblock on gssapi context");
445 	*minor_status = EINVAL;
446 	return GSS_S_FAILURE;
447     }
448 
449     krb5_data_zero(&data);
450 
451     ret = krb5_store_keyblock(sp, *ctx->service_keyblock);
452 
453     HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
454 
455     if (ret)
456 	goto out;
457 
458     ret = krb5_storage_to_data(sp, &data);
459     if (ret)
460 	goto out;
461 
462     {
463 	gss_buffer_desc value;
464 
465 	value.length = data.length;
466 	value.value = data.data;
467 
468 	maj_stat = gss_add_buffer_set_member(minor_status,
469 					     &value,
470 					     data_set);
471     }
472 
473 out:
474     krb5_data_free(&data);
475     if (sp)
476 	krb5_storage_free(sp);
477     if (ret) {
478 	*minor_status = ret;
479 	maj_stat = GSS_S_FAILURE;
480     }
481     return maj_stat;
482 }
483 /*
484  *
485  */
486 
487 OM_uint32 _gsskrb5_inquire_sec_context_by_oid
488            (OM_uint32 *minor_status,
489             const gss_ctx_id_t context_handle,
490             const gss_OID desired_object,
491             gss_buffer_set_t *data_set)
492 {
493     krb5_context context;
494     const gsskrb5_ctx ctx = (const gsskrb5_ctx) context_handle;
495     unsigned suffix;
496 
497     if (ctx == NULL) {
498 	*minor_status = EINVAL;
499 	return GSS_S_NO_CONTEXT;
500     }
501 
502     GSSAPI_KRB5_INIT (&context);
503 
504     if (gss_oid_equal(desired_object, GSS_KRB5_GET_TKT_FLAGS_X)) {
505 	return inquire_sec_context_tkt_flags(minor_status,
506 					     ctx,
507 					     data_set);
508     } else if (gss_oid_equal(desired_object, GSS_C_PEER_HAS_UPDATED_SPNEGO)) {
509 	return inquire_sec_context_has_updated_spnego(minor_status,
510 						      ctx,
511 						      data_set);
512     } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_SUBKEY_X)) {
513 	return inquire_sec_context_get_subkey(minor_status,
514 					      ctx,
515 					      context,
516 					      TOKEN_KEY,
517 					      data_set);
518     } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_INITIATOR_SUBKEY_X)) {
519 	return inquire_sec_context_get_subkey(minor_status,
520 					      ctx,
521 					      context,
522 					      INITIATOR_KEY,
523 					      data_set);
524     } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_ACCEPTOR_SUBKEY_X)) {
525 	return inquire_sec_context_get_subkey(minor_status,
526 					      ctx,
527 					      context,
528 					      ACCEPTOR_KEY,
529 					      data_set);
530     } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_AUTHTIME_X)) {
531 	return get_authtime(minor_status, ctx, data_set);
532     } else if (oid_prefix_equal(desired_object,
533 				GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X,
534 				&suffix)) {
535 	return inquire_sec_context_authz_data(minor_status,
536 					      ctx,
537 					      context,
538 					      suffix,
539 					      data_set);
540     } else if (oid_prefix_equal(desired_object,
541 				GSS_KRB5_EXPORT_LUCID_CONTEXT_X,
542 				&suffix)) {
543 	if (suffix == 1)
544 	    return export_lucid_sec_context_v1(minor_status,
545 					       ctx,
546 					       context,
547 					       data_set);
548 	*minor_status = 0;
549 	return GSS_S_FAILURE;
550     } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_SERVICE_KEYBLOCK_X)) {
551 	return get_service_keyblock(minor_status, ctx, data_set);
552     } else {
553 	*minor_status = 0;
554 	return GSS_S_FAILURE;
555     }
556 }
557 
558