~ubuntu-branches/ubuntu/utopic/moonshot-gss-eap/utopic-backports

« back to all changes in this revision

Viewing changes to mech_eap/util_sm.c

  • Committer: Package Import Robot
  • Author(s): Sam Hartman
  • Date: 2014-09-16 08:38:39 UTC
  • Revision ID: package-import@ubuntu.com-20140916083839-ipqco3thb1wcwvs0
Tags: upstream-0.9.2
ImportĀ upstreamĀ versionĀ 0.9.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (c) 2011, JANET(UK)
 
3
 * All rights reserved.
 
4
 *
 
5
 * Redistribution and use in source and binary forms, with or without
 
6
 * modification, are permitted provided that the following conditions
 
7
 * are met:
 
8
 *
 
9
 * 1. Redistributions of source code must retain the above copyright
 
10
 *    notice, this list of conditions and the following disclaimer.
 
11
 *
 
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.
 
15
 *
 
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.
 
19
 *
 
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
 
30
 * SUCH DAMAGE.
 
31
 */
 
32
 
 
33
/*
 
34
 * Context establishment state machine.
 
35
 */
 
36
 
 
37
#include "gssapiP_eap.h"
 
38
 
 
39
/* private flags */
 
40
#define SM_FLAG_TRANSITED                   0x80000000
 
41
 
 
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)); \
 
46
    } while (0)
 
47
 
 
48
#ifdef GSSEAP_DEBUG
 
49
static const char *
 
50
gssEapStateToString(enum gss_eap_state state)
 
51
{
 
52
    const char *s;
 
53
 
 
54
    switch (state) {
 
55
    case GSSEAP_STATE_INITIAL:
 
56
        s = "INITIAL";
 
57
        break;
 
58
    case GSSEAP_STATE_AUTHENTICATE:
 
59
        s = "AUTHENTICATE";
 
60
        break;
 
61
    case GSSEAP_STATE_INITIATOR_EXTS:
 
62
        s = "INITIATOR_EXTS";
 
63
        break;
 
64
    case GSSEAP_STATE_ACCEPTOR_EXTS:
 
65
        s = "ACCEPTOR_EXTS";
 
66
        break;
 
67
#ifdef GSSEAP_ENABLE_REAUTH
 
68
    case GSSEAP_STATE_REAUTHENTICATE:
 
69
        s = "REAUTHENTICATE";
 
70
        break;
 
71
#endif
 
72
    case GSSEAP_STATE_ESTABLISHED:
 
73
        s = "ESTABLISHED";
 
74
        break;
 
75
    default:
 
76
        s = "INVALID";
 
77
        break;
 
78
    }
 
79
 
 
80
    return s;
 
81
}
 
82
 
 
83
void
 
84
gssEapSmTransition(gss_ctx_id_t ctx, enum gss_eap_state state)
 
85
{
 
86
    GSSEAP_ASSERT(state >= GSSEAP_STATE_INITIAL);
 
87
    GSSEAP_ASSERT(state <= GSSEAP_STATE_ESTABLISHED);
 
88
 
 
89
    fprintf(stderr, "GSS-EAP: state transition %s->%s\n",
 
90
            gssEapStateToString(GSSEAP_SM_STATE(ctx)),
 
91
            gssEapStateToString(state));
 
92
 
 
93
    ctx->state = state;
 
94
}
 
95
#endif /* GSSEAP_DEBUG */
 
96
 
 
97
static OM_uint32
 
98
makeErrorToken(OM_uint32 *minor,
 
99
               OM_uint32 majorStatus,
 
100
               OM_uint32 minorStatus,
 
101
               struct gss_eap_token_buffer_set *token)
 
102
{
 
103
    OM_uint32 major, tmpMinor;
 
104
    unsigned char errorData[8];
 
105
    gss_buffer_desc errorBuffer;
 
106
 
 
107
    GSSEAP_ASSERT(GSS_ERROR(majorStatus));
 
108
 
 
109
    /*
 
110
     * Only return error codes that the initiator could have caused,
 
111
     * to avoid information leakage.
 
112
     */
 
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;
 
119
    }
 
