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