]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - crypto/heimdal/lib/gssapi/krb5/inquire_sec_context_by_oid.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / crypto / heimdal / lib / gssapi / krb5 / inquire_sec_context_by_oid.c
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