120
 
 
121
    minorStatus -= ERROR_TABLE_BASE_eapg;
 
122
 
 
123
    store_uint32_be(majorStatus, &errorData[0]);
 
124
    store_uint32_be(minorStatus, &errorData[4]);
 
125
 
 
126
    major = gssEapAllocInnerTokens(&tmpMinor, 1, token);
 
127
    if (GSS_ERROR(major)) {
 
128
        *minor = tmpMinor;
 
129
        return major;
 
130
    }
 
131
 
 
132
    errorBuffer.length = sizeof(errorData);
 
133
    errorBuffer.value = errorData;
 
134
 
 
135
    major = duplicateBuffer(&tmpMinor, &errorBuffer, &token->buffers.elements[0]);
 
136
    if (GSS_ERROR(major)) {
 
137
        gssEapReleaseInnerTokens(&tmpMinor, token, 1);
 
138
        *minor = tmpMinor;
 
139
        return major;
 
140
    }
 
141
 
 
142
    token->buffers.count = 1;
 
143
    token->types[0] = ITOK_TYPE_CONTEXT_ERR | ITOK_FLAG_CRITICAL;
 
144
 
 
145
    *minor = 0;
 
146
    return GSS_S_COMPLETE;
 
147
}
 
148
 
 
149
OM_uint32
 
150
gssEapSmStep(OM_uint32 *minor,
 
151
             gss_cred_id_t cred,
 
152
             gss_ctx_id_t ctx,
 
153
             gss_name_t target,
 
154
             gss_OID mech,
 
155
             OM_uint32 reqFlags,
 
156
             OM_uint32 timeReq,
 
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 */
 
161
             size_t smCount)
 
162
{
 
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;
 
169
    size_t i, j;
 
170
    int initialContextToken = 0;
 
171
    enum gss_eap_token_type tokType;
 
172
 
 
173
    GSSEAP_ASSERT(smCount > 0);
 
174
 
 
175
    *minor = 0;
 
176
 
 
177
    outputToken->length = 0;
 
178
    outputToken->value = NULL;
 
179
 
 
180
    if (inputToken != GSS_C_NO_BUFFER && inputToken->length != 0) {
 
181
        major = gssEapVerifyToken(minor, ctx, inputToken, &tokType,
 
182
                                  &unwrappedInputToken);
 
183
        if (GSS_ERROR(major))
 
184
            goto cleanup;
 
185
 
 
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;
 
190
            goto cleanup;
 
191
        }
 
192
    } else if (!CTX_IS_INITIATOR(ctx) || ctx->state != GSSEAP_STATE_INITIAL) {
 
193
        major = GSS_S_DEFECTIVE_TOKEN;
 
194
        *minor = GSSEAP_WRONG_SIZE;
 
195
        goto cleanup;
 
196
    } else {
 
197
        initialContextToken = 1;
 
198
    }
 
199
 
 
200
    if (CTX_IS_ESTABLISHED(ctx)) {
 
201
        major = GSS_S_BAD_STATUS;
 
202
        *minor = GSSEAP_CONTEXT_ESTABLISHED;
 
203
        goto cleanup;
 
204
    }
 
205
 
 
206
    GSSEAP_ASSERT(ctx->state < GSSEAP_STATE_ESTABLISHED);
 
207
 
 
208
    major = gssEapDecodeInnerTokens(minor, &unwrappedInputToken, &inputTokens);
 
209
    if (GSS_ERROR(major))
 
210
        goto cleanup;
 
211
 
 
212
    major = gssEapAllocInnerTokens(minor, smCount, &outputTokens);
 
213
    if (GSS_ERROR(major))
 
214
        goto cleanup;
 
215
 
 
216
    ctx->inputTokens = &inputTokens;
 
217
    ctx->outputTokens = &outputTokens;
 
218
 
 
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;
 
226
 
 
227
        if ((smp->validStates & ctx->state) == 0)
 
228
            continue;
 
229
 
 
230
        /*
 
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
 
238
         * not emit a token.
 
239
         */
 
