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