5ca7536e6a39a38403bed545040251f1b82b3cc8
[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: 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