240
        if (smp->inputTokenType == ITOK_TYPE_NONE || initialContextToken) {
 
241
            processToken = 1;
 
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) {
 
246
                    if (processToken) {
 
247
                        /* Check for duplicate inner tokens */
 
248
                        major = GSS_S_DEFECTIVE_TOKEN;
 
249
                        *minor = GSSEAP_DUPLICATE_ITOK;
 
250
                        break;
 
251
                    }
 
252
                    processToken = 1;
 
253
                    innerInputToken = &inputTokens.buffers.elements[j];
 
254
                    inputTokenType = &inputTokens.types[j];
 
255
                }
 
256
            }
 
257
            if (GSS_ERROR(major))
 
258
                break;
 
259
        }
 
260
 
 
261
        if (processToken) {
 
262
            enum gss_eap_state oldState = ctx->state;
 
263
 
 
264
            smFlags = 0;
 
265
            if (inputTokenType != NULL && (*inputTokenType & ITOK_FLAG_CRITICAL))
 
266
                smFlags |= SM_FLAG_INPUT_TOKEN_CRITICAL;
 
267
 
 
268
            major = smp->processToken(minor, cred, ctx, target, mech, reqFlags,
 
269
                                      timeReq, chanBindings, innerInputToken,
 
270
                                      &innerOutputToken, &smFlags);
 
271
            if (GSS_ERROR(major))
 
272
                break;
 
273
 
 
274
            if (inputTokenType != NULL)
 
275
                *inputTokenType |= ITOK_FLAG_VERIFIED;
 
276
            if (ctx->state < oldState)
 
277
                i = 0; /* restart */
 
278
            else if (ctx->state != oldState)
 
279
                smFlags |= SM_FLAG_TRANSITED;
 
280
 
 
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++;
 
288
            }
 
289
            /*
 
290
             * Break out if we made a state transition and have some tokens to send.
 
291
             */
 
292
            if ((smFlags & SM_FLAG_TRANSITED) &&
 
293
                 ((smFlags & SM_FLAG_FORCE_SEND_TOKEN) || outputTokens.buffers.count != 0)) {
 
294
                SM_ASSERT_VALID(ctx, major);
 
295
                break;
 
296
            }
 
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;
 
302
            break;
 
303
        }
 
304
    }
 
305
 
 
306
    GSSEAP_ASSERT(outputTokens.buffers.count <= smCount);
 
307
 
 
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;
 
315
                goto cleanup;
 
316
            }
 
317
        }
 
318
    }
 
319
 
 
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 */
 
324
 
 
325
        /* replace any emitted tokens with error token */
 
326
        gssEapReleaseInnerTokens(&tmpMinor, &outputTokens, 1);
 
327
 
 
328
        tmpMajor = makeErrorToken(&tmpMinor, major, *minor, &outputTokens);
 
329
        if (GSS_ERROR(tmpMajor)) {
 
330
            major = tmpMajor;
 
331
            *minor = tmpMinor;
 
332
            goto cleanup;
 
333
        }
 
334
    }
 
335
 
 
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;
 
344
            else
 
345
                tokType = TOK_TYPE_ACCEPTOR_CONTEXT;
 
346
 
 
347
            tmpMajor = gssEapMakeToken(&tmpMinor, ctx, &unwrappedOutputToken,
 
348
                                       tokType, outputToken);
 
349
            if (GSS_ERROR(tmpMajor)) {
 
350
                major = tmpMajor;
 
351
                *minor = tmpMinor;
 
352
                goto cleanup;
 
353
            }
 
354
        }
 
355
    }
 
356
 
 
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)));
 
359
 
 
360
    SM_ASSERT_VALID(ctx, major);
 
361
 
 
362
cleanup:
 
363
    gssEapReleaseInnerTokens(&tmpMinor, &inputTokens, 0);
 
364
    gssEapReleaseInnerTokens(&tmpMinor, &inputTokens, 1);
 
365
 
 
366
    gss_release_buffer(&tmpMinor, &unwrappedOutputToken);
 
367
 
 
368
    ctx->inputTokens = NULL;
 
369
    ctx->outputTokens = NULL;
 
370
 
 
371
    return major;
 
372
}