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