add test vendor info acceptor
[mech_eap.orig] / util_sm.c
1 /*
2  * Copyright (c) 2011, 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 /*
34  * Context establishment state machine.
35  */
36
37 #include "gssapiP_eap.h"
38
39 /* private flags */
40 #define SM_FLAG_TRANSITED                   0x80000000
41
42 #define SM_ASSERT_VALID(ctx, status)        do { \
43         assert(GSS_ERROR((status)) || \
44                ((status) == GSS_S_CONTINUE_NEEDED && ((ctx)->state > GSSEAP_STATE_INITIAL && (ctx)->state < GSSEAP_STATE_ESTABLISHED)) || \
45                ((status) == GSS_S_COMPLETE && (ctx)->state == GSSEAP_STATE_ESTABLISHED)); \
46     } while (0)
47
48 static const char *
49 gssEapStateToString(enum gss_eap_state state)
50 {
51     const char *s;
52
53     switch (state) {
54     case GSSEAP_STATE_INITIAL:
55         s = "INITIAL";
56         break;
57     case GSSEAP_STATE_AUTHENTICATE:
58         s = "AUTHENTICATE";
59         break;
60     case GSSEAP_STATE_INITIATOR_EXTS:
61         s = "INITIATOR_EXTS";
62         break;
63     case GSSEAP_STATE_ACCEPTOR_EXTS:
64         s = "ACCEPTOR_EXTS";
65         break;
66     case GSSEAP_STATE_REAUTHENTICATE:
67         s = "REAUTHENTICATE";
68         break;
69     case GSSEAP_STATE_ESTABLISHED:
70         s = "ESTABLISHED";
71         break;
72     default:
73         s = "INVALID";
74         break;
75     }
76
77     return s;
78 }
79
80 #ifdef GSSEAP_DEBUG
81 void
82 gssEapSmTransition(gss_ctx_id_t ctx, enum gss_eap_state state)
83 {
84     assert(state > ctx->state);
85     assert(state <= GSSEAP_STATE_ESTABLISHED);
86
87     fprintf(stderr, "GSS-EAP: state transition %s->%s\n",
88             gssEapStateToString(ctx->state), gssEapStateToString(state));
89
90     ctx->state = state;
91 }
92 #endif
93
94 static OM_uint32
95 makeErrorToken(OM_uint32 *minor,
96                OM_uint32 majorStatus,
97                OM_uint32 minorStatus,
98                gss_buffer_set_t *outputToken)
99 {
100     OM_uint32 major;
101     unsigned char errorData[8];
102     gss_buffer_desc errorBuffer;
103
104     assert(GSS_ERROR(majorStatus));
105
106     major = gss_create_empty_buffer_set(minor, outputToken);
107     if (GSS_ERROR(major))
108         return major;
109
110     /*
111      * Only return error codes that the initiator could have caused,
112      * to avoid information leakage.
113      */
114     if (IS_RADIUS_ERROR(minorStatus)) {
115         /* Squash RADIUS error codes */
116         minorStatus = GSSEAP_RADIUS_PROT_FAILURE;
117     } else if (!IS_WIRE_ERROR(minorStatus)) {
118         /* Don't return non-wire error codes */
119         return GSS_S_COMPLETE;
120     }
121
122     minorStatus -= ERROR_TABLE_BASE_eapg;
123
124     store_uint32_be(majorStatus, &errorData[0]);
125     store_uint32_be(minorStatus, &errorData[4]);
126
127     errorBuffer.length = sizeof(errorData);
128     errorBuffer.value = errorData;
129
130     major = gss_add_buffer_set_member(minor, &errorBuffer, outputToken);
131     if (GSS_ERROR(major))
132         return major;
133
134     return GSS_S_COMPLETE;
135 }
136
137 static OM_uint32
138 allocInnerTokens(OM_uint32 *minor,
139                  size_t count,
140                  gss_buffer_set_t *pTokens,
141                  OM_uint32 **pTokenTypes)
142 {
143     OM_uint32 major, tmpMinor;
144     gss_buffer_set_t tokens = GSS_C_NO_BUFFER_SET;
145     OM_uint32 *tokenTypes = NULL;
146
147     major = gss_create_empty_buffer_set(minor, &tokens);
148     if (GSS_ERROR(major))
149         goto cleanup;
150
151     assert(tokens->count == 0);
152     assert(tokens->elements == NULL);
153
154     tokens->elements = (gss_buffer_desc *)GSSEAP_CALLOC(count, sizeof(gss_buffer_desc));
155     if (tokens->elements == NULL) {
156         major = GSS_S_FAILURE;
157         *minor = ENOMEM;
158         goto cleanup;
159     }
160
161     tokenTypes = (OM_uint32 *)GSSEAP_CALLOC(count, sizeof(OM_uint32));
162     if (tokenTypes == NULL) {
163         major = GSS_S_FAILURE;
164         *minor = ENOMEM;
165         goto cleanup;
166     }
167
168     major = GSS_S_COMPLETE;
169     *minor = 0;
170
171 cleanup:
172     if (GSS_ERROR(major)) {
173         gss_release_buffer_set(&tmpMinor, &tokens);
174         tokens = GSS_C_NO_BUFFER_SET;
175         if (tokenTypes != NULL) {
176             GSSEAP_FREE(tokenTypes);
177             tokenTypes = NULL;
178         }
179     }
180
181     *pTokens = tokens;
182     *pTokenTypes = tokenTypes;
183
184     return major;
185 }
186
187 OM_uint32
188 gssEapSmStep(OM_uint32 *minor,
189              gss_cred_id_t cred,
190              gss_ctx_id_t ctx,
191              gss_name_t target,
192              gss_OID mech,
193              OM_uint32 reqFlags,
194              OM_uint32 timeReq,
195              gss_channel_bindings_t chanBindings,
196              gss_buffer_t inputToken,
197              gss_buffer_t outputToken,
198              struct gss_eap_sm *sm, /* ordered by state */
199              size_t smCount)
200 {
201     OM_uint32 major, tmpMajor, tmpMinor;
202     gss_buffer_desc unwrappedInputToken = GSS_C_EMPTY_BUFFER;
203     gss_buffer_desc unwrappedOutputToken = GSS_C_EMPTY_BUFFER;
204     gss_buffer_set_t innerInputTokens = GSS_C_NO_BUFFER_SET;
205     gss_buffer_set_t innerOutputTokens = GSS_C_NO_BUFFER_SET;
206     OM_uint32 *inputTokenTypes = NULL, *outputTokenTypes = NULL;
207     unsigned int smFlags = 0;
208     size_t i, j;
209     int initialContextToken = 0;
210
211     assert(smCount > 0);
212
213     *minor = 0;
214
215     outputToken->length = 0;
216     outputToken->value = NULL;
217
218     if (inputToken != GSS_C_NO_BUFFER && inputToken->length != 0) {
219         enum gss_eap_token_type tokType;
220
221         major = gssEapVerifyToken(minor, ctx, inputToken, &tokType,
222                                   &unwrappedInputToken);
223         if (GSS_ERROR(major))
224             goto cleanup;
225
226         if (tokType != TOK_TYPE_ESTABLISH_CONTEXT) {
227             major = GSS_S_DEFECTIVE_TOKEN;
228             *minor = GSSEAP_WRONG_TOK_ID;
229             goto cleanup;
230         }
231     } else if (!CTX_IS_INITIATOR(ctx) || ctx->state != GSSEAP_STATE_INITIAL) {
232         major = GSS_S_DEFECTIVE_TOKEN;
233         *minor = GSSEAP_WRONG_SIZE;
234         goto cleanup;
235     } else {
236         initialContextToken = 1;
237     }
238
239     if (ctx->state == GSSEAP_STATE_ESTABLISHED) {
240         major = GSS_S_BAD_STATUS;
241         *minor = GSSEAP_CONTEXT_ESTABLISHED;
242         goto cleanup;
243     }
244
245     assert(ctx->state < GSSEAP_STATE_ESTABLISHED);
246
247     major = gssEapDecodeInnerTokens(minor, &unwrappedInputToken,
248                                     &innerInputTokens, &inputTokenTypes);
249     if (GSS_ERROR(major))
250         goto cleanup;
251
252     assert(innerInputTokens != GSS_C_NO_BUFFER_SET);
253
254     major = allocInnerTokens(minor, smCount, &innerOutputTokens, &outputTokenTypes);
255     if (GSS_ERROR(major))
256         goto cleanup;
257
258     /* Process all the tokens that are valid for the current state. */
259     for (i = 0; i < smCount; i++) {
260         struct gss_eap_sm *smp = &sm[i];
261         int processToken = 0;
262         gss_buffer_t innerInputToken = GSS_C_NO_BUFFER;
263         OM_uint32 *inputTokenType = NULL;
264         gss_buffer_desc innerOutputToken = GSS_C_EMPTY_BUFFER;
265
266         if ((smp->validStates & ctx->state) == 0)
267             continue;
268
269         if (smp->inputTokenType == ITOK_TYPE_NONE || initialContextToken) {
270             processToken = 1;
271         } else if ((smFlags & SM_FLAG_TRANSITED) == 0) {
272             for (j = 0; j < innerInputTokens->count; j++) {
273                 if ((inputTokenTypes[j] & ITOK_TYPE_MASK) == smp->inputTokenType) {
274                     if (processToken) {
275                         major = GSS_S_DEFECTIVE_TOKEN;
276                         *minor = GSSEAP_DUPLICATE_ITOK;
277                         break;
278                     }
279                     processToken = 1;
280                     innerInputToken = &innerInputTokens->elements[j];
281                     inputTokenType = &inputTokenTypes[j];
282                 }
283             }
284             if (GSS_ERROR(major))
285                 break;
286         }
287
288         if (processToken) {
289             enum gss_eap_state oldState = ctx->state;
290
291             smFlags = 0;
292
293             major = smp->processToken(minor, cred, ctx, target, mech, reqFlags,
294                                       timeReq, chanBindings, innerInputToken,
295                                       &innerOutputToken, &smFlags);
296             if (GSS_ERROR(major))
297                 break;
298
299             if (inputTokenType != NULL)
300                 *inputTokenType |= ITOK_FLAG_VERIFIED;
301             if (ctx->state != oldState)
302                 smFlags |= SM_FLAG_TRANSITED;
303
304             if (innerOutputToken.value != NULL) {
305                 innerOutputTokens->elements[innerOutputTokens->count] = innerOutputToken;
306                 assert(smp->outputTokenType != ITOK_TYPE_NONE);
307                 outputTokenTypes[innerOutputTokens->count] = smp->outputTokenType;
308                 if (smp->itokFlags & SM_ITOK_FLAG_CRITICAL)
309                     outputTokenTypes[innerOutputTokens->count] |= ITOK_FLAG_CRITICAL;
310                 innerOutputTokens->count++;
311             }
312             /*
313              * Break out if explicitly requested, or if we made a state transition
314              * and have some tokens to send.
315              */
316             if ((smFlags & SM_FLAG_STOP_EVAL) ||
317                 ((smFlags & SM_FLAG_TRANSITED) &&
318                  ((smFlags & SM_FLAG_FORCE_SEND_TOKEN) || innerOutputTokens->count != 0))) {
319                 SM_ASSERT_VALID(ctx, major);
320                 break;
321             }
322         } else if ((smp->itokFlags & SM_ITOK_FLAG_REQUIRED) &&
323             smp->inputTokenType != ITOK_TYPE_NONE) {
324             major = GSS_S_DEFECTIVE_TOKEN;
325             *minor = GSSEAP_MISSING_REQUIRED_ITOK;
326             break;
327         }
328     }
329
330     assert(innerOutputTokens->count <= smCount);
331
332     /* Check we understood all critical tokens */
333     if (!GSS_ERROR(major)) {
334         for (j = 0; j < innerInputTokens->count; j++) {
335             if ((inputTokenTypes[j] & ITOK_FLAG_CRITICAL) &&
336                 (inputTokenTypes[j] & ITOK_FLAG_VERIFIED) == 0) {
337                 major = GSS_S_UNAVAILABLE;
338                 *minor = GSSEAP_CRIT_ITOK_UNAVAILABLE;
339                 goto cleanup;
340             }
341         }
342     }
343
344     /* Emit an error token if we are the acceptor */
345     if (GSS_ERROR(major)) {
346         if (CTX_IS_INITIATOR(ctx))
347             goto cleanup; /* return error directly to caller */
348
349         /* replace any emitted tokens with error token */
350         gss_release_buffer_set(&tmpMinor, &innerOutputTokens);
351
352         tmpMajor = makeErrorToken(&tmpMinor, major, *minor, &innerOutputTokens);
353         if (GSS_ERROR(tmpMajor)) {
354             major = tmpMajor;
355             *minor = tmpMinor;
356             goto cleanup;
357         }
358
359         if (innerOutputTokens->count != 0)
360             outputTokenTypes[0] = ITOK_TYPE_CONTEXT_ERR | ITOK_FLAG_CRITICAL;
361     }
362
363     /* Format composite output token */
364     if (innerOutputTokens->count != 0 ||            /* inner tokens to send */
365         !CTX_IS_INITIATOR(ctx) ||                   /* any leg acceptor */
366         ctx->state != GSSEAP_STATE_ESTABLISHED) {   /* non-last leg initiator */
367         tmpMajor = gssEapEncodeInnerTokens(&tmpMinor, innerOutputTokens,
368                                            outputTokenTypes, &unwrappedOutputToken);
369         if (tmpMajor == GSS_S_COMPLETE) {
370             tmpMajor = gssEapMakeToken(&tmpMinor, ctx, &unwrappedOutputToken,
371                                        TOK_TYPE_ESTABLISH_CONTEXT, outputToken);
372             if (GSS_ERROR(tmpMajor)) {
373                 major = tmpMajor;
374                 *minor = tmpMinor;
375                 goto cleanup;
376             }
377         }
378     }
379
380     SM_ASSERT_VALID(ctx, major);
381
382 cleanup:
383     gss_release_buffer_set(&tmpMinor, &innerInputTokens);
384     gss_release_buffer_set(&tmpMinor, &innerOutputTokens);
385     if (inputTokenTypes != NULL)
386         GSSEAP_FREE(inputTokenTypes);
387     if (outputTokenTypes != NULL)
388     gss_release_buffer(&tmpMinor, &unwrappedOutputToken);
389         GSSEAP_FREE(outputTokenTypes);
390
391     return major;
392 }