Some preliminary exposing of attributes here
[mech_eap.git] / accept_sec_context.c
1 /*
2  * Copyright (c) 2010, JANET(UK)
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 JANET(UK) 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND 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 THE COPYRIGHT HOLDER 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 "gssapiP_eap.h"
34
35 /*
36  * Mark a context as ready for cryptographic operations
37  */
38 static OM_uint32
39 acceptReady(OM_uint32 *minor, gss_ctx_id_t ctx, gss_cred_id_t cred)
40 {
41     OM_uint32 major;
42     VALUE_PAIR *vp;
43     gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER;
44
45     /* Cache encryption type derived from selected mechanism OID */
46     major = gssEapOidToEnctype(minor, ctx->mechanismUsed,
47                                &ctx->encryptionType);
48     if (GSS_ERROR(major))
49         return major;
50
51     vp = rc_avpair_get(ctx->acceptorCtx.avps, PW_USER_NAME, 0);
52     if (vp != NULL) {
53         nameBuf.length = vp->lvalue;
54         nameBuf.value = vp->strvalue;
55     } else if (ctx->initiatorName == GSS_C_NO_NAME) {
56         ctx->gssFlags |= GSS_C_ANON_FLAG;
57     }
58
59     if (nameBuf.length != 0 || ctx->initiatorName == GSS_C_NO_NAME) {
60         major = gssEapImportName(minor, &nameBuf, GSS_C_NT_USER_NAME,
61                                  &ctx->initiatorName);
62         if (GSS_ERROR(major))
63             return major;
64     }
65
66     ctx->initiatorName->attrCtx = gssEapCreateAttrContext(cred, ctx);
67
68     vp = rc_avpair_get(ctx->acceptorCtx.avps, 16, 311);
69     if (ctx->encryptionType != ENCTYPE_NULL && vp != NULL) {
70         major = gssEapDeriveRfc3961Key(minor,
71                                        (unsigned char *)vp->strvalue,
72                                        vp->lvalue,
73                                        ctx->encryptionType,
74                                        &ctx->rfc3961Key);
75         if (GSS_ERROR(major))
76             return major;
77
78         major = rfc3961ChecksumTypeForKey(minor, &ctx->rfc3961Key,
79                                            &ctx->checksumType);
80         if (GSS_ERROR(major))
81             return major;
82     } else {
83         /*
84          * draft-howlett-eap-gss says that integrity/confidentialty should
85          * always be advertised as available, but if we have no keying
86          * material it seems confusing to the caller to advertise this.
87          */
88         ctx->gssFlags &= ~(GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG);
89         ctx->encryptionType = ENCTYPE_NULL;
90     }
91
92     major = sequenceInit(minor,
93                          &ctx->seqState, ctx->recvSeq,
94                          ((ctx->gssFlags & GSS_C_REPLAY_FLAG) != 0),
95                          ((ctx->gssFlags & GSS_C_SEQUENCE_FLAG) != 0),
96                          TRUE);
97     if (GSS_ERROR(major))
98         return major;
99
100     return GSS_S_COMPLETE;
101 }
102
103 static OM_uint32
104 eapGssSmAcceptIdentity(OM_uint32 *minor,
105                        gss_ctx_id_t ctx,
106                        gss_cred_id_t cred,
107                        gss_buffer_t inputToken,
108                        gss_channel_bindings_t chanBindings,
109                        gss_buffer_t outputToken)
110 {
111     OM_uint32 major;
112     union {
113         struct eap_hdr pdu;
114         unsigned char data[5];
115     } pkt;
116     gss_buffer_desc pktBuffer;
117
118     if (inputToken != GSS_C_NO_BUFFER && inputToken->length != 0)
119         return GSS_S_DEFECTIVE_TOKEN;
120
121     assert(ctx->acceptorCtx.radHandle == NULL);
122
123     major = gssEapRadiusAllocHandle(minor, cred, &ctx->acceptorCtx.radHandle);
124     if (GSS_ERROR(major))
125         return major;
126
127     if (ctx->acceptorName == GSS_C_NO_NAME &&
128         cred != GSS_C_NO_CREDENTIAL &&
129         cred->name != GSS_C_NO_NAME) {
130         major = gss_duplicate_name(minor, cred->name, &ctx->acceptorName);
131         if (GSS_ERROR(major))
132             return major;
133     }
134
135     pkt.pdu.code = EAP_CODE_REQUEST;
136     pkt.pdu.identifier = 0;
137     pkt.pdu.length = htons(sizeof(pkt.data));
138     pkt.data[4] = EAP_TYPE_IDENTITY;
139
140     pktBuffer.length = sizeof(pkt.data);
141     pktBuffer.value = pkt.data;
142
143     major = duplicateBuffer(minor, &pktBuffer, outputToken);
144     if (GSS_ERROR(major))
145         return major;
146
147     ctx->state = EAP_STATE_AUTHENTICATE;
148
149     return GSS_S_CONTINUE_NEEDED;
150 }
151
152 static OM_uint32
153 importInitiatorIdentity(OM_uint32 *minor,
154                         gss_ctx_id_t ctx,
155                         gss_buffer_t inputToken,
156                         gss_buffer_t nameBuf)
157 {
158     OM_uint32 major, tmpMinor;
159     struct eap_hdr *pdu = (struct eap_hdr *)inputToken->value;
160     unsigned char *pos = (unsigned char *)(pdu + 1);
161     gss_name_t name;
162
163     assert(pdu->code == EAP_CODE_RESPONSE);
164     assert(pos[0] == EAP_TYPE_IDENTITY);
165
166     nameBuf->value = pos + 1;
167     nameBuf->length = inputToken->length - sizeof(*pdu) - 1;
168
169     major = gssEapImportName(minor, nameBuf, GSS_C_NT_USER_NAME, &name);
170     if (GSS_ERROR(major))
171         return major;
172
173     gssEapReleaseName(&tmpMinor, &ctx->initiatorName);
174     ctx->initiatorName = name;
175
176     return GSS_S_COMPLETE;
177 }
178
179 static OM_uint32
180 eapGssSmAcceptAuthenticate(OM_uint32 *minor,
181                            gss_ctx_id_t ctx,
182                            gss_cred_id_t cred,
183                            gss_buffer_t inputToken,
184                            gss_channel_bindings_t chanBindings,
185                            gss_buffer_t outputToken)
186 {
187     OM_uint32 major, tmpMinor;
188     int code;
189     VALUE_PAIR *send = NULL;
190     VALUE_PAIR *received = NULL;
191     rc_handle *rh = ctx->acceptorCtx.radHandle;
192     char msgBuffer[4096];
193     struct eap_hdr *pdu;
194     unsigned char *pos;
195     gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER;
196
197     pdu = (struct eap_hdr *)inputToken->value;
198     pos = (unsigned char *)(pdu + 1);
199
200     if (inputToken->length > sizeof(*pdu) &&
201         pdu->code == EAP_CODE_RESPONSE &&
202         pos[0] == EAP_TYPE_IDENTITY) {
203         major = importInitiatorIdentity(minor, ctx, inputToken, &nameBuf);
204         if (GSS_ERROR(major))
205             goto cleanup;
206
207         major = addAvpFromBuffer(minor, rh, &send, PW_USER_NAME, &nameBuf);
208         if (GSS_ERROR(major))
209             goto cleanup;
210     }
211
212     major = addAvpFromBuffer(minor, rh, &send, PW_EAP_MESSAGE, inputToken);
213     if (GSS_ERROR(major))
214         goto cleanup;
215
216     if (ctx->acceptorCtx.lastStatus == CHALLENGE_RC) {
217         major = addAvpFromBuffer(minor, rh, &send, PW_STATE,
218                                  &ctx->acceptorCtx.state);
219         if (GSS_ERROR(major))
220             goto cleanup;
221
222         gss_release_buffer(&tmpMinor, &ctx->acceptorCtx.state);
223     }
224
225     code = rc_auth(rh, 0, send, &received, msgBuffer);
226     switch (code) {
227     case OK_RC:
228     case CHALLENGE_RC:
229         major = GSS_S_CONTINUE_NEEDED;
230         break;
231     case TIMEOUT_RC:
232         major = GSS_S_UNAVAILABLE;
233         break;
234     case REJECT_RC:
235         major = GSS_S_DEFECTIVE_CREDENTIAL;
236         break;
237     default:
238         major = GSS_S_FAILURE;
239         goto cleanup;
240     }
241
242     if (GSS_ERROR(major))
243         goto cleanup;
244
245     ctx->acceptorCtx.lastStatus = code;
246
247     major = getBufferFromAvps(minor, received, PW_EAP_MESSAGE,
248                               outputToken, TRUE);
249     if ((major == GSS_S_UNAVAILABLE && code != OK_RC) ||
250         GSS_ERROR(major))
251         goto cleanup;
252
253     if (code == CHALLENGE_RC) {
254         major = getBufferFromAvps(minor, received, PW_STATE,
255                                   &ctx->acceptorCtx.state, TRUE);
256         if (major != GSS_S_UNAVAILABLE && GSS_ERROR(major))
257             goto cleanup;
258     } else {
259         ctx->acceptorCtx.avps = received;
260         received = NULL;
261
262         major = acceptReady(minor, ctx, cred);
263         if (GSS_ERROR(major))
264             goto cleanup;
265
266         ctx->state = EAP_STATE_GSS_CHANNEL_BINDINGS;
267     }
268
269     major = GSS_S_CONTINUE_NEEDED;
270
271 cleanup:
272     if (received != NULL)
273         rc_avpair_free(received);
274
275     return major;
276 }
277
278 static OM_uint32
279 eapGssSmAcceptGssChannelBindings(OM_uint32 *minor,
280                                  gss_ctx_id_t ctx,
281                                  gss_cred_id_t cred,
282                                  gss_buffer_t inputToken,
283                                  gss_channel_bindings_t chanBindings,
284                                  gss_buffer_t outputToken)
285 {
286     OM_uint32 major;
287     gss_iov_buffer_desc iov[2];
288
289     outputToken->length = 0;
290     outputToken->value = NULL;
291
292     if (chanBindings == GSS_C_NO_CHANNEL_BINDINGS) {
293         ctx->state = EAP_STATE_ESTABLISHED;
294         return GSS_S_COMPLETE;
295     }
296
297     if (inputToken->length < 14) {
298         return GSS_S_DEFECTIVE_TOKEN;
299     }
300
301     iov[0].type = GSS_IOV_BUFFER_TYPE_DATA;
302     iov[0].buffer.length = 0;
303     iov[0].buffer.value = NULL;
304
305     if (chanBindings != GSS_C_NO_CHANNEL_BINDINGS)
306         iov[0].buffer = chanBindings->application_data;
307
308     iov[1].type = GSS_IOV_BUFFER_TYPE_HEADER;
309     iov[1].buffer.length = 16;
310     iov[1].buffer.value = (unsigned char *)inputToken->value - 2;
311
312     assert(load_uint16_be(iov[1].buffer.value) == TOK_TYPE_GSS_CB);
313
314     iov[2].type = GSS_IOV_BUFFER_TYPE_TRAILER;
315     iov[2].buffer.length = inputToken->length - 14;
316     iov[2].buffer.value = (unsigned char *)inputToken->value + 14;
317
318     major = gssEapUnwrapOrVerifyMIC(minor, ctx, NULL, NULL,
319                                     iov, 3, TOK_TYPE_GSS_CB);
320     if (major == GSS_S_COMPLETE) {
321         ctx->state = EAP_STATE_ESTABLISHED;
322     }
323
324 #if 0
325     gss_release_buffer(&tmpMinor, &iov[0].buffer);
326 #endif
327
328     return major;
329 }
330
331 static OM_uint32
332 eapGssSmAcceptEstablished(OM_uint32 *minor,
333                           gss_ctx_id_t ctx,
334                           gss_cred_id_t cred,
335                           gss_buffer_t inputToken,
336                           gss_channel_bindings_t chanBindings,
337                           gss_buffer_t outputToken)
338 {
339     /* Called with already established context */
340     *minor = EINVAL;
341     return GSS_S_BAD_STATUS;
342 }
343
344 static struct gss_eap_acceptor_sm {
345     enum gss_eap_token_type inputTokenType;
346     enum gss_eap_token_type outputTokenType;
347     OM_uint32 (*processToken)(OM_uint32 *,
348                               gss_ctx_id_t,
349                               gss_cred_id_t,
350                               gss_buffer_t,
351                               gss_channel_bindings_t,
352                               gss_buffer_t);
353 } eapGssAcceptorSm[] = {
354     { TOK_TYPE_EAP_RESP,    TOK_TYPE_EAP_REQ,  eapGssSmAcceptIdentity           },
355     { TOK_TYPE_EAP_RESP,    TOK_TYPE_EAP_REQ,  eapGssSmAcceptAuthenticate       },
356     { TOK_TYPE_GSS_CB,      TOK_TYPE_NONE,     eapGssSmAcceptGssChannelBindings },
357     { TOK_TYPE_NONE,        TOK_TYPE_NONE,     eapGssSmAcceptEstablished        },
358 };
359
360 OM_uint32
361 gss_accept_sec_context(OM_uint32 *minor,
362                        gss_ctx_id_t *context_handle,
363                        gss_cred_id_t cred,
364                        gss_buffer_t input_token,
365                        gss_channel_bindings_t input_chan_bindings,
366                        gss_name_t *src_name,
367                        gss_OID *mech_type,
368                        gss_buffer_t output_token,
369                        OM_uint32 *ret_flags,
370                        OM_uint32 *time_rec,
371                        gss_cred_id_t *delegated_cred_handle)
372 {
373     OM_uint32 major;
374     OM_uint32 tmpMajor, tmpMinor;
375     gss_ctx_id_t ctx = *context_handle;
376     struct gss_eap_acceptor_sm *sm = NULL;
377     gss_buffer_desc innerInputToken = GSS_C_EMPTY_BUFFER;
378     gss_buffer_desc innerOutputToken = GSS_C_EMPTY_BUFFER;
379
380     *minor = 0;
381
382     output_token->length = 0;
383     output_token->value = NULL;
384
385     if (cred != GSS_C_NO_CREDENTIAL && !(cred->flags & CRED_FLAG_ACCEPT)) {
386         return GSS_S_NO_CRED;
387     }
388
389     if (input_token == GSS_C_NO_BUFFER || input_token->length == 0) {
390         return GSS_S_DEFECTIVE_TOKEN;
391     }
392
393     if (ctx == GSS_C_NO_CONTEXT) {
394         major = gssEapAllocContext(minor, &ctx);
395         if (GSS_ERROR(major))
396             return major;
397
398         *context_handle = ctx;
399     }
400
401     GSSEAP_MUTEX_LOCK(&ctx->mutex);
402
403     sm = &eapGssAcceptorSm[ctx->state];
404
405     major = gssEapVerifyToken(minor, ctx, input_token,
406                               sm->inputTokenType, &innerInputToken);
407     if (GSS_ERROR(major))
408         goto cleanup;
409
410     /* If credentials were provided, check they're usable with this mech */
411     if (!gssEapCredAvailable(cred, ctx->mechanismUsed)) {
412         major = GSS_S_BAD_MECH;
413         goto cleanup;
414     }
415
416     do {
417         sm = &eapGssAcceptorSm[ctx->state];
418
419         major = (sm->processToken)(minor,
420                                    ctx,
421                                    cred,
422                                    &innerInputToken,
423                                    input_chan_bindings,
424                                    &innerOutputToken);
425         if (GSS_ERROR(major))
426             goto cleanup;
427     } while (major == GSS_S_CONTINUE_NEEDED && innerOutputToken.length == 0);
428
429     if (mech_type != NULL) {
430         if (!gssEapInternalizeOid(ctx->mechanismUsed, mech_type))
431             duplicateOid(&tmpMinor, ctx->mechanismUsed, mech_type);
432     }
433     if (innerOutputToken.length != 0) {
434         tmpMajor = gssEapMakeToken(&tmpMinor, ctx, &innerOutputToken,
435                                    sm->outputTokenType, output_token);
436         if (GSS_ERROR(tmpMajor)) {
437             major = tmpMajor;
438             *minor = tmpMinor;
439             goto cleanup;
440         }
441     }
442     if (ret_flags != NULL)
443         *ret_flags = ctx->gssFlags;
444     if (delegated_cred_handle != NULL)
445         *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
446
447     if (major == GSS_S_COMPLETE) {
448         if (src_name != NULL && ctx->initiatorName != GSS_C_NO_NAME) {
449             major = gss_duplicate_name(&tmpMinor, ctx->initiatorName, src_name);
450             if (GSS_ERROR(major))
451                 goto cleanup;
452         }
453         if (time_rec != NULL)
454             gss_context_time(&tmpMinor, ctx, time_rec);
455     }
456
457     assert(ctx->state == EAP_STATE_ESTABLISHED || major == GSS_S_CONTINUE_NEEDED);
458
459 cleanup:
460     GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
461
462     if (GSS_ERROR(major))
463         gssEapReleaseContext(&tmpMinor, context_handle);
464
465     gss_release_buffer(&tmpMinor, &innerOutputToken);
466
467     return major;
468 }
469