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)); \
50 gssEapStateToString(enum gss_eap_state state)
55 case GSSEAP_STATE_INITIAL:
58 case GSSEAP_STATE_AUTHENTICATE:
61 case GSSEAP_STATE_INITIATOR_EXTS:
64 case GSSEAP_STATE_ACCEPTOR_EXTS:
67 case GSSEAP_STATE_REAUTHENTICATE:
70 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));
92 #endif /* GSSEAP_DEBUG */
95 makeErrorToken(OM_uint32 *minor,
96 OM_uint32 majorStatus,
97 OM_uint32 minorStatus,
98 gss_buffer_set_t *outputToken)
101 unsigned char errorData[8];
102 gss_buffer_desc errorBuffer;
104 assert(GSS_ERROR(majorStatus));
106 major = gss_create_empty_buffer_set(minor, outputToken);
107 if (GSS_ERROR(major))
111 * Only return error codes that the initiator could have caused,
112 * to avoid information leakage.
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;
122 minorStatus -= ERROR_TABLE_BASE_eapg;
124 store_uint32_be(majorStatus, &errorData[0]);
125 store_uint32_be(minorStatus, &errorData[4]);
127 errorBuffer.length = sizeof(errorData);
128 errorBuffer.value = errorData;
130 major = gss_add_buffer_set_member(minor, &errorBuffer, outputToken);
131 if (GSS_ERROR(major))
134 return GSS_S_COMPLETE;
138 allocInnerTokens(OM_uint32 *minor,
140 gss_buffer_set_t *pTokens,
141 OM_uint32 **pTokenTypes)
143 OM_uint32 major, tmpMinor;
144 gss_buffer_set_t tokens = GSS_C_NO_BUFFER_SET;
145 OM_uint32 *tokenTypes = NULL;
147 major = gss_create_empty_buffer_set(minor, &tokens);
148 if (GSS_ERROR(major))
151 assert(tokens->count == 0);
152 assert(tokens->elements == NULL);
154 tokens->elements = (gss_buffer_desc *)GSSEAP_CALLOC(count, sizeof(gss_buffer_desc));
155 if (tokens->elements == NULL) {
156 major = GSS_S_FAILURE;
161 tokenTypes = (OM_uint32 *)GSSEAP_CALLOC(count, sizeof(OM_uint32));
162 if (tokenTypes == NULL) {
163 major = GSS_S_FAILURE;
168 major = GSS_S_COMPLETE;
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);
182 *pTokenTypes = tokenTypes;
188 gssEapSmStep(OM_uint32 *minor,
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 */
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;
209 int initialContextToken = 0;
215 outputToken->length = 0;
216 outputToken->value = NULL;
218 if (inputToken != GSS_C_NO_BUFFER && inputToken->length != 0) {
219 enum gss_eap_token_type tokType;
221 major = gssEapVerifyToken(minor, ctx, inputToken, &tokType,
222 &unwrappedInputToken);
223 if (GSS_ERROR(major))
226 if (tokType != TOK_TYPE_ESTABLISH_CONTEXT) {
227 major = GSS_S_DEFECTIVE_TOKEN;
228 *minor = GSSEAP_WRONG_TOK_ID;
231 } else if (!CTX_IS_INITIATOR(ctx) || ctx->state != GSSEAP_STATE_INITIAL) {
232 major = GSS_S_DEFECTIVE_TOKEN;
233 *minor = GSSEAP_WRONG_SIZE;
236 initialContextToken = 1;
239 if (ctx->state == GSSEAP_STATE_ESTABLISHED) {
240 major = GSS_S_BAD_STATUS;
241 *minor = GSSEAP_CONTEXT_ESTABLISHED;
245 assert(ctx->state < GSSEAP_STATE_ESTABLISHED);
247 major = gssEapDecodeInnerTokens(minor, &unwrappedInputToken,
248 &innerInputTokens, &inputTokenTypes);
249 if (GSS_ERROR(major))
252 assert(innerInputTokens != GSS_C_NO_BUFFER_SET);
254 major = allocInnerTokens(minor, smCount, &innerOutputTokens, &outputTokenTypes);
255 if (GSS_ERROR(major))
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;
266 if ((smp->validStates & ctx->state) == 0)
270 * We special case the first call to gss_init_sec_context so that
271 * all token providers have the opportunity to generate an initial
272 * context token. Providers where inputTokenType is ITOK_TYPE_NONE
273 * are always called and generally act on state transition boundaries,
274 * for example to advance the state after a series of optional tokens
275 * (as is the case with the extension token exchange) or to generate
276 * a new token after the state was advanced by a provider which did
279 if (smp->inputTokenType == ITOK_TYPE_NONE || initialContextToken) {
281 } else if ((smFlags & SM_FLAG_TRANSITED) == 0) {
282 /* Don't regurgitate a token which belonds to a previous state. */
283 for (j = 0; j < innerInputTokens->count; j++) {
284 if ((inputTokenTypes[j] & ITOK_TYPE_MASK) == smp->inputTokenType) {
286 /* Check for duplicate inner tokens */
287 major = GSS_S_DEFECTIVE_TOKEN;
288 *minor = GSSEAP_DUPLICATE_ITOK;
292 innerInputToken = &innerInputTokens->elements[j];
293 inputTokenType = &inputTokenTypes[j];
296 if (GSS_ERROR(major))
301 enum gss_eap_state oldState = ctx->state;
305 major = smp->processToken(minor, cred, ctx, target, mech, reqFlags,
306 timeReq, chanBindings, innerInputToken,
307 &innerOutputToken, &smFlags);
308 if (GSS_ERROR(major))
311 if (inputTokenType != NULL)
312 *inputTokenType |= ITOK_FLAG_VERIFIED;
313 if (ctx->state != oldState)
314 smFlags |= SM_FLAG_TRANSITED;
316 if (innerOutputToken.value != NULL) {
317 innerOutputTokens->elements[innerOutputTokens->count] = innerOutputToken;
318 assert(smp->outputTokenType != ITOK_TYPE_NONE);
319 outputTokenTypes[innerOutputTokens->count] = smp->outputTokenType;
320 if (smp->itokFlags & SM_ITOK_FLAG_CRITICAL)
321 outputTokenTypes[innerOutputTokens->count] |= ITOK_FLAG_CRITICAL;
322 innerOutputTokens->count++;
325 * Break out if explicitly requested, or if we made a state transition
326 * and have some tokens to send.
328 if ((smFlags & SM_FLAG_STOP_EVAL) ||
329 ((smFlags & SM_FLAG_TRANSITED) &&
330 ((smFlags & SM_FLAG_FORCE_SEND_TOKEN) || innerOutputTokens->count != 0))) {
331 SM_ASSERT_VALID(ctx, major);
334 } else if ((smp->itokFlags & SM_ITOK_FLAG_REQUIRED) &&
335 smp->inputTokenType != ITOK_TYPE_NONE) {
336 /* Check for required inner tokens */
337 major = GSS_S_DEFECTIVE_TOKEN;
338 *minor = GSSEAP_MISSING_REQUIRED_ITOK;
343 assert(innerOutputTokens->count <= smCount);
345 /* Check we understood all critical tokens sent by peer */
346 if (!GSS_ERROR(major)) {
347 for (j = 0; j < innerInputTokens->count; j++) {
348 if ((inputTokenTypes[j] & ITOK_FLAG_CRITICAL) &&
349 (inputTokenTypes[j] & ITOK_FLAG_VERIFIED) == 0) {
350 major = GSS_S_UNAVAILABLE;
351 *minor = GSSEAP_CRIT_ITOK_UNAVAILABLE;
357 /* Optionaly emit an error token if we are the acceptor */
358 if (GSS_ERROR(major)) {
359 if (CTX_IS_INITIATOR(ctx))
360 goto cleanup; /* return error directly to caller */
362 /* replace any emitted tokens with error token */
363 gss_release_buffer_set(&tmpMinor, &innerOutputTokens);
365 tmpMajor = makeErrorToken(&tmpMinor, major, *minor, &innerOutputTokens);
366 if (GSS_ERROR(tmpMajor)) {
372 if (innerOutputTokens->count != 0)
373 outputTokenTypes[0] = ITOK_TYPE_CONTEXT_ERR | ITOK_FLAG_CRITICAL;
376 /* Format output token from inner tokens */
377 if (innerOutputTokens->count != 0 || /* inner tokens to send */
378 !CTX_IS_INITIATOR(ctx) || /* any leg acceptor */
379 ctx->state != GSSEAP_STATE_ESTABLISHED) { /* non-last leg initiator */
380 tmpMajor = gssEapEncodeInnerTokens(&tmpMinor, innerOutputTokens,
381 outputTokenTypes, &unwrappedOutputToken);
382 if (tmpMajor == GSS_S_COMPLETE) {
383 tmpMajor = gssEapMakeToken(&tmpMinor, ctx, &unwrappedOutputToken,
384 TOK_TYPE_ESTABLISH_CONTEXT, outputToken);
385 if (GSS_ERROR(tmpMajor)) {
393 SM_ASSERT_VALID(ctx, major);
396 gss_release_buffer_set(&tmpMinor, &innerInputTokens);
397 gss_release_buffer_set(&tmpMinor, &innerOutputTokens);
398 if (inputTokenTypes != NULL)
399 GSSEAP_FREE(inputTokenTypes);
400 if (outputTokenTypes != NULL)
401 gss_release_buffer(&tmpMinor, &unwrappedOutputToken);
402 GSSEAP_FREE(outputTokenTypes);