heimdal: import heimdal's trunk svn rev 23697 + lorikeet-heimdal patches
[metze/samba/wip.git] / source / heimdal / lib / gssapi / spnego / compat.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 "spnego/spnego_locl.h"
34
35 RCSID("$Id$");
36
37 /*
38  * Apparently Microsoft got the OID wrong, and used
39  * 1.2.840.48018.1.2.2 instead. We need both this and
40  * the correct Kerberos OID here in order to deal with
41  * this. Because this is manifest in SPNEGO only I'd
42  * prefer to deal with this here rather than inside the
43  * Kerberos mechanism.
44  */
45 gss_OID_desc _gss_spnego_mskrb_mechanism_oid_desc =
46         {9, (void *)"\x2a\x86\x48\x82\xf7\x12\x01\x02\x02"};
47
48 gss_OID_desc _gss_spnego_krb5_mechanism_oid_desc =
49         {9, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"};
50
51 /*
52  * Allocate a SPNEGO context handle
53  */
54 OM_uint32 _gss_spnego_alloc_sec_context (OM_uint32 * minor_status,
55                                          gss_ctx_id_t *context_handle)
56 {
57     gssspnego_ctx ctx;
58
59     ctx = calloc(1, sizeof(*ctx));
60     if (ctx == NULL) {
61         *minor_status = ENOMEM;
62         return GSS_S_FAILURE;
63     }
64
65     ctx->initiator_mech_types.len = 0;
66     ctx->initiator_mech_types.val = NULL;
67     ctx->preferred_mech_type = GSS_C_NO_OID;
68     ctx->negotiated_mech_type = GSS_C_NO_OID;
69     ctx->negotiated_ctx_id = GSS_C_NO_CONTEXT;
70
71     /*
72      * Cache these so we can return them before returning
73      * GSS_S_COMPLETE, even if the mechanism has itself
74      * completed earlier
75      */
76     ctx->mech_flags = 0;
77     ctx->mech_time_rec = 0;
78     ctx->mech_src_name = GSS_C_NO_NAME;
79
80     ctx->open = 0;
81     ctx->local = 0;
82     ctx->require_mic = 0;
83     ctx->verified_mic = 0;
84
85     HEIMDAL_MUTEX_init(&ctx->ctx_id_mutex);
86
87     *context_handle = (gss_ctx_id_t)ctx;
88
89     return GSS_S_COMPLETE;
90 }
91
92 /*
93  * Free a SPNEGO context handle. The caller must have acquired
94  * the lock before this is called.
95  */
96 OM_uint32 _gss_spnego_internal_delete_sec_context
97            (OM_uint32 *minor_status,
98             gss_ctx_id_t *context_handle,
99             gss_buffer_t output_token
100            )
101 {
102     gssspnego_ctx ctx;
103     OM_uint32 ret, minor;
104
105     *minor_status = 0;
106
107     if (context_handle == NULL) {
108         return GSS_S_NO_CONTEXT;
109     }
110
111     if (output_token != GSS_C_NO_BUFFER) {
112         output_token->length = 0;
113         output_token->value = NULL;
114     }
115
116     ctx = (gssspnego_ctx)*context_handle;
117     *context_handle = GSS_C_NO_CONTEXT;
118
119     if (ctx == NULL) {
120         return GSS_S_NO_CONTEXT;
121     }
122
123     if (ctx->initiator_mech_types.val != NULL)
124         free_MechTypeList(&ctx->initiator_mech_types);
125
126     gss_release_oid(&minor, &ctx->preferred_mech_type);
127     ctx->negotiated_mech_type = GSS_C_NO_OID;
128
129     gss_release_name(&minor, &ctx->target_name);
130     gss_release_name(&minor, &ctx->mech_src_name);
131
132     if (ctx->negotiated_ctx_id != GSS_C_NO_CONTEXT) {
133         ret = gss_delete_sec_context(minor_status,
134                                      &ctx->negotiated_ctx_id,
135                                      output_token);
136         ctx->negotiated_ctx_id = GSS_C_NO_CONTEXT;
137     } else {
138         ret = GSS_S_COMPLETE;
139     }
140
141     HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
142     HEIMDAL_MUTEX_destroy(&ctx->ctx_id_mutex);
143
144     free(ctx);
145     *context_handle = NULL;
146
147     return ret;
148 }
149
150 /*
151  * For compatability with the Windows SPNEGO implementation, the
152  * default is to ignore the mechListMIC unless CFX is used and
153  * a non-preferred mechanism was negotiated
154  */
155
156 OM_uint32
157 _gss_spnego_require_mechlist_mic(OM_uint32 *minor_status,
158                                  gssspnego_ctx ctx,
159                                  int *require_mic)
160 {
161     gss_buffer_set_t buffer_set = GSS_C_NO_BUFFER_SET;
162     OM_uint32 minor;
163
164     *minor_status = 0;
165     *require_mic = 0;
166
167     if (ctx == NULL) {
168         return GSS_S_COMPLETE;
169     }
170
171     if (ctx->require_mic) {
172         /* Acceptor requested it: mandatory to honour */
173         *require_mic = 1;
174         return GSS_S_COMPLETE;
175     }
176
177     /*
178      * Check whether peer indicated implicit support for updated SPNEGO
179      * (eg. in the Kerberos case by using CFX)
180      */
181     if (gss_inquire_sec_context_by_oid(&minor, ctx->negotiated_ctx_id,
182                                        GSS_C_PEER_HAS_UPDATED_SPNEGO,
183                                        &buffer_set) == GSS_S_COMPLETE) {
184         *require_mic = 1;
185         gss_release_buffer_set(&minor, &buffer_set);
186     }
187
188     /* Safe-to-omit MIC rules follow */
189     if (*require_mic) {
190         if (gss_oid_equal(ctx->negotiated_mech_type, ctx->preferred_mech_type)) {
191             *require_mic = 0;
192         } else if (gss_oid_equal(ctx->negotiated_mech_type, &_gss_spnego_krb5_mechanism_oid_desc) &&
193                    gss_oid_equal(ctx->preferred_mech_type, &_gss_spnego_mskrb_mechanism_oid_desc)) {
194             *require_mic = 0;
195         }
196     }
197
198     return GSS_S_COMPLETE;
199 }
200
201 static int
202 add_mech_type(gss_OID mech_type,
203               int includeMSCompatOID,
204               MechTypeList *mechtypelist)
205 {
206     MechType mech;
207     int ret;
208
209     if (gss_oid_equal(mech_type, GSS_SPNEGO_MECHANISM))
210         return 0;
211
212     if (includeMSCompatOID &&
213         gss_oid_equal(mech_type, &_gss_spnego_krb5_mechanism_oid_desc)) {
214         ret = der_get_oid(_gss_spnego_mskrb_mechanism_oid_desc.elements,
215                           _gss_spnego_mskrb_mechanism_oid_desc.length,
216                           &mech,
217                           NULL);
218         if (ret)
219             return ret;
220         ret = add_MechTypeList(mechtypelist, &mech);
221         free_MechType(&mech);
222         if (ret)
223             return ret;
224     }
225     ret = der_get_oid(mech_type->elements, mech_type->length, &mech, NULL);
226     if (ret)
227         return ret;
228     ret = add_MechTypeList(mechtypelist, &mech);
229     free_MechType(&mech);
230     return ret;
231 }
232
233
234 OM_uint32
235 _gss_spnego_indicate_mechtypelist (OM_uint32 *minor_status,
236                                    gss_name_t target_name,
237                                    OM_uint32 (*func)(gss_name_t, gss_OID),
238                                    int includeMSCompatOID,
239                                    const gssspnego_cred cred_handle,
240                                    MechTypeList *mechtypelist,
241                                    gss_OID *preferred_mech)
242 {
243     gss_OID_set supported_mechs = GSS_C_NO_OID_SET;
244     gss_OID first_mech = GSS_C_NO_OID;
245     OM_uint32 ret;
246     int i;
247
248     mechtypelist->len = 0;
249     mechtypelist->val = NULL;
250
251     if (cred_handle != NULL) {
252         ret = gss_inquire_cred(minor_status,
253                                cred_handle->negotiated_cred_id,
254                                NULL,
255                                NULL,
256                                NULL,
257                                &supported_mechs);
258     } else {
259         ret = gss_indicate_mechs(minor_status, &supported_mechs);
260     }
261
262     if (ret != GSS_S_COMPLETE) {
263         return ret;
264     }
265
266     if (supported_mechs->count == 0) {
267         *minor_status = ENOENT;
268         gss_release_oid_set(minor_status, &supported_mechs);
269         return GSS_S_FAILURE;
270     }
271
272     ret = (*func)(target_name, GSS_KRB5_MECHANISM);
273     if (ret == GSS_S_COMPLETE) {
274         ret = add_mech_type(GSS_KRB5_MECHANISM,
275                             includeMSCompatOID,
276                             mechtypelist);
277         if (!GSS_ERROR(ret))
278             first_mech = GSS_KRB5_MECHANISM;
279     }
280     ret = GSS_S_COMPLETE;
281
282     for (i = 0; i < supported_mechs->count; i++) {
283         OM_uint32 subret;
284         if (gss_oid_equal(&supported_mechs->elements[i], GSS_SPNEGO_MECHANISM))
285             continue;
286         if (gss_oid_equal(&supported_mechs->elements[i], GSS_KRB5_MECHANISM))
287             continue;
288
289         subret = (*func)(target_name, &supported_mechs->elements[i]);
290         if (subret != GSS_S_COMPLETE)
291             continue;
292
293         ret = add_mech_type(&supported_mechs->elements[i],
294                             includeMSCompatOID,
295                             mechtypelist);
296         if (ret != 0) {
297             *minor_status = ret;
298             ret = GSS_S_FAILURE;
299             break;
300         }
301         if (first_mech == GSS_C_NO_OID)
302             first_mech = &supported_mechs->elements[i];
303     }
304
305     if (mechtypelist->len == 0) {
306         gss_release_oid_set(minor_status, &supported_mechs);
307         *minor_status = 0;
308         return GSS_S_BAD_MECH;
309     }
310
311     if (preferred_mech != NULL) {
312         ret = gss_duplicate_oid(minor_status, first_mech, preferred_mech);
313         if (ret != GSS_S_COMPLETE)
314             free_MechTypeList(mechtypelist);
315     }
316     gss_release_oid_set(minor_status, &supported_mechs);
317
318     return ret;
319 }