9f76695e0637d048dde2174cb1d137c86cdaae24
[mod_auth_kerb.git] / spnegokrb5 / accept_sec_context.c
1 /*
2  * SPNEGO wrapper for Kerberos5 GSS-API 
3  * kouril@ics.muni.cz, 2003
4  * (mostly based on Heimdal code)
5  */
6
7 #include "spnegokrb5_locl.h"
8
9 #define OID_cmp(o1, o2) \
10         (((o1)->length == (o2)->length) && \
11          (memcmp((o1)->components, (o2)->components,(int) (o1)->length) == 0))
12
13 static OM_uint32
14 code_NegTokenArg(OM_uint32 *minor_status,
15                  const NegTokenTarg *targ,
16                  unsigned char **outbuf,
17                  size_t *outbuf_size)
18 {
19     OM_uint32 ret;
20     u_char *buf;
21     size_t buf_size, buf_len;
22
23     buf_size = 1024;
24     buf = malloc(buf_size);
25     if (buf == NULL) {
26         *minor_status = ENOMEM;
27         return GSS_S_FAILURE;
28     }
29
30     do {
31         ret = encode_NegTokenTarg(buf + buf_size -1,
32                                   buf_size,
33                                   targ, &buf_len);
34         if (ret == 0) {
35             size_t tmp;
36
37             ret = der_put_length_and_tag(buf + buf_size - buf_len - 1,
38                                          buf_size - buf_len,
39                                          buf_len,
40                                          CONTEXT,
41                                          CONS,
42                                          1,
43                                          &tmp);
44             if (ret == 0)
45                 buf_len += tmp;
46         }
47         if (ret) {
48             if (ret == ASN1_OVERFLOW) {
49                 u_char *tmp;
50
51                 buf_size *= 2;
52                 tmp = realloc (buf, buf_size);
53                 if (tmp == NULL) {
54                     *minor_status = ENOMEM;
55                     free(buf);
56                     return GSS_S_FAILURE;
57                 }
58                 buf = tmp;
59             } else {
60                 *minor_status = ret;
61                 free(buf);
62                 return GSS_S_FAILURE;
63             }
64         }
65     } while (ret == ASN1_OVERFLOW);
66
67     *outbuf      = buf + buf_size - buf_len;
68     *outbuf_size = buf_len;
69     return GSS_S_COMPLETE;
70 }
71
72 static OM_uint32
73 send_reject (OM_uint32 *minor_status,
74              gss_buffer_t output_token)
75 {
76     NegTokenTarg targ;
77     OM_uint32 ret;
78
79     targ.negResult = malloc(sizeof(*targ.negResult));
80     if (targ.negResult == NULL) {
81         *minor_status = ENOMEM;
82         return GSS_S_FAILURE;
83     }
84     *(targ.negResult) = reject;
85
86     targ.supportedMech = NULL;
87     targ.responseToken = NULL;
88     targ.mechListMIC   = NULL;
89     
90     ret = code_NegTokenArg (minor_status, &targ, 
91                             (unsigned char**) &output_token->value, &output_token->length);
92     free_NegTokenTarg(&targ);
93     if (ret)
94         return ret;
95
96     return GSS_S_BAD_MECH;
97 }
98
99 static OM_uint32
100 send_accept (OM_uint32 *minor_status,
101              gss_buffer_t output_token,
102              gss_buffer_t mech_token)
103 {
104     NegTokenTarg targ;
105     OM_uint32 ret;
106
107     memset(&targ, 0, sizeof(targ));
108     targ.negResult = malloc(sizeof(*targ.negResult));
109     if (targ.negResult == NULL) {
110         *minor_status = ENOMEM;
111         return GSS_S_FAILURE;
112     }
113     *(targ.negResult) = accept_completed;
114
115     targ.supportedMech = malloc(sizeof(*targ.supportedMech));
116     if (targ.supportedMech == NULL) {
117         free_NegTokenTarg(&targ);
118         *minor_status = ENOMEM;
119         return GSS_S_FAILURE;
120     }
121
122     ret = der_get_oid(GSS_KRB5_MECH->elements,
123                       GSS_KRB5_MECH->length,
124                       targ.supportedMech,
125                       NULL);
126     if (ret) {
127         free_NegTokenTarg(&targ);
128         *minor_status = ENOMEM;
129         return GSS_S_FAILURE;
130     }
131
132     if (mech_token != NULL && mech_token->length != 0) {
133         targ.responseToken = malloc(sizeof(*targ.responseToken));
134         if (targ.responseToken == NULL) {
135             free_NegTokenTarg(&targ);
136             *minor_status = ENOMEM;
137             return GSS_S_FAILURE;
138         }
139         targ.responseToken->length = mech_token->length;
140         targ.responseToken->data   = mech_token->value;
141         mech_token->length = 0;
142         mech_token->value  = NULL;
143     } else {
144         targ.responseToken = NULL;
145     }
146
147     ret = code_NegTokenArg (minor_status, &targ, 
148                             (unsigned char**) &output_token->value, &output_token->length);
149     free_NegTokenTarg(&targ);
150     if (ret)
151         return ret;
152
153     return GSS_S_COMPLETE;
154 }
155
156 OM_uint32 gss_accept_sec_context_spnego
157            (OM_uint32 * minor_status,
158             gss_ctx_id_t * context_handle,
159             const gss_cred_id_t acceptor_cred_handle,
160             const gss_buffer_t input_token_buffer,
161             const gss_channel_bindings_t input_chan_bindings,
162             gss_name_t * src_name,
163             gss_OID * mech_type,
164             gss_buffer_t output_token,
165             OM_uint32 * ret_flags,
166             OM_uint32 * time_rec,
167             gss_cred_id_t * delegated_cred_handle)
168 {
169    NegTokenInit init_token;
170    OM_uint32 major_status;
171    gss_buffer_desc ibuf, obuf;
172    gss_buffer_t ot = NULL;
173    OM_uint32 minor;
174    unsigned char *buf;
175    size_t buf_size;
176    size_t len, taglen, ni_len;
177    int found = 0;
178    int ret, i;
179
180    memset(&init_token, 0, sizeof(init_token));
181
182    ret = gssapi_spnego_decapsulate(minor_status, input_token_buffer,
183                                    &buf, &buf_size, GSS_SPNEGO_MECH);
184    if (ret)
185       return ret;
186
187    ret = der_match_tag_and_length(buf, buf_size, CONTEXT, CONS,
188                                   0, &len, &taglen);
189    if (ret)
190       return ret;
191
192    ret = decode_NegTokenInit(buf + taglen, len, &init_token, &ni_len);
193    if (ret) {
194       *minor_status = EINVAL; /* XXX */
195       return GSS_S_DEFECTIVE_TOKEN;
196    }
197
198    if (init_token.mechTypes == NULL)
199       return send_reject (minor_status, output_token);
200
201    for (i = 0; !found && i < init_token.mechTypes->len; ++i) {
202       char mechbuf[17];
203       size_t mech_len;
204
205       ret = der_put_oid (mechbuf + sizeof(mechbuf) - 1,
206                          sizeof(mechbuf),
207                          &init_token.mechTypes->val[i],
208                          &mech_len);
209       if (ret)
210           return GSS_S_DEFECTIVE_TOKEN;
211       if (mech_len == GSS_KRB5_MECH->length
212           && memcmp(GSS_KRB5_MECH->elements,
213                     mechbuf + sizeof(mechbuf) - mech_len,
214                     mech_len) == 0)
215           found = 1;
216    }
217
218    if (!found)
219       return send_reject (minor_status, output_token);
220
221    if (init_token.mechToken != NULL) {
222       ibuf.length = init_token.mechToken->length;
223       ibuf.value  = init_token.mechToken->data;
224
225       major_status = gss_accept_sec_context(&minor,
226                                             context_handle,
227                                             acceptor_cred_handle,
228                                             &ibuf,
229                                             input_chan_bindings,
230                                             src_name,
231                                             mech_type,
232                                             &obuf,
233                                             ret_flags,
234                                             time_rec,
235                                             delegated_cred_handle);
236       if (GSS_ERROR(major_status)) {
237          send_reject (minor_status, output_token);
238          return major_status;
239       }
240       ot = &obuf;
241    }
242
243    ret = send_accept (minor_status, output_token, ot);
244    if (ot != NULL)
245       gss_release_buffer(&minor, ot);
246
247    return ret;
248 }