2 * Copyright (c) 2011, JANET(UK)
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
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.
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.
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
34 * Context establishment state machine.
37 #include "gssapiP_eap.h"
40 gssEapStateToString(enum gss_eap_state state)
45 case GSSEAP_STATE_INITIAL:
48 case GSSEAP_STATE_AUTHENTICATE:
51 case GSSEAP_STATE_INITIATOR_EXTS:
54 case GSSEAP_STATE_ACCEPTOR_EXTS:
57 case GSSEAP_STATE_REAUTHENTICATE:
60 case GSSEAP_STATE_ESTABLISHED:
72 makeErrorToken(OM_uint32 *minor,
73 OM_uint32 majorStatus,
74 OM_uint32 minorStatus,
75 gss_buffer_set_t *outputToken)
77 unsigned char errorData[8];
78 gss_buffer_desc errorBuffer;
80 assert(GSS_ERROR(majorStatus));
83 * Only return error codes that the initiator could have caused,
84 * to avoid information leakage.
86 if (IS_RADIUS_ERROR(minorStatus)) {
87 /* Squash RADIUS error codes */
88 minorStatus = GSSEAP_RADIUS_PROT_FAILURE;
89 } else if (!IS_WIRE_ERROR(minorStatus)) {
90 /* Don't return non-wire error codes */
91 return GSS_S_COMPLETE;
94 minorStatus -= ERROR_TABLE_BASE_eapg;
96 store_uint32_be(majorStatus, &errorData[0]);
97 store_uint32_be(minorStatus, &errorData[4]);
99 errorBuffer.length = sizeof(errorData);
100 errorBuffer.value = errorData;
102 return gss_add_buffer_set_member(minor, &errorBuffer, outputToken);
106 gssEapSmStep(OM_uint32 *minor,
113 gss_channel_bindings_t chanBindings,
114 gss_buffer_t inputToken,
115 gss_buffer_t outputToken,
116 struct gss_eap_sm *sm,
119 OM_uint32 major, tmpMajor, tmpMinor;
120 gss_buffer_desc unwrappedInputToken = GSS_C_EMPTY_BUFFER;
121 gss_buffer_desc unwrappedOutputToken = GSS_C_EMPTY_BUFFER;
122 gss_buffer_set_t innerInputTokens = GSS_C_NO_BUFFER_SET;
123 gss_buffer_set_t innerOutputTokens = GSS_C_NO_BUFFER_SET;
124 OM_uint32 *inputTokenTypes = NULL, *outputTokenTypes = NULL;
125 unsigned int smFlags = 0;
127 int initialContextToken = 0;
133 outputToken->length = 0;
134 outputToken->value = NULL;
136 if (inputToken != GSS_C_NO_BUFFER && inputToken->length != 0) {
137 enum gss_eap_token_type tokType;
139 major = gssEapVerifyToken(minor, ctx, inputToken, &tokType,
140 &unwrappedInputToken);
141 if (GSS_ERROR(major))
144 if (tokType != TOK_TYPE_ESTABLISH_CONTEXT) {
145 major = GSS_S_DEFECTIVE_TOKEN;
146 *minor = GSSEAP_WRONG_TOK_ID;
149 } else if (!CTX_IS_INITIATOR(ctx) || ctx->state != GSSEAP_STATE_INITIAL) {
150 major = GSS_S_DEFECTIVE_TOKEN;
151 *minor = GSSEAP_WRONG_SIZE;
154 initialContextToken = 1;
157 if (ctx->state == GSSEAP_STATE_ESTABLISHED) {
158 major = GSS_S_BAD_STATUS;
159 *minor = GSSEAP_CONTEXT_ESTABLISHED;
163 assert(ctx->state < GSSEAP_STATE_ESTABLISHED);
165 major = gssEapDecodeInnerTokens(minor, &unwrappedInputToken,
166 &innerInputTokens, &inputTokenTypes);
167 if (GSS_ERROR(major))
170 assert(innerInputTokens != GSS_C_NO_BUFFER_SET);
172 major = gss_create_empty_buffer_set(minor, &innerOutputTokens);
173 if (GSS_ERROR(major))
176 assert(innerOutputTokens->count == 0);
177 assert(innerOutputTokens->elements == NULL);
179 innerOutputTokens->elements = (gss_buffer_desc *)GSSEAP_CALLOC(smCount,
180 sizeof(gss_buffer_desc));
181 if (innerOutputTokens->elements == NULL) {
182 major = GSS_S_FAILURE;
187 outputTokenTypes = (OM_uint32 *)GSSEAP_CALLOC(smCount, sizeof(OM_uint32));
188 if (outputTokenTypes == NULL) {
189 major = GSS_S_FAILURE;
195 * Process all the tokens that are valid for the current state. If
196 * the processToken function returns GSS_S_COMPLETE, the state is
197 * advanced until there is a token to send or the ESTABLISHED state
201 major = GSS_S_COMPLETE;
203 for (i = 0; i < smCount; i++) {
204 struct gss_eap_sm *smp = &sm[i];
205 int processToken = 0;
206 gss_buffer_t innerInputToken = GSS_C_NO_BUFFER;
207 OM_uint32 *inputTokenType = NULL;
208 gss_buffer_desc innerOutputToken = GSS_C_EMPTY_BUFFER;
210 if ((smp->validStates & ctx->state) == 0)
213 if (smp->inputTokenType == ITOK_TYPE_NONE || initialContextToken) {
215 } else if ((smFlags & SM_FLAG_TRANSITION) == 0) {
216 for (j = 0; j < innerInputTokens->count; j++) {
217 if ((inputTokenTypes[j] & ITOK_TYPE_MASK) == smp->inputTokenType) {
219 if (innerInputToken != GSS_C_NO_BUFFER) {
220 major = GSS_S_DEFECTIVE_TOKEN;
221 *minor = GSSEAP_DUPLICATE_ITOK;
225 innerInputToken = &innerInputTokens->elements[j];
226 inputTokenType = &inputTokenTypes[j];
231 fprintf(stderr, "GSS-EAP: state %d processToken %d inputTokenType %08x "
232 "innerInputToken %p innerOutputTokensCount %zd\n",
233 ctx->state, processToken, smp->inputTokenType,
234 innerInputToken, innerOutputTokens->count);
240 major = smp->processToken(minor, cred, ctx, target, mech, reqFlags,
241 timeReq, chanBindings, innerInputToken,
242 &innerOutputToken, &smFlags);
243 if (GSS_ERROR(major))
246 if (inputTokenType != NULL)
247 *inputTokenType |= ITOK_FLAG_VERIFIED;
249 if (innerOutputToken.value != NULL) {
250 innerOutputTokens->elements[innerOutputTokens->count] = innerOutputToken;
251 assert(smp->outputTokenType != ITOK_TYPE_NONE);
252 outputTokenTypes[innerOutputTokens->count] = smp->outputTokenType;
253 if (smp->itokFlags & SM_ITOK_FLAG_CRITICAL)
254 outputTokenTypes[innerOutputTokens->count] |= ITOK_FLAG_CRITICAL;
255 innerOutputTokens->count++;
257 if (smFlags & SM_FLAG_STOP_EVAL)
259 } else if ((smp->itokFlags & SM_ITOK_FLAG_REQUIRED) &&
260 smp->inputTokenType != ITOK_TYPE_NONE) {
261 major = GSS_S_DEFECTIVE_TOKEN;
262 *minor = GSSEAP_MISSING_REQUIRED_ITOK;
267 if (GSS_ERROR(major) || (smFlags & SM_FLAG_TRANSITION) == 0)
270 assert(ctx->state < GSSEAP_STATE_ESTABLISHED);
273 fprintf(stderr, "GSS-EAP: state transition %s->%s\n",
274 gssEapStateToString(ctx->state),
275 gssEapStateToString(GSSEAP_STATE_NEXT(ctx->state)));
278 ctx->state = GSSEAP_STATE_NEXT(ctx->state);
280 if (innerOutputTokens->count != 0 || (smFlags & SM_FLAG_FORCE_SEND_TOKEN)) {
281 assert(major == GSS_S_CONTINUE_NEEDED || ctx->state == GSSEAP_STATE_ESTABLISHED);
282 break; /* send any tokens if we have them */
284 } while (ctx->state != GSSEAP_STATE_ESTABLISHED);
286 assert(innerOutputTokens->count <= smCount);
288 /* Check we understood all critical tokens */
289 if (!GSS_ERROR(major)) {
290 for (j = 0; j < innerInputTokens->count; j++) {
291 if ((inputTokenTypes[j] & ITOK_FLAG_CRITICAL) &&
292 (inputTokenTypes[j] & ITOK_FLAG_VERIFIED) == 0) {
293 major = GSS_S_UNAVAILABLE;
294 *minor = GSSEAP_CRIT_ITOK_UNAVAILABLE;
300 /* Emit an error token if we are the acceptor */
301 if (GSS_ERROR(major)) {
302 if (CTX_IS_INITIATOR(ctx))
303 goto cleanup; /* return error directly to caller */
305 /* replace any emitted tokens with error token */
306 gss_release_buffer_set(&tmpMinor, &innerOutputTokens);
308 tmpMajor = makeErrorToken(&tmpMinor, major, *minor, &innerOutputTokens);
309 if (GSS_ERROR(tmpMajor)) {
315 outputTokenTypes[0] = ITOK_TYPE_CONTEXT_ERR | ITOK_FLAG_CRITICAL;
319 for (i = 0; i < innerOutputTokens->count; i++) {
320 fprintf(stderr, "GSS-EAP: type %08x length %zd value %p\n",
322 innerOutputTokens->elements[i].length,
323 innerOutputTokens->elements[i].value);
327 /* Format composite output token */
328 if (innerOutputTokens->count != 0 || /* inner tokens to send */
329 !CTX_IS_INITIATOR(ctx) || /* any leg acceptor */
330 ctx->state != GSSEAP_STATE_ESTABLISHED) { /* non-last leg initiator */
331 tmpMajor = gssEapEncodeInnerTokens(&tmpMinor, innerOutputTokens,
332 outputTokenTypes, &unwrappedOutputToken);
333 if (tmpMajor == GSS_S_COMPLETE) {
334 tmpMajor = gssEapMakeToken(&tmpMinor, ctx, &unwrappedOutputToken,
335 TOK_TYPE_ESTABLISH_CONTEXT, outputToken);
336 if (GSS_ERROR(tmpMajor)) {
344 assert(GSS_ERROR(major) ||
345 (major == GSS_S_CONTINUE_NEEDED && (ctx->state > GSSEAP_STATE_INITIAL && ctx->state < GSSEAP_STATE_ESTABLISHED)) ||
346 (major == GSS_S_COMPLETE && ctx->state == GSSEAP_STATE_ESTABLISHED));
349 gss_release_buffer_set(&tmpMinor, &innerInputTokens);
350 gss_release_buffer_set(&tmpMinor, &innerOutputTokens);
351 if (inputTokenTypes != NULL)
352 GSSEAP_FREE(inputTokenTypes);
353 if (outputTokenTypes != NULL)
354 gss_release_buffer(&tmpMinor, &unwrappedOutputToken);
355 GSSEAP_FREE(outputTokenTypes);