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 #define SM_FLAG_TRANSITED 0x80000000
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)); \
49 gssEapStateToString(enum gss_eap_state state)
54 case GSSEAP_STATE_INITIAL:
57 case GSSEAP_STATE_AUTHENTICATE:
60 case GSSEAP_STATE_INITIATOR_EXTS:
63 case GSSEAP_STATE_ACCEPTOR_EXTS:
66 case GSSEAP_STATE_REAUTHENTICATE:
69 case GSSEAP_STATE_ESTABLISHED:
82 gssEapSmTransition(gss_ctx_id_t ctx, enum gss_eap_state state)
84 assert(state > ctx->state);
85 assert(state <= GSSEAP_STATE_ESTABLISHED);
87 fprintf(stderr, "GSS-EAP: state transition %s->%s\n",
88 gssEapStateToString(ctx->state), gssEapStateToString(state));
95 makeErrorToken(OM_uint32 *minor,
96 OM_uint32 majorStatus,
97 OM_uint32 minorStatus,
98 gss_buffer_set_t *outputToken)
100 unsigned char errorData[8];
101 gss_buffer_desc errorBuffer;
103 assert(GSS_ERROR(majorStatus));
106 * Only return error codes that the initiator could have caused,
107 * to avoid information leakage.
109 if (IS_RADIUS_ERROR(minorStatus)) {
110 /* Squash RADIUS error codes */
111 minorStatus = GSSEAP_RADIUS_PROT_FAILURE;
112 } else if (!IS_WIRE_ERROR(minorStatus)) {
113 /* Don't return non-wire error codes */
114 return GSS_S_COMPLETE;
117 minorStatus -= ERROR_TABLE_BASE_eapg;
119 store_uint32_be(majorStatus, &errorData[0]);
120 store_uint32_be(minorStatus, &errorData[4]);
122 errorBuffer.length = sizeof(errorData);
123 errorBuffer.value = errorData;
125 return gss_add_buffer_set_member(minor, &errorBuffer, outputToken);
129 allocInnerTokens(OM_uint32 *minor,
131 gss_buffer_set_t *pTokens,
132 OM_uint32 **pTokenTypes)
134 OM_uint32 major, tmpMinor;
135 gss_buffer_set_t tokens = GSS_C_NO_BUFFER_SET;
136 OM_uint32 *tokenTypes = NULL;
138 major = gss_create_empty_buffer_set(minor, &tokens);
139 if (GSS_ERROR(major))
142 assert(tokens->count == 0);
143 assert(tokens->elements == NULL);
145 tokens->elements = (gss_buffer_desc *)GSSEAP_CALLOC(count, sizeof(gss_buffer_desc));
146 if (tokens->elements == NULL) {
147 major = GSS_S_FAILURE;
152 tokenTypes = (OM_uint32 *)GSSEAP_CALLOC(count, sizeof(OM_uint32));
153 if (tokenTypes == NULL) {
154 major = GSS_S_FAILURE;
159 major = GSS_S_COMPLETE;
163 if (GSS_ERROR(major)) {
164 gss_release_buffer_set(&tmpMinor, &tokens);
165 tokens = GSS_C_NO_BUFFER_SET;
166 if (tokenTypes != NULL) {
167 GSSEAP_FREE(tokenTypes);
173 *pTokenTypes = tokenTypes;
179 gssEapSmStep(OM_uint32 *minor,
186 gss_channel_bindings_t chanBindings,
187 gss_buffer_t inputToken,
188 gss_buffer_t outputToken,
189 struct gss_eap_sm *sm,
192 OM_uint32 major, tmpMajor, tmpMinor;
193 gss_buffer_desc unwrappedInputToken = GSS_C_EMPTY_BUFFER;
194 gss_buffer_desc unwrappedOutputToken = GSS_C_EMPTY_BUFFER;
195 gss_buffer_set_t innerInputTokens = GSS_C_NO_BUFFER_SET;
196 gss_buffer_set_t innerOutputTokens = GSS_C_NO_BUFFER_SET;
197 OM_uint32 *inputTokenTypes = NULL, *outputTokenTypes = NULL;
198 unsigned int smFlags = 0;
200 int initialContextToken = 0;
206 outputToken->length = 0;
207 outputToken->value = NULL;
209 if (inputToken != GSS_C_NO_BUFFER && inputToken->length != 0) {
210 enum gss_eap_token_type tokType;
212 major = gssEapVerifyToken(minor, ctx, inputToken, &tokType,
213 &unwrappedInputToken);
214 if (GSS_ERROR(major))
217 if (tokType != TOK_TYPE_ESTABLISH_CONTEXT) {
218 major = GSS_S_DEFECTIVE_TOKEN;
219 *minor = GSSEAP_WRONG_TOK_ID;
222 } else if (!CTX_IS_INITIATOR(ctx) || ctx->state != GSSEAP_STATE_INITIAL) {
223 major = GSS_S_DEFECTIVE_TOKEN;
224 *minor = GSSEAP_WRONG_SIZE;
227 initialContextToken = 1;
230 if (ctx->state == GSSEAP_STATE_ESTABLISHED) {
231 major = GSS_S_BAD_STATUS;
232 *minor = GSSEAP_CONTEXT_ESTABLISHED;
236 assert(ctx->state < GSSEAP_STATE_ESTABLISHED);
238 major = gssEapDecodeInnerTokens(minor, &unwrappedInputToken,
239 &innerInputTokens, &inputTokenTypes);
240 if (GSS_ERROR(major))
243 assert(innerInputTokens != GSS_C_NO_BUFFER_SET);
245 major = allocInnerTokens(minor, smCount, &innerOutputTokens, &outputTokenTypes);
246 if (GSS_ERROR(major))
249 /* Process all the tokens that are valid for the current state. */
250 for (i = 0; i < smCount; i++) {
251 struct gss_eap_sm *smp = &sm[i];
252 int processToken = 0;
253 gss_buffer_t innerInputToken = GSS_C_NO_BUFFER;
254 OM_uint32 *inputTokenType = NULL;
255 gss_buffer_desc innerOutputToken = GSS_C_EMPTY_BUFFER;
257 if ((smp->validStates & ctx->state) == 0)
260 if (smp->inputTokenType == ITOK_TYPE_NONE || initialContextToken) {
262 } else if ((smFlags & SM_FLAG_TRANSITED) == 0) {
263 for (j = 0; j < innerInputTokens->count; j++) {
264 if ((inputTokenTypes[j] & ITOK_TYPE_MASK) == smp->inputTokenType) {
266 if (innerInputToken != GSS_C_NO_BUFFER) {
267 major = GSS_S_DEFECTIVE_TOKEN;
268 *minor = GSSEAP_DUPLICATE_ITOK;
272 innerInputToken = &innerInputTokens->elements[j];
273 inputTokenType = &inputTokenTypes[j];
278 enum gss_eap_state oldState = ctx->state;
282 major = smp->processToken(minor, cred, ctx, target, mech, reqFlags,
283 timeReq, chanBindings, innerInputToken,
284 &innerOutputToken, &smFlags);
285 if (GSS_ERROR(major))
288 if (inputTokenType != NULL)
289 *inputTokenType |= ITOK_FLAG_VERIFIED;
290 if (ctx->state != oldState)
291 smFlags |= SM_FLAG_TRANSITED;
293 if (innerOutputToken.value != NULL) {
294 innerOutputTokens->elements[innerOutputTokens->count] = innerOutputToken;
295 assert(smp->outputTokenType != ITOK_TYPE_NONE);
296 outputTokenTypes[innerOutputTokens->count] = smp->outputTokenType;
297 if (smp->itokFlags & SM_ITOK_FLAG_CRITICAL)
298 outputTokenTypes[innerOutputTokens->count] |= ITOK_FLAG_CRITICAL;
299 innerOutputTokens->count++;
302 * Break out if explicitly requested, or if we made a state transition
303 * and have some tokens to send.
305 if ((smFlags & SM_FLAG_STOP_EVAL) ||
306 ((smFlags & SM_FLAG_TRANSITED) &&
307 ((smFlags & SM_FLAG_FORCE_SEND_TOKEN) || innerOutputTokens->count != 0))) {
308 SM_ASSERT_VALID(ctx, major);
311 } else if ((smp->itokFlags & SM_ITOK_FLAG_REQUIRED) &&
312 smp->inputTokenType != ITOK_TYPE_NONE) {
313 major = GSS_S_DEFECTIVE_TOKEN;
314 *minor = GSSEAP_MISSING_REQUIRED_ITOK;
319 assert(innerOutputTokens->count <= smCount);
321 /* Check we understood all critical tokens */
322 if (!GSS_ERROR(major)) {
323 for (j = 0; j < innerInputTokens->count; j++) {
324 if ((inputTokenTypes[j] & ITOK_FLAG_CRITICAL) &&
325 (inputTokenTypes[j] & ITOK_FLAG_VERIFIED) == 0) {
326 major = GSS_S_UNAVAILABLE;
327 *minor = GSSEAP_CRIT_ITOK_UNAVAILABLE;
333 /* Emit an error token if we are the acceptor */
334 if (GSS_ERROR(major)) {
335 if (CTX_IS_INITIATOR(ctx))
336 goto cleanup; /* return error directly to caller */
338 /* replace any emitted tokens with error token */
339 gss_release_buffer_set(&tmpMinor, &innerOutputTokens);
341 tmpMajor = makeErrorToken(&tmpMinor, major, *minor, &innerOutputTokens);
342 if (GSS_ERROR(tmpMajor)) {
348 outputTokenTypes[0] = ITOK_TYPE_CONTEXT_ERR | ITOK_FLAG_CRITICAL;
351 /* Format composite output token */
352 if (innerOutputTokens->count != 0 || /* inner tokens to send */
353 !CTX_IS_INITIATOR(ctx) || /* any leg acceptor */
354 ctx->state != GSSEAP_STATE_ESTABLISHED) { /* non-last leg initiator */
355 tmpMajor = gssEapEncodeInnerTokens(&tmpMinor, innerOutputTokens,
356 outputTokenTypes, &unwrappedOutputToken);
357 if (tmpMajor == GSS_S_COMPLETE) {
358 tmpMajor = gssEapMakeToken(&tmpMinor, ctx, &unwrappedOutputToken,
359 TOK_TYPE_ESTABLISH_CONTEXT, outputToken);
360 if (GSS_ERROR(tmpMajor)) {
368 SM_ASSERT_VALID(ctx, major);
371 gss_release_buffer_set(&tmpMinor, &innerInputTokens);
372 gss_release_buffer_set(&tmpMinor, &innerOutputTokens);
373 if (inputTokenTypes != NULL)
374 GSSEAP_FREE(inputTokenTypes);
375 if (outputTokenTypes != NULL)
376 gss_release_buffer(&tmpMinor, &unwrappedOutputToken);
377 GSSEAP_FREE(outputTokenTypes);