Merge branch 'v4-0-test' of ssh://git.samba.org/data/git/samba into v4-0-test
[kai/samba.git] / source4 / 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 "krb5/gsskrb5_locl.h"
34
35 RCSID("$Id$");
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(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
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
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(EINVAL, "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     if (ret) goto out;
305     ret = krb5_store_uint32(sp, (uint32_t)number);
306     if (ret) goto out;
307     krb5_auth_getremoteseqnumber (context,
308                                   context_handle->auth_context,
309                                   &number);
310     ret = krb5_store_uint32(sp, (uint32_t)0); /* store top half as zero */
311     if (ret) goto out;
312     ret = krb5_store_uint32(sp, (uint32_t)number);
313     if (ret) goto out;
314     ret = krb5_store_int32(sp, (is_cfx) ? 1 : 0);
315     if (ret) goto out;
316
317     ret = _gsskrb5i_get_token_key(context_handle, context, &key);
318     if (ret) goto out;
319
320     if (is_cfx == 0) {
321         int sign_alg, seal_alg;
322
323         switch (key->keytype) {
324         case ETYPE_DES_CBC_CRC:
325         case ETYPE_DES_CBC_MD4:
326         case ETYPE_DES_CBC_MD5:
327             sign_alg = 0;
328             seal_alg = 0;
329             break;
330         case ETYPE_DES3_CBC_MD5:
331         case ETYPE_DES3_CBC_SHA1:
332             sign_alg = 4;
333             seal_alg = 2;
334             break;
335         case ETYPE_ARCFOUR_HMAC_MD5:
336         case ETYPE_ARCFOUR_HMAC_MD5_56:
337             sign_alg = 17;
338             seal_alg = 16;
339             break;
340         default:
341             sign_alg = -1;
342             seal_alg = -1;
343             break;
344         }
345         ret = krb5_store_int32(sp, sign_alg);
346         if (ret) goto out;
347         ret = krb5_store_int32(sp, seal_alg);
348         if (ret) goto out;
349         /* ctx_key */
350         ret = krb5_store_keyblock(sp, *key);
351         if (ret) goto out;
352     } else {
353         int subkey_p = (context_handle->more_flags & ACCEPTOR_SUBKEY) ? 1 : 0;
354
355         /* have_acceptor_subkey */
356         ret = krb5_store_int32(sp, subkey_p);
357         if (ret) goto out;
358         /* ctx_key */
359         ret = krb5_store_keyblock(sp, *key);
360         if (ret) goto out;
361         /* acceptor_subkey */
362         if (subkey_p) {
363             ret = krb5_store_keyblock(sp, *key);
364             if (ret) goto out;
365         }
366     }
367     ret = krb5_storage_to_data(sp, &data);
368     if (ret) goto out;
369
370     {
371         gss_buffer_desc ad_data;
372
373         ad_data.value = data.data;
374         ad_data.length = data.length;
375
376         ret = gss_add_buffer_set_member(minor_status, &ad_data, data_set);
377         krb5_data_free(&data);
378         if (ret)
379             goto out;
380     }
381
382 out:
383     if (key)
384         krb5_free_keyblock (context, key);
385     if (sp)
386         krb5_storage_free(sp);
387     if (ret) {
388         *minor_status = ret;
389         major_status = GSS_S_FAILURE;
390     }
391     HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
392     return major_status;
393 }
394
395 static OM_uint32
396 get_authtime(OM_uint32 *minor_status,
397              gsskrb5_ctx ctx, 
398              gss_buffer_set_t *data_set)
399
400 {
401     gss_buffer_desc value;
402     unsigned char buf[4];
403     OM_uint32 authtime;
404
405     HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
406     if (ctx->ticket == NULL) {
407         HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
408         _gsskrb5_set_status(EINVAL, "No ticket to obtain auth time from");
409         *minor_status = EINVAL;
410         return GSS_S_FAILURE;
411     }
412     
413     authtime = ctx->ticket->ticket.authtime;
414     
415     HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
416
417     _gsskrb5_encode_om_uint32(authtime, buf);
418     value.length = sizeof(buf);
419     value.value = buf;
420
421     return gss_add_buffer_set_member(minor_status,
422                                      &value,
423                                      data_set);
424 }
425
426
427 static OM_uint32 
428 get_service_keyblock
429         (OM_uint32 *minor_status,
430          gsskrb5_ctx ctx, 
431          gss_buffer_set_t *data_set)
432 {
433     krb5_storage *sp = NULL;
434     krb5_data data;
435     OM_uint32 maj_stat = GSS_S_COMPLETE;
436     krb5_error_code ret = EINVAL;
437     
438     sp = krb5_storage_emem();
439     if (sp == NULL) {
440         _gsskrb5_clear_status();
441         *minor_status = ENOMEM;
442         return GSS_S_FAILURE;
443     }
444
445     HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
446     if (ctx->service_keyblock == NULL) {
447         HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
448         _gsskrb5_set_status(EINVAL, "No service keyblock on gssapi context");
449         *minor_status = EINVAL;
450         return GSS_S_FAILURE; 
451     }
452
453     krb5_data_zero(&data);
454
455     ret = krb5_store_keyblock(sp, *ctx->service_keyblock);
456
457     HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
458
459     if (ret)
460         goto out;
461
462     ret = krb5_storage_to_data(sp, &data);
463     if (ret)
464         goto out;
465
466     {
467         gss_buffer_desc value;
468         
469         value.length = data.length;
470         value.value = data.data;
471         
472         maj_stat = gss_add_buffer_set_member(minor_status,
473                                              &value,
474                                              data_set);
475     }
476
477 out:
478     krb5_data_free(&data);
479     if (sp)
480         krb5_storage_free(sp);
481     if (ret) {
482         *minor_status = ret;
483         maj_stat = GSS_S_FAILURE;
484     }
485     return maj_stat;
486 }
487 /*
488  *
489  */
490
491 OM_uint32 _gsskrb5_inquire_sec_context_by_oid
492            (OM_uint32 *minor_status,
493             const gss_ctx_id_t context_handle,
494             const gss_OID desired_object,
495             gss_buffer_set_t *data_set)
496 {
497     krb5_context context;
498     const gsskrb5_ctx ctx = (const gsskrb5_ctx) context_handle;
499     unsigned suffix;
500
501     if (ctx == NULL) {
502         *minor_status = EINVAL;
503         return GSS_S_NO_CONTEXT;
504     }
505
506     GSSAPI_KRB5_INIT (&context);
507
508     if (gss_oid_equal(desired_object, GSS_KRB5_GET_TKT_FLAGS_X)) {
509         return inquire_sec_context_tkt_flags(minor_status,
510                                              ctx,
511                                              data_set);
512     } else if (gss_oid_equal(desired_object, GSS_C_PEER_HAS_UPDATED_SPNEGO)) {
513         return inquire_sec_context_has_updated_spnego(minor_status,
514                                                       ctx,
515                                                       data_set);
516     } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_SUBKEY_X)) {
517         return inquire_sec_context_get_subkey(minor_status,
518                                               ctx,
519                                               context,
520                                               TOKEN_KEY,
521                                               data_set);
522     } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_INITIATOR_SUBKEY_X)) {
523         return inquire_sec_context_get_subkey(minor_status,
524                                               ctx,
525                                               context,
526                                               INITIATOR_KEY,
527                                               data_set);
528     } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_ACCEPTOR_SUBKEY_X)) {
529         return inquire_sec_context_get_subkey(minor_status,
530                                               ctx,
531                                               context,
532                                               ACCEPTOR_KEY,
533                                               data_set);
534     } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_AUTHTIME_X)) {
535         return get_authtime(minor_status, ctx, data_set);
536     } else if (oid_prefix_equal(desired_object,
537                                 GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X,
538                                 &suffix)) {
539         return inquire_sec_context_authz_data(minor_status,
540                                               ctx,
541                                               context,
542                                               suffix,
543                                               data_set);
544     } else if (oid_prefix_equal(desired_object,
545                                 GSS_KRB5_EXPORT_LUCID_CONTEXT_X,
546                                 &suffix)) {
547         if (suffix == 1)
548             return export_lucid_sec_context_v1(minor_status,
549                                                ctx,
550                                                context,
551                                                data_set);
552         *minor_status = 0;
553         return GSS_S_FAILURE;
554     } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_SERVICE_KEYBLOCK_X)) {
555         return get_service_keyblock(minor_status, ctx, data_set);
556     } else {
557         *minor_status = 0;
558         return GSS_S_FAILURE;
559     }
560 }
561