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
GSSEAP_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
#ifdef GSSEAP_ENABLE_REAUTH
68
case GSSEAP_STATE_REAUTHENTICATE:
72
case GSSEAP_STATE_ESTABLISHED:
84
gssEapSmTransition(gss_ctx_id_t ctx, enum gss_eap_state state)
86
GSSEAP_ASSERT(state >= GSSEAP_STATE_INITIAL);
87
GSSEAP_ASSERT(state <= GSSEAP_STATE_ESTABLISHED);
89
fprintf(stderr, "GSS-EAP: state transition %s->%s\n",
90
gssEapStateToString(GSSEAP_SM_STATE(ctx)),
91
gssEapStateToString(state));
95
#endif /* GSSEAP_DEBUG */
98
makeErrorToken(OM_uint32 *minor,
99
OM_uint32 majorStatus,
100
OM_uint32 minorStatus,
101
struct gss_eap_token_buffer_set *token)
103
OM_uint32 major, tmpMinor;
104
unsigned char errorData[8];
105
gss_buffer_desc errorBuffer;
107
GSSEAP_ASSERT(GSS_ERROR(majorStatus));
110
* Only return error codes that the initiator could have caused,
111
* to avoid information leakage.
113
if (IS_RADIUS_ERROR(minorStatus)) {
114
/* Squash RADIUS error codes */
115
minorStatus = GSSEAP_RADIUS_PROT_FAILURE;
116
} else if (!IS_WIRE_ERROR(minorStatus)) {
117
/* Don't return non-wire error codes */
118
return GSS_S_COMPLETE;
121
minorStatus -= ERROR_TABLE_BASE_eapg;
123
store_uint32_be(majorStatus, &errorData[0]);
124
store_uint32_be(minorStatus, &errorData[4]);
126
major = gssEapAllocInnerTokens(&tmpMinor, 1, token);
127
if (GSS_ERROR(major)) {
132
errorBuffer.length = sizeof(errorData);
133
errorBuffer.value = errorData;
135
major = duplicateBuffer(&tmpMinor, &errorBuffer, &token->buffers.elements[0]);
136
if (GSS_ERROR(major)) {
137
gssEapReleaseInnerTokens(&tmpMinor, token, 1);
142
token->buffers.count = 1;
143
token->types[0] = ITOK_TYPE_CONTEXT_ERR | ITOK_FLAG_CRITICAL;
146
return GSS_S_COMPLETE;
150
gssEapSmStep(OM_uint32 *minor,
157
gss_channel_bindings_t chanBindings,
158
gss_buffer_t inputToken,
159
gss_buffer_t outputToken,
160
struct gss_eap_sm *sm, /* ordered by state */
163
OM_uint32 major, tmpMajor, tmpMinor;
164
struct gss_eap_token_buffer_set inputTokens = { { 0, GSS_C_NO_BUFFER }, NULL };
165
struct gss_eap_token_buffer_set outputTokens = { { 0, GSS_C_NO_BUFFER }, NULL };
166
gss_buffer_desc unwrappedInputToken = GSS_C_EMPTY_BUFFER;
167
gss_buffer_desc unwrappedOutputToken = GSS_C_EMPTY_BUFFER;
168
unsigned int smFlags = 0;
170
int initialContextToken = 0;
171
enum gss_eap_token_type tokType;
173
GSSEAP_ASSERT(smCount > 0);
177
outputToken->length = 0;
178
outputToken->value = NULL;
180
if (inputToken != GSS_C_NO_BUFFER && inputToken->length != 0) {
181
major = gssEapVerifyToken(minor, ctx, inputToken, &tokType,
182
&unwrappedInputToken);
183
if (GSS_ERROR(major))
186
if (tokType != (CTX_IS_INITIATOR(ctx)
187
? TOK_TYPE_ACCEPTOR_CONTEXT : TOK_TYPE_INITIATOR_CONTEXT)) {
188
major = GSS_S_DEFECTIVE_TOKEN;
189
*minor = GSSEAP_WRONG_TOK_ID;
192
} else if (!CTX_IS_INITIATOR(ctx) || ctx->state != GSSEAP_STATE_INITIAL) {
193
major = GSS_S_DEFECTIVE_TOKEN;
194
*minor = GSSEAP_WRONG_SIZE;
197
initialContextToken = 1;
200
if (CTX_IS_ESTABLISHED(ctx)) {
201
major = GSS_S_BAD_STATUS;
202
*minor = GSSEAP_CONTEXT_ESTABLISHED;
206
GSSEAP_ASSERT(ctx->state < GSSEAP_STATE_ESTABLISHED);
208
major = gssEapDecodeInnerTokens(minor, &unwrappedInputToken, &inputTokens);
209
if (GSS_ERROR(major))
212
major = gssEapAllocInnerTokens(minor, smCount, &outputTokens);
213
if (GSS_ERROR(major))
216
ctx->inputTokens = &inputTokens;
217
ctx->outputTokens = &outputTokens;
219
/* Process all the tokens that are valid for the current state. */
220
for (i = 0; i < smCount; i++) {
221
struct gss_eap_sm *smp = &sm[i];
222
int processToken = 0;
223
gss_buffer_t innerInputToken = GSS_C_NO_BUFFER;
224
OM_uint32 *inputTokenType = NULL;
225
gss_buffer_desc innerOutputToken = GSS_C_EMPTY_BUFFER;
227
if ((smp->validStates & ctx->state) == 0)
231
* We special case the first call to gss_init_sec_context so that
232
* all token providers have the opportunity to generate an initial
233
* context token. Providers where inputTokenType is ITOK_TYPE_NONE
234
* are always called and generally act on state transition boundaries,
235
* for example to advance the state after a series of optional tokens
236
* (as is the case with the extension token exchange) or to generate
237
* a new token after the state was advanced by a provider which did
240
if (smp->inputTokenType == ITOK_TYPE_NONE || initialContextToken) {
242
} else if ((smFlags & SM_FLAG_TRANSITED) == 0) {
243
/* Don't regurgitate a token which belonds to a previous state. */
244
for (j = 0; j < inputTokens.buffers.count; j++) {
245
if ((inputTokens.types[j] & ITOK_TYPE_MASK) == smp->inputTokenType) {
247
/* Check for duplicate inner tokens */
248
major = GSS_S_DEFECTIVE_TOKEN;
249
*minor = GSSEAP_DUPLICATE_ITOK;
253
innerInputToken = &inputTokens.buffers.elements[j];
254
inputTokenType = &inputTokens.types[j];
257
if (GSS_ERROR(major))
262
enum gss_eap_state oldState = ctx->state;
265
if (inputTokenType != NULL && (*inputTokenType & ITOK_FLAG_CRITICAL))
266
smFlags |= SM_FLAG_INPUT_TOKEN_CRITICAL;
268
major = smp->processToken(minor, cred, ctx, target, mech, reqFlags,
269
timeReq, chanBindings, innerInputToken,
270
&innerOutputToken, &smFlags);
271
if (GSS_ERROR(major))
274
if (inputTokenType != NULL)
275
*inputTokenType |= ITOK_FLAG_VERIFIED;
276
if (ctx->state < oldState)
278
else if (ctx->state != oldState)
279
smFlags |= SM_FLAG_TRANSITED;
281
if (innerOutputToken.value != NULL) {
282
outputTokens.buffers.elements[outputTokens.buffers.count] = innerOutputToken;
283
GSSEAP_ASSERT(smp->outputTokenType != ITOK_TYPE_NONE);
284
outputTokens.types[outputTokens.buffers.count] = smp->outputTokenType;
285
if (smFlags & SM_FLAG_OUTPUT_TOKEN_CRITICAL)
286
outputTokens.types[outputTokens.buffers.count] |= ITOK_FLAG_CRITICAL;
287
outputTokens.buffers.count++;
290
* Break out if we made a state transition and have some tokens to send.
292
if ((smFlags & SM_FLAG_TRANSITED) &&
293
((smFlags & SM_FLAG_FORCE_SEND_TOKEN) || outputTokens.buffers.count != 0)) {
294
SM_ASSERT_VALID(ctx, major);
297
} else if ((smp->itokFlags & SM_ITOK_FLAG_REQUIRED) &&
298
smp->inputTokenType != ITOK_TYPE_NONE) {
299
/* Check for required inner tokens */
300
major = GSS_S_DEFECTIVE_TOKEN;
301
*minor = GSSEAP_MISSING_REQUIRED_ITOK;
306
GSSEAP_ASSERT(outputTokens.buffers.count <= smCount);
308
/* Check we understood all critical tokens sent by peer */
309
if (!GSS_ERROR(major)) {
310
for (j = 0; j < inputTokens.buffers.count; j++) {
311
if ((inputTokens.types[j] & ITOK_FLAG_CRITICAL) &&
312
(inputTokens.types[j] & ITOK_FLAG_VERIFIED) == 0) {
313
major = GSS_S_UNAVAILABLE;
314
*minor = GSSEAP_CRIT_ITOK_UNAVAILABLE;
320
/* Optionaly emit an error token if we are the acceptor */
321
if (GSS_ERROR(major)) {
322
if (CTX_IS_INITIATOR(ctx))
323
goto cleanup; /* return error directly to caller */
325
/* replace any emitted tokens with error token */
326
gssEapReleaseInnerTokens(&tmpMinor, &outputTokens, 1);
328
tmpMajor = makeErrorToken(&tmpMinor, major, *minor, &outputTokens);
329
if (GSS_ERROR(tmpMajor)) {
336
/* Format output token from inner tokens */
337
if (outputTokens.buffers.count != 0 || /* inner tokens to send */
338
!CTX_IS_INITIATOR(ctx) || /* any leg acceptor */
339
!CTX_IS_ESTABLISHED(ctx)) { /* non-last leg initiator */
340
tmpMajor = gssEapEncodeInnerTokens(&tmpMinor, &outputTokens, &unwrappedOutputToken);
341
if (tmpMajor == GSS_S_COMPLETE) {
342
if (CTX_IS_INITIATOR(ctx))
343
tokType = TOK_TYPE_INITIATOR_CONTEXT;
345
tokType = TOK_TYPE_ACCEPTOR_CONTEXT;
347
tmpMajor = gssEapMakeToken(&tmpMinor, ctx, &unwrappedOutputToken,
348
tokType, outputToken);
349
if (GSS_ERROR(tmpMajor)) {
357
/* If the context is established, empty tokens only to be emitted by initiator */
358
GSSEAP_ASSERT(!CTX_IS_ESTABLISHED(ctx) || ((outputToken->length == 0) == CTX_IS_INITIATOR(ctx)));
360
SM_ASSERT_VALID(ctx, major);
363
gssEapReleaseInnerTokens(&tmpMinor, &inputTokens, 0);
364
gssEapReleaseInnerTokens(&tmpMinor, &inputTokens, 1);
366
gss_release_buffer(&tmpMinor, &unwrappedOutputToken);
368
ctx->inputTokens = NULL;
369
ctx->outputTokens = NULL;