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 gssEapSmTransition(gss_ctx_id_t ctx, enum gss_eap_state state)
74 assert(state > ctx->state);
75 assert(state <= GSSEAP_STATE_ESTABLISHED);
78 fprintf(stderr, "GSS-EAP: state transition %s->%s\n",
79 gssEapStateToString(ctx->state), gssEapStateToString(state));
86 makeErrorToken(OM_uint32 *minor,
87 OM_uint32 majorStatus,
88 OM_uint32 minorStatus,
89 gss_buffer_set_t *outputToken)
91 unsigned char errorData[8];
92 gss_buffer_desc errorBuffer;
94 assert(GSS_ERROR(majorStatus));
97 * Only return error codes that the initiator could have caused,
98 * to avoid information leakage.
100 if (IS_RADIUS_ERROR(minorStatus)) {
101 /* Squash RADIUS error codes */
102 minorStatus = GSSEAP_RADIUS_PROT_FAILURE;
103 } else if (!IS_WIRE_ERROR(minorStatus)) {
104 /* Don't return non-wire error codes */
105 return GSS_S_COMPLETE;
108 minorStatus -= ERROR_TABLE_BASE_eapg;
110 store_uint32_be(majorStatus, &errorData[0]);
111 store_uint32_be(minorStatus, &errorData[4]);
113 errorBuffer.length = sizeof(errorData);
114 errorBuffer.value = errorData;
116 return gss_add_buffer_set_member(minor, &errorBuffer, outputToken);
120 gssEapSmStep(OM_uint32 *minor,
127 gss_channel_bindings_t chanBindings,
128 gss_buffer_t inputToken,
129 gss_buffer_t outputToken,
130 struct gss_eap_sm *sm,
133 OM_uint32 major, tmpMajor, tmpMinor;
134 gss_buffer_desc unwrappedInputToken = GSS_C_EMPTY_BUFFER;
135 gss_buffer_desc unwrappedOutputToken = GSS_C_EMPTY_BUFFER;
136 gss_buffer_set_t innerInputTokens = GSS_C_NO_BUFFER_SET;
137 gss_buffer_set_t innerOutputTokens = GSS_C_NO_BUFFER_SET;
138 OM_uint32 *inputTokenTypes = NULL, *outputTokenTypes = NULL;
139 unsigned int smFlags = 0;
141 int initialContextToken = 0;
147 outputToken->length = 0;
148 outputToken->value = NULL;
150 if (inputToken != GSS_C_NO_BUFFER && inputToken->length != 0) {
151 enum gss_eap_token_type tokType;
153 major = gssEapVerifyToken(minor, ctx, inputToken, &tokType,
154 &unwrappedInputToken);
155 if (GSS_ERROR(major))
158 if (tokType != TOK_TYPE_ESTABLISH_CONTEXT) {
159 major = GSS_S_DEFECTIVE_TOKEN;
160 *minor = GSSEAP_WRONG_TOK_ID;
163 } else if (!CTX_IS_INITIATOR(ctx) || ctx->state != GSSEAP_STATE_INITIAL) {
164 major = GSS_S_DEFECTIVE_TOKEN;
165 *minor = GSSEAP_WRONG_SIZE;
168 initialContextToken = 1;
171 if (ctx->state == GSSEAP_STATE_ESTABLISHED) {
172 major = GSS_S_BAD_STATUS;
173 *minor = GSSEAP_CONTEXT_ESTABLISHED;
177 assert(ctx->state < GSSEAP_STATE_ESTABLISHED);
179 major = gssEapDecodeInnerTokens(minor, &unwrappedInputToken,
180 &innerInputTokens, &inputTokenTypes);
181 if (GSS_ERROR(major))
184 assert(innerInputTokens != GSS_C_NO_BUFFER_SET);
186 major = gss_create_empty_buffer_set(minor, &innerOutputTokens);
187 if (GSS_ERROR(major))
190 assert(innerOutputTokens->count == 0);
191 assert(innerOutputTokens->elements == NULL);
193 innerOutputTokens->elements = (gss_buffer_desc *)GSSEAP_CALLOC(smCount,
194 sizeof(gss_buffer_desc));
195 if (innerOutputTokens->elements == NULL) {
196 major = GSS_S_FAILURE;
201 outputTokenTypes = (OM_uint32 *)GSSEAP_CALLOC(smCount, sizeof(OM_uint32));
202 if (outputTokenTypes == NULL) {
203 major = GSS_S_FAILURE;
209 * Process all the tokens that are valid for the current state. If
210 * the processToken function returns GSS_S_COMPLETE, the state is
211 * advanced until there is a token to send or the ESTABLISHED state
215 major = GSS_S_COMPLETE;
217 for (i = 0; i < smCount; i++) {
218 struct gss_eap_sm *smp = &sm[i];
219 int processToken = 0;
220 gss_buffer_t innerInputToken = GSS_C_NO_BUFFER;
221 OM_uint32 *inputTokenType = NULL;
222 gss_buffer_desc innerOutputToken = GSS_C_EMPTY_BUFFER;
224 if ((smp->validStates & ctx->state) == 0)
227 if (smp->inputTokenType == ITOK_TYPE_NONE || initialContextToken) {
229 } else if ((smFlags & SM_FLAG_TRANSITION) == 0) {
230 for (j = 0; j < innerInputTokens->count; j++) {
231 if ((inputTokenTypes[j] & ITOK_TYPE_MASK) == smp->inputTokenType) {
233 if (innerInputToken != GSS_C_NO_BUFFER) {
234 major = GSS_S_DEFECTIVE_TOKEN;
235 *minor = GSSEAP_DUPLICATE_ITOK;
239 innerInputToken = &innerInputTokens->elements[j];
240 inputTokenType = &inputTokenTypes[j];
245 fprintf(stderr, "GSS-EAP: state %d processToken %d inputTokenType %08x "
246 "innerInputToken %p innerOutputTokensCount %zd\n",
247 ctx->state, processToken, smp->inputTokenType,
248 innerInputToken, innerOutputTokens->count);
254 major = smp->processToken(minor, cred, ctx, target, mech, reqFlags,
255 timeReq, chanBindings, innerInputToken,
256 &innerOutputToken, &smFlags);
257 if (GSS_ERROR(major))
260 if (inputTokenType != NULL)
261 *inputTokenType |= ITOK_FLAG_VERIFIED;
263 if (innerOutputToken.value != NULL) {
264 innerOutputTokens->elements[innerOutputTokens->count] = innerOutputToken;
265 assert(smp->outputTokenType != ITOK_TYPE_NONE);
266 outputTokenTypes[innerOutputTokens->count] = smp->outputTokenType;
267 if (smp->itokFlags & SM_ITOK_FLAG_CRITICAL)
268 outputTokenTypes[innerOutputTokens->count] |= ITOK_FLAG_CRITICAL;
269 innerOutputTokens->count++;
271 if (smFlags & SM_FLAG_STOP_EVAL)
273 } else if ((smp->itokFlags & SM_ITOK_FLAG_REQUIRED) &&
274 smp->inputTokenType != ITOK_TYPE_NONE) {
275 major = GSS_S_DEFECTIVE_TOKEN;
276 *minor = GSSEAP_MISSING_REQUIRED_ITOK;
281 if (GSS_ERROR(major) || (smFlags & SM_FLAG_TRANSITION) == 0)
284 gssEapSmTransition(ctx, GSSEAP_STATE_NEXT(ctx->state));
286 if (innerOutputTokens->count != 0 || (smFlags & SM_FLAG_FORCE_SEND_TOKEN)) {
287 assert(major == GSS_S_CONTINUE_NEEDED || ctx->state == GSSEAP_STATE_ESTABLISHED);
288 break; /* send any tokens if we have them */
290 } while (ctx->state != GSSEAP_STATE_ESTABLISHED);
292 assert(innerOutputTokens->count <= smCount);
294 /* Check we understood all critical tokens */
295 if (!GSS_ERROR(major)) {
296 for (j = 0; j < innerInputTokens->count; j++) {
297 if ((inputTokenTypes[j] & ITOK_FLAG_CRITICAL) &&
298 (inputTokenTypes[j] & ITOK_FLAG_VERIFIED) == 0) {
299 major = GSS_S_UNAVAILABLE;
300 *minor = GSSEAP_CRIT_ITOK_UNAVAILABLE;
306 /* Emit an error token if we are the acceptor */
307 if (GSS_ERROR(major)) {
308 if (CTX_IS_INITIATOR(ctx))
309 goto cleanup; /* return error directly to caller */
311 /* replace any emitted tokens with error token */
312 gss_release_buffer_set(&tmpMinor, &innerOutputTokens);
314 tmpMajor = makeErrorToken(&tmpMinor, major, *minor, &innerOutputTokens);
315 if (GSS_ERROR(tmpMajor)) {
321 outputTokenTypes[0] = ITOK_TYPE_CONTEXT_ERR | ITOK_FLAG_CRITICAL;
325 for (i = 0; i < innerOutputTokens->count; i++) {
326 fprintf(stderr, "GSS-EAP: type %08x length %zd value %p\n",
328 innerOutputTokens->elements[i].length,
329 innerOutputTokens->elements[i].value);
333 /* Format composite output token */
334 if (innerOutputTokens->count != 0 || /* inner tokens to send */
335 !CTX_IS_INITIATOR(ctx) || /* any leg acceptor */
336 ctx->state != GSSEAP_STATE_ESTABLISHED) { /* non-last leg initiator */
337 tmpMajor = gssEapEncodeInnerTokens(&tmpMinor, innerOutputTokens,
338 outputTokenTypes, &unwrappedOutputToken);
339 if (tmpMajor == GSS_S_COMPLETE) {
340 tmpMajor = gssEapMakeToken(&tmpMinor, ctx, &unwrappedOutputToken,
341 TOK_TYPE_ESTABLISH_CONTEXT, outputToken);
342 if (GSS_ERROR(tmpMajor)) {
350 assert(GSS_ERROR(major) ||
351 (major == GSS_S_CONTINUE_NEEDED && (ctx->state > GSSEAP_STATE_INITIAL && ctx->state < GSSEAP_STATE_ESTABLISHED)) ||
352 (major == GSS_S_COMPLETE && ctx->state == GSSEAP_STATE_ESTABLISHED));
355 gss_release_buffer_set(&tmpMinor, &innerInputTokens);
356 gss_release_buffer_set(&tmpMinor, &innerOutputTokens);
357 if (inputTokenTypes != NULL)
358 GSSEAP_FREE(inputTokenTypes);
359 if (outputTokenTypes != NULL)
360 gss_release_buffer(&tmpMinor, &unwrappedOutputToken);
361 GSSEAP_FREE(outputTokenTypes);