]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - crypto/heimdal/lib/gssapi/mech/gss_accept_sec_context.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / crypto / heimdal / lib / gssapi / mech / gss_accept_sec_context.c
1 /*-
2  * Copyright (c) 2005 Doug Rabson
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  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  *      $FreeBSD: src/lib/libgssapi/gss_accept_sec_context.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
27  */
28
29 #include "mech_locl.h"
30
31 static OM_uint32
32 parse_header(const gss_buffer_t input_token, gss_OID mech_oid)
33 {
34         unsigned char *p = input_token->value;
35         size_t len = input_token->length;
36         size_t a, b;
37
38         /*
39          * Token must start with [APPLICATION 0] SEQUENCE.
40          * But if it doesn't assume it is DCE-STYLE Kerberos!
41          */
42         if (len == 0)
43                 return (GSS_S_DEFECTIVE_TOKEN);
44
45         p++;
46         len--;
47
48         /*
49          * Decode the length and make sure it agrees with the
50          * token length.
51          */
52         if (len == 0)
53                 return (GSS_S_DEFECTIVE_TOKEN);
54         if ((*p & 0x80) == 0) {
55                 a = *p;
56                 p++;
57                 len--;
58         } else {
59                 b = *p & 0x7f;
60                 p++;
61                 len--;
62                 if (len < b)
63                     return (GSS_S_DEFECTIVE_TOKEN);
64                 a = 0;
65                 while (b) {
66                     a = (a << 8) | *p;
67                     p++;
68                     len--;
69                     b--;
70                 }
71         }
72         if (a != len)
73                 return (GSS_S_DEFECTIVE_TOKEN);
74
75         /*
76          * Decode the OID for the mechanism. Simplify life by
77          * assuming that the OID length is less than 128 bytes.
78          */
79         if (len < 2 || *p != 0x06)
80                 return (GSS_S_DEFECTIVE_TOKEN);
81         if ((p[1] & 0x80) || p[1] > (len - 2))
82                 return (GSS_S_DEFECTIVE_TOKEN);
83         mech_oid->length = p[1];
84         p += 2;
85         len -= 2;
86         mech_oid->elements = p;
87
88         return GSS_S_COMPLETE;
89 }
90
91 static gss_OID_desc krb5_mechanism =
92     {9, rk_UNCONST("\x2a\x86\x48\x86\xf7\x12\x01\x02\x02")};
93 static gss_OID_desc ntlm_mechanism =
94     {10, rk_UNCONST("\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a")};
95 static gss_OID_desc spnego_mechanism =
96     {6, rk_UNCONST("\x2b\x06\x01\x05\x05\x02")};
97
98 static OM_uint32
99 choose_mech(const gss_buffer_t input, gss_OID mech_oid)
100 {
101         OM_uint32 status;
102
103         /*
104          * First try to parse the gssapi token header and see if it's a
105          * correct header, use that in the first hand.
106          */
107
108         status = parse_header(input, mech_oid);
109         if (status == GSS_S_COMPLETE)
110             return GSS_S_COMPLETE;
111
112         /*
113          * Lets guess what mech is really is, callback function to mech ??
114          */
115
116         if (input->length > 8 &&
117             memcmp((const char *)input->value, "NTLMSSP\x00", 8) == 0)
118         {
119                 *mech_oid = ntlm_mechanism;
120                 return GSS_S_COMPLETE;
121         } else if (input->length != 0 &&
122                    ((const char *)input->value)[0] == 0x6E)
123         {
124                 /* Could be a raw AP-REQ (check for APPLICATION tag) */
125                 *mech_oid = krb5_mechanism;
126                 return GSS_S_COMPLETE;
127         } else if (input->length == 0) {
128                 /*
129                  * There is the a wierd mode of SPNEGO (in CIFS and
130                  * SASL GSS-SPENGO where the first token is zero
131                  * length and the acceptor returns a mech_list, lets
132                  * hope that is what is happening now.
133                  *
134                  * http://msdn.microsoft.com/en-us/library/cc213114.aspx
135                  * "NegTokenInit2 Variation for Server-Initiation"
136                  */
137                 *mech_oid = spnego_mechanism;
138                 return GSS_S_COMPLETE;
139         }
140         return status;
141 }
142
143
144 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
145 gss_accept_sec_context(OM_uint32 *minor_status,
146     gss_ctx_id_t *context_handle,
147     const gss_cred_id_t acceptor_cred_handle,
148     const gss_buffer_t input_token,
149     const gss_channel_bindings_t input_chan_bindings,
150     gss_name_t *src_name,
151     gss_OID *mech_type,
152     gss_buffer_t output_token,
153     OM_uint32 *ret_flags,
154     OM_uint32 *time_rec,
155     gss_cred_id_t *delegated_cred_handle)
156 {
157         OM_uint32 major_status, mech_ret_flags, junk;
158         gssapi_mech_interface m;
159         struct _gss_context *ctx = (struct _gss_context *) *context_handle;
160         struct _gss_cred *cred = (struct _gss_cred *) acceptor_cred_handle;
161         struct _gss_mechanism_cred *mc;
162         gss_cred_id_t acceptor_mc, delegated_mc;
163         gss_name_t src_mn;
164         gss_OID mech_ret_type = NULL;
165
166         *minor_status = 0;
167         if (src_name)
168             *src_name = GSS_C_NO_NAME;
169         if (mech_type)
170             *mech_type = GSS_C_NO_OID;
171         if (ret_flags)
172             *ret_flags = 0;
173         if (time_rec)
174             *time_rec = 0;
175         if (delegated_cred_handle)
176             *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
177         _mg_buffer_zero(output_token);
178
179
180         /*
181          * If this is the first call (*context_handle is NULL), we must
182          * parse the input token to figure out the mechanism to use.
183          */
184         if (*context_handle == GSS_C_NO_CONTEXT) {
185                 gss_OID_desc mech_oid;
186
187                 major_status = choose_mech(input_token, &mech_oid);
188                 if (major_status != GSS_S_COMPLETE)
189                         return major_status;
190
191                 /*
192                  * Now that we have a mechanism, we can find the
193                  * implementation.
194                  */
195                 ctx = malloc(sizeof(struct _gss_context));
196                 if (!ctx) {
197                         *minor_status = ENOMEM;
198                         return (GSS_S_DEFECTIVE_TOKEN);
199                 }
200                 memset(ctx, 0, sizeof(struct _gss_context));
201                 m = ctx->gc_mech = __gss_get_mechanism(&mech_oid);
202                 if (!m) {
203                         free(ctx);
204                         return (GSS_S_BAD_MECH);
205                 }
206                 *context_handle = (gss_ctx_id_t) ctx;
207         } else {
208                 m = ctx->gc_mech;
209         }
210
211         if (cred) {
212                 HEIM_SLIST_FOREACH(mc, &cred->gc_mc, gmc_link)
213                         if (mc->gmc_mech == m)
214                                 break;
215                 if (!mc) {
216                         gss_delete_sec_context(&junk, context_handle, NULL);
217                         return (GSS_S_BAD_MECH);
218                 }
219                 acceptor_mc = mc->gmc_cred;
220         } else {
221                 acceptor_mc = GSS_C_NO_CREDENTIAL;
222         }
223         delegated_mc = GSS_C_NO_CREDENTIAL;
224
225         mech_ret_flags = 0;
226         major_status = m->gm_accept_sec_context(minor_status,
227             &ctx->gc_ctx,
228             acceptor_mc,
229             input_token,
230             input_chan_bindings,
231             &src_mn,
232             &mech_ret_type,
233             output_token,
234             &mech_ret_flags,
235             time_rec,
236             &delegated_mc);
237         if (major_status != GSS_S_COMPLETE &&
238             major_status != GSS_S_CONTINUE_NEEDED)
239         {
240                 _gss_mg_error(m, major_status, *minor_status);
241                 gss_delete_sec_context(&junk, context_handle, NULL);
242                 return (major_status);
243         }
244
245         if (mech_type)
246             *mech_type = mech_ret_type;
247
248         if (src_name && src_mn) {
249                 /*
250                  * Make a new name and mark it as an MN.
251                  */
252                 struct _gss_name *name = _gss_make_name(m, src_mn);
253
254                 if (!name) {
255                         m->gm_release_name(minor_status, &src_mn);
256                         gss_delete_sec_context(&junk, context_handle, NULL);
257                         return (GSS_S_FAILURE);
258                 }
259                 *src_name = (gss_name_t) name;
260         } else if (src_mn) {
261                 m->gm_release_name(minor_status, &src_mn);
262         }
263
264         if (mech_ret_flags & GSS_C_DELEG_FLAG) {
265                 if (!delegated_cred_handle) {
266                         m->gm_release_cred(minor_status, &delegated_mc);
267                         mech_ret_flags &=
268                             ~(GSS_C_DELEG_FLAG|GSS_C_DELEG_POLICY_FLAG);
269                 } else if (gss_oid_equal(mech_ret_type, &m->gm_mech_oid) == 0) {
270                         /*
271                          * If the returned mech_type is not the same
272                          * as the mech, assume its pseudo mech type
273                          * and the returned type is already a
274                          * mech-glue object
275                          */
276                         *delegated_cred_handle = delegated_mc;
277
278                 } else if (delegated_mc) {
279                         struct _gss_cred *dcred;
280                         struct _gss_mechanism_cred *dmc;
281
282                         dcred = malloc(sizeof(struct _gss_cred));
283                         if (!dcred) {
284                                 *minor_status = ENOMEM;
285                                 gss_delete_sec_context(&junk, context_handle, NULL);
286                                 return (GSS_S_FAILURE);
287                         }
288                         HEIM_SLIST_INIT(&dcred->gc_mc);
289                         dmc = malloc(sizeof(struct _gss_mechanism_cred));
290                         if (!dmc) {
291                                 free(dcred);
292                                 *minor_status = ENOMEM;
293                                 gss_delete_sec_context(&junk, context_handle, NULL);
294                                 return (GSS_S_FAILURE);
295                         }
296                         dmc->gmc_mech = m;
297                         dmc->gmc_mech_oid = &m->gm_mech_oid;
298                         dmc->gmc_cred = delegated_mc;
299                         HEIM_SLIST_INSERT_HEAD(&dcred->gc_mc, dmc, gmc_link);
300
301                         *delegated_cred_handle = (gss_cred_id_t) dcred;
302                 }
303         }
304
305         if (ret_flags)
306             *ret_flags = mech_ret_flags;
307         return (major_status);
308 }