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