Conf file is now in source tree
[mod_auth_kerb.cvs/.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                                          KERB_CTXT,
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 = malloc(buf_len);
68     if (*outbuf == NULL) {
69        *minor_status = ENOMEM;
70        free(buf);
71        return GSS_S_FAILURE;
72     }
73
74     memcpy(*outbuf, buf + buf_size - buf_len, buf_len);
75     *outbuf_size = buf_len;
76
77     free(buf);
78     
79     return GSS_S_COMPLETE;
80 }
81
82 static OM_uint32
83 send_reject (OM_uint32 *minor_status,
84              gss_buffer_t output_token)
85 {
86     NegTokenTarg targ;
87     OM_uint32 ret;
88
89     targ.negResult = malloc(sizeof(*targ.negResult));
90     if (targ.negResult == NULL) {
91         *minor_status = ENOMEM;
92         return GSS_S_FAILURE;
93     }
94     *(targ.negResult) = reject;
95
96     targ.supportedMech = NULL;
97     targ.responseToken = NULL;
98     targ.mechListMIC   = NULL;
99     
100     ret = code_NegTokenArg (minor_status, &targ, 
101                             (unsigned char**) &output_token->value, &output_token->length);
102     free_NegTokenTarg(&targ);
103     if (ret)
104         return ret;
105
106     return GSS_S_BAD_MECH;
107 }
108
109 static OM_uint32
110 send_accept (OM_uint32 *minor_status,
111              gss_buffer_t output_token,
112              gss_buffer_t mech_token)
113 {
114     NegTokenTarg targ;
115     OM_uint32 ret;
116
117     memset(&targ, 0, sizeof(targ));
118     targ.negResult = malloc(sizeof(*targ.negResult));
119     if (targ.negResult == NULL) {
120         *minor_status = ENOMEM;
121         return GSS_S_FAILURE;
122     }
123     *(targ.negResult) = accept_completed;
124
125     targ.supportedMech = malloc(sizeof(*targ.supportedMech));
126     if (targ.supportedMech == NULL) {
127         free_NegTokenTarg(&targ);
128         *minor_status = ENOMEM;
129         return GSS_S_FAILURE;
130     }
131
132     ret = der_get_oid(GSS_KRB5_MECH->elements,
133                       GSS_KRB5_MECH->length,
134                       targ.supportedMech,
135                       NULL);
136     if (ret) {
137         free_NegTokenTarg(&targ);
138         *minor_status = ENOMEM;
139         return GSS_S_FAILURE;
140     }
141
142     if (mech_token != NULL && mech_token->length != 0) {
143         targ.responseToken = malloc(sizeof(*targ.responseToken));
144         if (targ.responseToken == NULL) {
145             free_NegTokenTarg(&targ);
146             *minor_status = ENOMEM;
147             return GSS_S_FAILURE;
148         }
149         targ.responseToken->length = mech_token->length;
150         targ.responseToken->data   = mech_token->value;
151         mech_token->length = 0;
152         mech_token->value  = NULL;
153     } else {
154         targ.responseToken = NULL;
155     }
156
157     ret = code_NegTokenArg (minor_status, &targ, 
158                             (unsigned char **) &output_token->value, &output_token->length);
159     free_NegTokenTarg(&targ);
160     if (ret)
161         return ret;
162
163     return GSS_S_COMPLETE;
164 }
165
166 OM_uint32 KRB5_LIB_FUNCTION gss_accept_sec_context_spnego
167            (OM_uint32 * minor_status,
168             gss_ctx_id_t * context_handle,
169             const gss_cred_id_t acceptor_cred_handle,
170             const gss_buffer_t input_token_buffer,
171             const gss_channel_bindings_t input_chan_bindings,
172             gss_name_t * src_name,
173             gss_OID * mech_type,
174             gss_buffer_t output_token,
175             OM_uint32 * ret_flags,
176             OM_uint32 * time_rec,
177             gss_cred_id_t * delegated_cred_handle)
178 {
179    NegTokenInit init_token;
180    OM_uint32 major_status;
181    OM_uint32 minor_status2;
182    gss_buffer_desc ibuf, obuf;
183    gss_buffer_t ot = NULL;
184    unsigned char *buf;
185    size_t buf_size;
186    size_t len, taglen, ni_len;
187    int found = 0;
188    int ret, i;
189
190    memset(&init_token, 0, sizeof(init_token));
191
192    ret = gssapi_spnego_decapsulate(minor_status, input_token_buffer,
193                                    &buf, &buf_size, GSS_SPNEGO_MECH);
194    if (ret)
195       return ret;
196
197    ret = der_match_tag_and_length(buf, buf_size, KERB_CTXT, CONS,
198                                   0, &len, &taglen);
199    if (ret)
200       return ret;
201
202    ret = decode_NegTokenInit(buf + taglen, len, &init_token, &ni_len);
203    if (ret) {
204       *minor_status = EINVAL; /* XXX */
205       return GSS_S_DEFECTIVE_TOKEN;
206    }
207
208    if (init_token.mechTypes == NULL)
209       return send_reject (minor_status, output_token);
210
211    for (i = 0; !found && i < init_token.mechTypes->len; ++i) {
212       unsigned char mechbuf[17];
213       size_t mech_len;
214
215       ret = der_put_oid (mechbuf + sizeof(mechbuf) - 1,
216                          sizeof(mechbuf),
217                          &init_token.mechTypes->val[i],
218                          &mech_len);
219       if (ret)
220           return GSS_S_DEFECTIVE_TOKEN;
221       if (mech_len == GSS_KRB5_MECH->length
222           && memcmp(GSS_KRB5_MECH->elements,
223                     mechbuf + sizeof(mechbuf) - mech_len,
224                     mech_len) == 0)
225           found = 1;
226    }
227
228    if (!found)
229       return send_reject (minor_status, output_token);
230
231    if (init_token.mechToken != NULL) {
232       ibuf.length = init_token.mechToken->length;
233       ibuf.value  = init_token.mechToken->data;
234
235       major_status = gss_accept_sec_context(minor_status,
236                                             context_handle,
237                                             acceptor_cred_handle,
238                                             &ibuf,
239                                             input_chan_bindings,
240                                             src_name,
241                                             mech_type,
242                                             &obuf,
243                                             ret_flags,
244                                             time_rec,
245                                             delegated_cred_handle);
246       if (GSS_ERROR(major_status)) {
247          send_reject (&minor_status2, output_token);
248          return major_status;
249       }
250       ot = &obuf;
251    }
252
253    ret = send_accept (&minor_status2, output_token, ot);
254    if (ot != NULL)
255       gss_release_buffer(&minor_status2, ot);
256
257    return ret;
258 }