r19633: Merge to lorikeet-heimdal, removing krb5_rd_req_return_keyblock in favour...
[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,v 1.11 2006/11/07 14:34:35 lha Exp $");
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             enum keytype keytype,
110             gss_buffer_set_t *data_set)
111 {
112     krb5_keyblock *key = NULL;
113     krb5_storage *sp = NULL;
114     krb5_data data;
115     OM_uint32 maj_stat = GSS_S_COMPLETE;
116     krb5_error_code ret;
117
118     krb5_data_zero(&data);
119
120     sp = krb5_storage_emem();
121     if (sp == NULL) {
122         _gsskrb5_clear_status();
123         ret = ENOMEM;
124         goto out;
125     }
126
127     HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
128     switch(keytype) {
129     case ACCEPTOR_KEY:
130         ret = _gsskrb5i_get_acceptor_subkey(context_handle, &key);
131         if (ret)
132             _gsskrb5_set_error_string ();
133         break;
134     case INITIATOR_KEY:
135         ret = _gsskrb5i_get_initiator_subkey(context_handle, &key);
136         if (ret)
137             _gsskrb5_set_error_string ();
138         break;
139     case TOKEN_KEY:
140         ret = _gsskrb5i_get_token_key(context_handle, &key);
141         if (ret)
142             _gsskrb5_set_error_string ();
143         break;
144     default:
145         _gsskrb5_set_status("%d is not a valid subkey type", keytype);
146         ret = EINVAL;
147         break;
148    }
149     HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
150     if (ret) 
151         goto out;
152     if (key == NULL) {
153         _gsskrb5_set_status("have no subkey of type %d", keytype);
154         ret = EINVAL;
155         goto out;
156     }
157
158     ret = krb5_store_keyblock(sp, *key);
159     krb5_free_keyblock (_gsskrb5_context, key);
160     if (ret) {
161         _gsskrb5_set_error_string ();
162         goto out;
163     }
164
165     ret = krb5_storage_to_data(sp, &data);
166     if (ret) {
167         _gsskrb5_set_error_string ();
168         goto out;
169     }
170
171     {
172         gss_buffer_desc value;
173         
174         value.length = data.length;
175         value.value = data.data;
176         
177         maj_stat = gss_add_buffer_set_member(minor_status,
178                                              &value,
179                                              data_set);
180     }
181
182 out:
183     krb5_data_free(&data);
184     if (sp)
185         krb5_storage_free(sp);
186     if (ret) {
187         *minor_status = ret;
188         maj_stat = GSS_S_FAILURE;
189     }
190     return maj_stat;
191 }
192
193 static OM_uint32 inquire_sec_context_authz_data
194            (OM_uint32 *minor_status,
195             const gsskrb5_ctx context_handle,
196             unsigned ad_type,
197             gss_buffer_set_t *data_set)
198 {
199     krb5_data data;
200     gss_buffer_desc ad_data;
201     OM_uint32 ret;
202
203     *minor_status = 0;
204     *data_set = GSS_C_NO_BUFFER_SET;
205
206     HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
207     if (context_handle->ticket == NULL) {
208         HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
209         *minor_status = EINVAL;
210         _gsskrb5_set_status("No ticket to obtain authz data from");
211         return GSS_S_NO_CONTEXT;
212     }
213
214     ret = krb5_ticket_get_authorization_data_type(_gsskrb5_context,
215                                                   context_handle->ticket,
216                                                   ad_type,
217                                                   &data);
218     HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
219     if (ret) {
220         _gsskrb5_set_error_string ();
221         *minor_status = ret;
222         return GSS_S_FAILURE;
223     }
224
225     ad_data.value = data.data;
226     ad_data.length = data.length;
227
228     ret = gss_add_buffer_set_member(minor_status,
229                                     &ad_data,
230                                     data_set);
231
232     krb5_data_free(&data);
233
234     return ret;
235 }
236
237 static OM_uint32 inquire_sec_context_has_updated_spnego
238            (OM_uint32 *minor_status,
239             const gsskrb5_ctx context_handle,
240             gss_buffer_set_t *data_set)
241 {
242     int is_updated = 0;
243
244     *minor_status = 0;
245     *data_set = GSS_C_NO_BUFFER_SET;
246
247     /*
248      * For Windows SPNEGO implementations, both the initiator and the
249      * acceptor are assumed to have been updated if a "newer" [CLAR] or
250      * different enctype is negotiated for use by the Kerberos GSS-API
251      * mechanism.
252      */
253     HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
254     _gsskrb5i_is_cfx(context_handle, &is_updated);
255     if (is_updated == 0) {
256         krb5_keyblock *acceptor_subkey;
257
258         if (context_handle->more_flags & LOCAL)
259             acceptor_subkey = context_handle->auth_context->remote_subkey;
260         else
261             acceptor_subkey = context_handle->auth_context->local_subkey;
262
263         if (acceptor_subkey != NULL)
264             is_updated = (acceptor_subkey->keytype !=
265                           context_handle->auth_context->keyblock->keytype);
266     }
267     HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
268
269     return is_updated ? GSS_S_COMPLETE : GSS_S_FAILURE;
270 }
271
272 /*
273  *
274  */
275
276 static OM_uint32
277 export_lucid_sec_context_v1(OM_uint32 *minor_status,
278                             gsskrb5_ctx context_handle,
279                             gss_buffer_set_t *data_set)
280 {
281     krb5_storage *sp = NULL;
282     OM_uint32 major_status = GSS_S_COMPLETE;
283     krb5_error_code ret;
284     krb5_keyblock *key = NULL;
285     int32_t number;
286     int is_cfx;
287     krb5_data data;
288     
289     *minor_status = 0;
290
291     GSSAPI_KRB5_INIT ();
292
293     HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex);
294
295     _gsskrb5i_is_cfx(context_handle, &is_cfx);
296
297     sp = krb5_storage_emem();
298     if (sp == NULL) {
299         _gsskrb5_clear_status();
300         ret = ENOMEM;
301         goto out;
302     }
303
304     ret = krb5_store_int32(sp, 1);
305     if (ret) goto out;
306     ret = krb5_store_int32(sp, (context_handle->more_flags & LOCAL) ? 1 : 0);
307     if (ret) goto out;
308     ret = krb5_store_int32(sp, context_handle->lifetime);
309     if (ret) goto out;
310     krb5_auth_con_getlocalseqnumber (_gsskrb5_context,
311                                      context_handle->auth_context,
312                                      &number);
313     ret = krb5_store_uint32(sp, (uint32_t)0); /* store top half as zero */
314     ret = krb5_store_uint32(sp, (uint32_t)number);
315     krb5_auth_getremoteseqnumber (_gsskrb5_context,
316                                   context_handle->auth_context,
317                                   &number);
318     ret = krb5_store_uint32(sp, (uint32_t)0); /* store top half as zero */
319     ret = krb5_store_uint32(sp, (uint32_t)number);
320     ret = krb5_store_int32(sp, (is_cfx) ? 1 : 0);
321     if (ret) goto out;
322
323     ret = _gsskrb5i_get_token_key(context_handle, &key);
324     if (ret) goto out;
325
326     if (is_cfx == 0) {
327         int sign_alg, seal_alg;
328
329         switch (key->keytype) {
330         case ETYPE_DES_CBC_CRC:
331         case ETYPE_DES_CBC_MD4:
332         case ETYPE_DES_CBC_MD5:
333             sign_alg = 0;
334             seal_alg = 0;
335             break;
336         case ETYPE_DES3_CBC_MD5:
337         case ETYPE_DES3_CBC_SHA1:
338             sign_alg = 4;
339             seal_alg = 2;
340             break;
341         case ETYPE_ARCFOUR_HMAC_MD5:
342         case ETYPE_ARCFOUR_HMAC_MD5_56:
343             sign_alg = 17;
344             seal_alg = 16;
345             break;
346         default:
347             sign_alg = -1;
348             seal_alg = -1;
349             break;
350         }
351         ret = krb5_store_int32(sp, sign_alg);
352         if (ret) goto out;
353         ret = krb5_store_int32(sp, seal_alg);
354         if (ret) goto out;
355         /* ctx_key */
356         ret = krb5_store_keyblock(sp, *key);
357         if (ret) goto out;
358     } else {
359         int subkey_p = (context_handle->more_flags & ACCEPTOR_SUBKEY) ? 1 : 0;
360
361         /* have_acceptor_subkey */
362         ret = krb5_store_int32(sp, subkey_p);
363         if (ret) goto out;
364         /* ctx_key */
365         ret = krb5_store_keyblock(sp, *key);
366         if (ret) goto out;
367         /* acceptor_subkey */
368         if (subkey_p) {
369             ret = krb5_store_keyblock(sp, *key);
370             if (ret) goto out;
371         }
372     }
373     ret = krb5_storage_to_data(sp, &data);
374     if (ret) goto out;
375
376     {
377         gss_buffer_desc ad_data;
378
379         ad_data.value = data.data;
380         ad_data.length = data.length;
381
382         ret = gss_add_buffer_set_member(minor_status, &ad_data, data_set);
383         krb5_data_free(&data);
384         if (ret)
385             goto out;
386     }
387
388 out:
389     if (key)
390         krb5_free_keyblock (_gsskrb5_context, key);
391     if (sp)
392         krb5_storage_free(sp);
393     if (ret) {
394         *minor_status = ret;
395         major_status = GSS_S_FAILURE;
396     }
397     HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex);
398     return major_status;
399 }
400
401 static OM_uint32
402 get_authtime(OM_uint32 *minor_status,
403              gsskrb5_ctx ctx, 
404              gss_buffer_set_t *data_set)
405
406 {
407     gss_buffer_desc value;
408     unsigned char buf[4];
409     OM_uint32 authtime;
410
411     HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
412     if (ctx->ticket == NULL) {
413         HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
414         _gsskrb5_set_status("No ticket to obtain auth time from");
415         *minor_status = EINVAL;
416         return GSS_S_FAILURE;
417     }
418     
419     authtime = ctx->ticket->ticket.authtime;
420     
421     HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
422
423     _gsskrb5_encode_om_uint32(authtime, buf);
424     value.length = sizeof(buf);
425     value.value = buf;
426
427     return gss_add_buffer_set_member(minor_status,
428                                      &value,
429                                      data_set);
430 }
431
432
433 static OM_uint32 
434 get_service_keyblock
435         (OM_uint32 *minor_status,
436          gsskrb5_ctx ctx, 
437          gss_buffer_set_t *data_set)
438 {
439     krb5_storage *sp = NULL;
440     krb5_data data;
441     OM_uint32 maj_stat = GSS_S_COMPLETE;
442     krb5_error_code ret = EINVAL;
443     
444     sp = krb5_storage_emem();
445     if (sp == NULL) {
446         _gsskrb5_clear_status();
447         *minor_status = ENOMEM;
448         return GSS_S_FAILURE;
449     }
450
451     HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
452     if (ctx->service_keyblock == NULL) {
453         HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
454         _gsskrb5_set_status("No service keyblock on gssapi context");
455         *minor_status = EINVAL;
456         return GSS_S_FAILURE; 
457     }
458
459     krb5_data_zero(&data);
460
461     ret = krb5_store_keyblock(sp, *ctx->service_keyblock);
462
463     HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
464
465     if (ret)
466         goto out;
467
468     ret = krb5_storage_to_data(sp, &data);
469     if (ret)
470         goto out;
471
472     {
473         gss_buffer_desc value;
474         
475         value.length = data.length;
476         value.value = data.data;
477         
478         maj_stat = gss_add_buffer_set_member(minor_status,
479                                              &value,
480                                              data_set);
481     }
482
483 out:
484     krb5_data_free(&data);
485     if (sp)
486         krb5_storage_free(sp);
487     if (ret) {
488         _gsskrb5_set_error_string ();
489         *minor_status = ret;
490         maj_stat = GSS_S_FAILURE;
491     }
492     return maj_stat;
493 }
494 /*
495  *
496  */
497
498 OM_uint32 _gsskrb5_inquire_sec_context_by_oid
499            (OM_uint32 *minor_status,
500             const gss_ctx_id_t context_handle,
501             const gss_OID desired_object,
502             gss_buffer_set_t *data_set)
503 {
504     const gsskrb5_ctx ctx = (const gsskrb5_ctx) context_handle;
505     unsigned suffix;
506
507     if (ctx == NULL) {
508         *minor_status = EINVAL;
509         return GSS_S_NO_CONTEXT;
510     }
511
512     if (gss_oid_equal(desired_object, GSS_KRB5_GET_TKT_FLAGS_X)) {
513         return inquire_sec_context_tkt_flags(minor_status,
514                                              ctx,
515                                              data_set);
516     } else if (gss_oid_equal(desired_object, GSS_C_PEER_HAS_UPDATED_SPNEGO)) {
517         return inquire_sec_context_has_updated_spnego(minor_status,
518                                                       ctx,
519                                                       data_set);
520     } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_SUBKEY_X)) {
521         return inquire_sec_context_get_subkey(minor_status,
522                                               ctx,
523                                               TOKEN_KEY,
524                                               data_set);
525     } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_INITIATOR_SUBKEY_X)) {
526         return inquire_sec_context_get_subkey(minor_status,
527                                               ctx,
528                                               INITIATOR_KEY,
529                                               data_set);
530     } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_ACCEPTOR_SUBKEY_X)) {
531         return inquire_sec_context_get_subkey(minor_status,
532                                               ctx,
533                                               ACCEPTOR_KEY,
534                                               data_set);
535     } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_AUTHTIME_X)) {
536         return get_authtime(minor_status, ctx, data_set);
537     } else if (oid_prefix_equal(desired_object,
538                                 GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X,
539                                 &suffix)) {
540         return inquire_sec_context_authz_data(minor_status,
541                                               ctx,
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                                                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