~zulcss/samba/server-dailies-3.4

« back to all changes in this revision

Viewing changes to source4/heimdal/lib/gssapi/spnego/accept_sec_context.c

  • Committer: Chuck Short
  • Date: 2010-09-28 20:38:39 UTC
  • Revision ID: zulcss@ubuntu.com-20100928203839-pgjulytsi9ue63x1
Initial version

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan
 
3
 * (Royal Institute of Technology, Stockholm, Sweden).
 
4
 * Portions Copyright (c) 2004 PADL Software Pty Ltd.
 
5
 *
 
6
 * Redistribution and use in source and binary forms, with or without
 
7
 * modification, are permitted provided that the following conditions
 
8
 * are met:
 
9
 *
 
10
 * 1. Redistributions of source code must retain the above copyright
 
11
 *    notice, this list of conditions and the following disclaimer.
 
12
 *
 
13
 * 2. Redistributions in binary form must reproduce the above copyright
 
14
 *    notice, this list of conditions and the following disclaimer in the
 
15
 *    documentation and/or other materials provided with the distribution.
 
16
 *
 
17
 * 3. Neither the name of the Institute nor the names of its contributors
 
18
 *    may be used to endorse or promote products derived from this software
 
19
 *    without specific prior written permission.
 
20
 *
 
21
 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
 
22
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 
23
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 
24
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
 
25
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 
26
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 
27
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 
28
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 
29
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 
30
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 
31
 * SUCH DAMAGE.
 
32
 */
 
33
 
 
34
#include "spnego/spnego_locl.h"
 
35
 
 
36
RCSID("$Id$");
 
37
 
 
38
static OM_uint32
 
39
send_reject (OM_uint32 *minor_status,
 
40
             gss_buffer_t output_token)
 
41
{
 
42
    NegotiationToken nt;
 
43
    size_t size;
 
44
 
 
45
    nt.element = choice_NegotiationToken_negTokenResp;
 
46
 
 
47
    ALLOC(nt.u.negTokenResp.negResult, 1);
 
48
    if (nt.u.negTokenResp.negResult == NULL) {
 
49
        *minor_status = ENOMEM;
 
50
        return GSS_S_FAILURE;
 
51
    }
 
52
    *(nt.u.negTokenResp.negResult)  = reject;
 
53
    nt.u.negTokenResp.supportedMech = NULL;
 
54
    nt.u.negTokenResp.responseToken = NULL;
 
55
    nt.u.negTokenResp.mechListMIC   = NULL;
 
56
 
 
57
    ASN1_MALLOC_ENCODE(NegotiationToken,
 
58
                       output_token->value, output_token->length, &nt,
 
59
                       &size, *minor_status);
 
60
    free_NegotiationToken(&nt);
 
61
    if (*minor_status != 0)
 
62
        return GSS_S_FAILURE;
 
63
 
 
64
    return GSS_S_BAD_MECH;
 
65
}
 
66
 
 
67
static OM_uint32
 
68
acceptor_approved(gss_name_t target_name, gss_OID mech)
 
69
{
 
70
    gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
 
71
    gss_OID_set oidset;
 
72
    OM_uint32 junk, ret;
 
73
 
 
74
    if (target_name == GSS_C_NO_NAME)
 
75
        return GSS_S_COMPLETE;
 
76
 
 
77
    gss_create_empty_oid_set(&junk, &oidset);
 
78
    gss_add_oid_set_member(&junk, mech, &oidset);
 
79
 
 
80
    ret = gss_acquire_cred(&junk, target_name, GSS_C_INDEFINITE, oidset,
 
81
                           GSS_C_ACCEPT, &cred, NULL, NULL);
 
82
    gss_release_oid_set(&junk, &oidset);
 
83
    if (ret != GSS_S_COMPLETE)
 
84
        return ret;
 
85
    gss_release_cred(&junk, &cred);
 
86
 
 
87
    return GSS_S_COMPLETE;
 
88
}
 
89
 
 
90
static OM_uint32
 
91
send_supported_mechs (OM_uint32 *minor_status,
 
92
                      gss_buffer_t output_token)
 
93
{
 
94
    NegotiationTokenWin nt;
 
95
    char hostname[MAXHOSTNAMELEN + 1], *p;
 
96
    gss_buffer_desc name_buf;
 
97
    gss_OID name_type;
 
98
    gss_name_t target_princ;
 
99
    gss_name_t canon_princ;
 
100
    OM_uint32 minor;
 
101
    size_t buf_len;
 
102
    gss_buffer_desc data;
 
103
    OM_uint32 ret;
 
104
 
 
105
    memset(&nt, 0, sizeof(nt));
 
106
 
 
107
    nt.element = choice_NegotiationTokenWin_negTokenInit;
 
108
    nt.u.negTokenInit.reqFlags = NULL;
 
109
    nt.u.negTokenInit.mechToken = NULL;
 
110
    nt.u.negTokenInit.negHints = NULL;
 
111
 
 
112
    ret = _gss_spnego_indicate_mechtypelist(minor_status, GSS_C_NO_NAME,
 
113
                                            acceptor_approved, 1, NULL,
 
114
                                            &nt.u.negTokenInit.mechTypes, NULL);
 
115
    if (ret != GSS_S_COMPLETE) {
 
116
        return ret;
 
117
    }
 
118
 
 
119
    memset(&target_princ, 0, sizeof(target_princ));
 
120
    if (gethostname(hostname, sizeof(hostname) - 2) != 0) {
 
121
        *minor_status = errno;
 
122
        free_NegotiationTokenWin(&nt);
 
123
        return GSS_S_FAILURE;
 
124
    }
 
125
    hostname[sizeof(hostname) - 1] = '\0';
 
126
 
 
127
    /* Send the constructed SAM name for this host */
 
128
    for (p = hostname; *p != '\0' && *p != '.'; p++) {
 
129
        *p = toupper((unsigned char)*p);
 
130
    }
 
131
    *p++ = '$';
 
132
    *p = '\0';
 
133
 
 
134
    name_buf.length = strlen(hostname);
 
135
    name_buf.value = hostname;
 
136
 
 
137
    ret = gss_import_name(minor_status, &name_buf,
 
138
                          GSS_C_NO_OID,
 
139
                          &target_princ);
 
140
    if (ret != GSS_S_COMPLETE) {
 
141
        free_NegotiationTokenWin(&nt);
 
142
        return ret;
 
143
    }
 
144
 
 
145
    name_buf.length = 0;
 
146
    name_buf.value = NULL;
 
147
 
 
148
    /* Canonicalize the name using the preferred mechanism */
 
149
    ret = gss_canonicalize_name(minor_status,
 
150
                                target_princ,
 
151
                                GSS_C_NO_OID,
 
152
                                &canon_princ);
 
153
    if (ret != GSS_S_COMPLETE) {
 
154
        free_NegotiationTokenWin(&nt);
 
155
        gss_release_name(&minor, &target_princ);
 
156
        return ret;
 
157
    }
 
158
 
 
159
    ret = gss_display_name(minor_status, canon_princ,
 
160
                           &name_buf, &name_type);
 
161
    if (ret != GSS_S_COMPLETE) {
 
162
        free_NegotiationTokenWin(&nt);
 
163
        gss_release_name(&minor, &canon_princ);
 
164
        gss_release_name(&minor, &target_princ);
 
165
        return ret;
 
166
    }
 
167
 
 
168
    gss_release_name(&minor, &canon_princ);
 
169
    gss_release_name(&minor, &target_princ);
 
170
 
 
171
    ALLOC(nt.u.negTokenInit.negHints, 1);
 
172
    if (nt.u.negTokenInit.negHints == NULL) {
 
173
        *minor_status = ENOMEM;
 
174
        gss_release_buffer(&minor, &name_buf);
 
175
        free_NegotiationTokenWin(&nt);
 
176
        return GSS_S_FAILURE;
 
177
    }
 
178
 
 
179
    ALLOC(nt.u.negTokenInit.negHints->hintName, 1);
 
180
    if (nt.u.negTokenInit.negHints->hintName == NULL) {
 
181
        *minor_status = ENOMEM;
 
182
        gss_release_buffer(&minor, &name_buf);
 
183
        free_NegotiationTokenWin(&nt);
 
184
        return GSS_S_FAILURE;
 
185
    }
 
186
 
 
187
    *(nt.u.negTokenInit.negHints->hintName) = name_buf.value;
 
188
    name_buf.value = NULL;
 
189
    nt.u.negTokenInit.negHints->hintAddress = NULL;
 
190
 
 
191
    ASN1_MALLOC_ENCODE(NegotiationTokenWin,
 
192
                       data.value, data.length, &nt, &buf_len, ret);
 
193
    free_NegotiationTokenWin(&nt);
 
194
    if (ret) {
 
195
        return ret;
 
196
    }
 
197
    if (data.length != buf_len)
 
198
        abort();
 
199
 
 
200
    ret = gss_encapsulate_token(&data, GSS_SPNEGO_MECHANISM, output_token);
 
201
 
 
202
    free (data.value);
 
203
 
 
204
    if (ret != GSS_S_COMPLETE)
 
205
        return ret;
 
206
 
 
207
    *minor_status = 0;
 
208
 
 
209
    return GSS_S_CONTINUE_NEEDED;
 
210
}
 
211
 
 
212
static OM_uint32
 
213
send_accept (OM_uint32 *minor_status,
 
214
             gssspnego_ctx context_handle,
 
215
             gss_buffer_t mech_token,
 
216
             int initial_response,
 
217
             gss_buffer_t mech_buf,
 
218
             gss_buffer_t output_token)
 
219
{
 
220
    NegotiationToken nt;
 
221
    OM_uint32 ret;
 
222
    gss_buffer_desc mech_mic_buf;
 
223
    size_t size;
 
224
 
 
225
    memset(&nt, 0, sizeof(nt));
 
226
 
 
227
    nt.element = choice_NegotiationToken_negTokenResp;
 
228
 
 
229
    ALLOC(nt.u.negTokenResp.negResult, 1);
 
230
    if (nt.u.negTokenResp.negResult == NULL) {
 
231
        *minor_status = ENOMEM;
 
232
        return GSS_S_FAILURE;
 
233
    }
 
234
 
 
235
    if (context_handle->open) {
 
236
        if (mech_token != GSS_C_NO_BUFFER
 
237
            && mech_token->length != 0
 
238
            && mech_buf != GSS_C_NO_BUFFER)
 
239
            *(nt.u.negTokenResp.negResult)  = accept_incomplete;
 
240
        else
 
241
            *(nt.u.negTokenResp.negResult)  = accept_completed;
 
242
    } else {
 
243
        if (initial_response && context_handle->require_mic)
 
244
            *(nt.u.negTokenResp.negResult)  = request_mic;
 
245
        else
 
246
            *(nt.u.negTokenResp.negResult)  = accept_incomplete;
 
247
    }
 
248
 
 
249
    if (initial_response) {
 
250
        ALLOC(nt.u.negTokenResp.supportedMech, 1);
 
251
        if (nt.u.negTokenResp.supportedMech == NULL) {
 
252
            free_NegotiationToken(&nt);
 
253
            *minor_status = ENOMEM;
 
254
            return GSS_S_FAILURE;
 
255
        }
 
256
 
 
257
        ret = der_get_oid(context_handle->preferred_mech_type->elements,
 
258
                          context_handle->preferred_mech_type->length,
 
259
                          nt.u.negTokenResp.supportedMech,
 
260
                          NULL);
 
261
        if (ret) {
 
262
            free_NegotiationToken(&nt);
 
263
            *minor_status = ENOMEM;
 
264
            return GSS_S_FAILURE;
 
265
        }
 
266
    } else {
 
267
        nt.u.negTokenResp.supportedMech = NULL;
 
268
    }
 
269
 
 
270
    if (mech_token != GSS_C_NO_BUFFER && mech_token->length != 0) {
 
271
        ALLOC(nt.u.negTokenResp.responseToken, 1);
 
272
        if (nt.u.negTokenResp.responseToken == NULL) {
 
273
            free_NegotiationToken(&nt);
 
274
            *minor_status = ENOMEM;
 
275
            return GSS_S_FAILURE;
 
276
        }
 
277
        nt.u.negTokenResp.responseToken->length = mech_token->length;
 
278
        nt.u.negTokenResp.responseToken->data   = mech_token->value;
 
279
        mech_token->length = 0;
 
280
        mech_token->value  = NULL;
 
281
    } else {
 
282
        nt.u.negTokenResp.responseToken = NULL;
 
283
    }
 
284
 
 
285
    if (mech_buf != GSS_C_NO_BUFFER) {
 
286
        ret = gss_get_mic(minor_status,
 
287
                          context_handle->negotiated_ctx_id,
 
288
                          0,
 
289
                          mech_buf,
 
290
                          &mech_mic_buf);
 
291
        if (ret == GSS_S_COMPLETE) {
 
292
            ALLOC(nt.u.negTokenResp.mechListMIC, 1);
 
293
            if (nt.u.negTokenResp.mechListMIC == NULL) {
 
294
                gss_release_buffer(minor_status, &mech_mic_buf);
 
295
                free_NegotiationToken(&nt);
 
296
                *minor_status = ENOMEM;
 
297
                return GSS_S_FAILURE;
 
298
            }
 
299
            nt.u.negTokenResp.mechListMIC->length = mech_mic_buf.length;
 
300
            nt.u.negTokenResp.mechListMIC->data   = mech_mic_buf.value;
 
301
        } else if (ret == GSS_S_UNAVAILABLE) {
 
302
            nt.u.negTokenResp.mechListMIC = NULL;
 
303
        } else {
 
304
            free_NegotiationToken(&nt);
 
305
            return ret;
 
306
        }
 
307
 
 
308
    } else
 
309
        nt.u.negTokenResp.mechListMIC = NULL;
 
310
 
 
311
    ASN1_MALLOC_ENCODE(NegotiationToken,
 
312
                       output_token->value, output_token->length,
 
313
                       &nt, &size, ret);
 
314
    if (ret) {
 
315
        free_NegotiationToken(&nt);
 
316
        *minor_status = ret;
 
317
        return GSS_S_FAILURE;
 
318
    }
 
319
 
 
320
    /*
 
321
     * The response should not be encapsulated, because
 
322
     * it is a SubsequentContextToken (note though RFC 1964
 
323
     * specifies encapsulation for all _Kerberos_ tokens).
 
324
     */
 
325
 
 
326
    if (*(nt.u.negTokenResp.negResult) == accept_completed)
 
327
        ret = GSS_S_COMPLETE;
 
328
    else
 
329
        ret = GSS_S_CONTINUE_NEEDED;
 
330
    free_NegotiationToken(&nt);
 
331
    return ret;
 
332
}
 
333
 
 
334
 
 
335
static OM_uint32
 
336
verify_mechlist_mic
 
337
           (OM_uint32 *minor_status,
 
338
            gssspnego_ctx context_handle,
 
339
            gss_buffer_t mech_buf,
 
340
            heim_octet_string *mechListMIC
 
341
           )
 
342
{
 
343
    OM_uint32 ret;
 
344
    gss_buffer_desc mic_buf;
 
345
 
 
346
    if (context_handle->verified_mic) {
 
347
        /* This doesn't make sense, we've already verified it? */
 
348
        *minor_status = 0;
 
349
        return GSS_S_DUPLICATE_TOKEN;
 
350
    }
 
351
 
 
352
    if (mechListMIC == NULL) {
 
353
        *minor_status = 0;
 
354
        return GSS_S_DEFECTIVE_TOKEN;
 
355
    }
 
356
 
 
357
    mic_buf.length = mechListMIC->length;
 
358
    mic_buf.value  = mechListMIC->data;
 
359
 
 
360
    ret = gss_verify_mic(minor_status,
 
361
                         context_handle->negotiated_ctx_id,
 
362
                         mech_buf,
 
363
                         &mic_buf,
 
364
                         NULL);
 
365
 
 
366
    if (ret != GSS_S_COMPLETE)
 
367
        ret = GSS_S_DEFECTIVE_TOKEN;
 
368
 
 
369
    return ret;
 
370
}
 
371
 
 
372
static OM_uint32
 
373
select_mech(OM_uint32 *minor_status, MechType *mechType, int verify_p,
 
374
            gss_OID *mech_p)
 
375
{
 
376
    char mechbuf[64];
 
377
    size_t mech_len;
 
378
    gss_OID_desc oid;
 
379
    gss_OID oidp;
 
380
    gss_OID_set mechs;
 
381
    int i;
 
382
    OM_uint32 ret, junk;
 
383
 
 
384
    ret = der_put_oid ((unsigned char *)mechbuf + sizeof(mechbuf) - 1,
 
385
                       sizeof(mechbuf),
 
386
                       mechType,
 
387
                       &mech_len);
 
388
    if (ret) {
 
389
        return GSS_S_DEFECTIVE_TOKEN;
 
390
    }
 
391
 
 
392
    oid.length   = mech_len;
 
393
    oid.elements = mechbuf + sizeof(mechbuf) - mech_len;
 
394
 
 
395
    if (gss_oid_equal(&oid, GSS_SPNEGO_MECHANISM)) {
 
396
        return GSS_S_BAD_MECH;
 
397
    }
 
398
 
 
399
    *minor_status = 0;
 
400
 
 
401
    /* Translate broken MS Kebreros OID */
 
402
    if (gss_oid_equal(&oid, &_gss_spnego_mskrb_mechanism_oid_desc))
 
403
            oidp = &_gss_spnego_krb5_mechanism_oid_desc;
 
404
    else
 
405
            oidp = &oid;
 
406
 
 
407
 
 
408
    ret = gss_indicate_mechs(&junk, &mechs);
 
409
    if (ret)
 
410
            return (ret);
 
411
 
 
412
    for (i = 0; i < mechs->count; i++)
 
413
            if (gss_oid_equal(&mechs->elements[i], oidp))
 
414
                    break;
 
415
 
 
416
    if (i == mechs->count) {
 
417
            gss_release_oid_set(&junk, &mechs);
 
418
            return GSS_S_BAD_MECH;
 
419
    }
 
420
    gss_release_oid_set(&junk, &mechs);
 
421
 
 
422
    ret = gss_duplicate_oid(minor_status,
 
423
                            &oid, /* possibly this should be oidp */
 
424
                            mech_p);
 
425
 
 
426
    if (verify_p) {
 
427
        gss_name_t name = GSS_C_NO_NAME;
 
428
        gss_buffer_desc namebuf;
 
429
        char *str = NULL, *host, hostname[MAXHOSTNAMELEN];
 
430
 
 
431
        host = getenv("GSSAPI_SPNEGO_NAME");
 
432
        if (host == NULL || issuid()) {
 
433
            if (gethostname(hostname, sizeof(hostname)) != 0) {
 
434
                *minor_status = errno;
 
435
                return GSS_S_FAILURE;
 
436
            }
 
437
            asprintf(&str, "host@%s", hostname);
 
438
            host = str;
 
439
        }
 
440
 
 
441
        namebuf.length = strlen(host);
 
442
        namebuf.value = host;
 
443
 
 
444
        ret = gss_import_name(minor_status, &namebuf,
 
445
                              GSS_C_NT_HOSTBASED_SERVICE, &name);
 
446
        if (str)
 
447
            free(str);
 
448
        if (ret != GSS_S_COMPLETE)
 
449
            return ret;
 
450
 
 
451
        ret = acceptor_approved(name, *mech_p);
 
452
        gss_release_name(&junk, &name);
 
453
    }
 
454
 
 
455
    return ret;
 
456
}
 
457
 
 
458
 
 
459
static OM_uint32
 
460
acceptor_complete(OM_uint32 * minor_status,
 
461
                  gssspnego_ctx ctx,
 
462
                  int *get_mic,
 
463
                  gss_buffer_t mech_buf,
 
464
                  gss_buffer_t mech_input_token,
 
465
                  gss_buffer_t mech_output_token,
 
466
                  heim_octet_string *mic,
 
467
                  gss_buffer_t output_token)
 
468
{
 
469
    OM_uint32 ret;
 
470
    int require_mic, verify_mic;
 
471
    gss_buffer_desc buf;
 
472
 
 
473
    buf.length = 0;
 
474
    buf.value = NULL;
 
475
 
 
476
    ret = _gss_spnego_require_mechlist_mic(minor_status, ctx, &require_mic);
 
477
    if (ret)
 
478
        return ret;
 
479
 
 
480
    ctx->require_mic = require_mic;
 
481
 
 
482
    if (mic != NULL)
 
483
        require_mic = 1;
 
484
 
 
485
    if (ctx->open && require_mic) {
 
486
        if (mech_input_token == GSS_C_NO_BUFFER) { /* Even/One */
 
487
            verify_mic = 1;
 
488
            *get_mic = 0;
 
489
        } else if (mech_output_token != GSS_C_NO_BUFFER &&
 
490
                   mech_output_token->length == 0) { /* Odd */
 
491
            *get_mic = verify_mic = 1;
 
492
        } else { /* Even/One */
 
493
            verify_mic = 0;
 
494
            *get_mic = 1;
 
495
        }
 
496
        
 
497
        if (verify_mic || get_mic) {
 
498
            int eret;
 
499
            size_t buf_len;
 
500
        
 
501
            ASN1_MALLOC_ENCODE(MechTypeList,
 
502
                               mech_buf->value, mech_buf->length,
 
503
                               &ctx->initiator_mech_types, &buf_len, eret);
 
504
            if (eret) {
 
505
                *minor_status = eret;
 
506
                return GSS_S_FAILURE;
 
507
            }
 
508
            if (buf.length != buf_len)
 
509
                abort();
 
510
        }
 
511
        
 
512
        if (verify_mic) {
 
513
            ret = verify_mechlist_mic(minor_status, ctx, mech_buf, mic);
 
514
            if (ret) {
 
515
                if (get_mic)
 
516
                    send_reject (minor_status, output_token);
 
517
                if (buf.value)
 
518
                    free(buf.value);
 
519
                return ret;
 
520
            }
 
521
            ctx->verified_mic = 1;
 
522
        }
 
523
        if (buf.value)
 
524
            free(buf.value);
 
525
 
 
526
    } else
 
527
        *get_mic = 0;
 
528
 
 
529
    return GSS_S_COMPLETE;
 
530
}
 
531
 
 
532
 
 
533
static OM_uint32
 
534
acceptor_start
 
535
           (OM_uint32 * minor_status,
 
536
            gss_ctx_id_t * context_handle,
 
537
            const gss_cred_id_t acceptor_cred_handle,
 
538
            const gss_buffer_t input_token_buffer,
 
539
            const gss_channel_bindings_t input_chan_bindings,
 
540
            gss_name_t * src_name,
 
541
            gss_OID * mech_type,
 
542
            gss_buffer_t output_token,
 
543
            OM_uint32 * ret_flags,
 
544
            OM_uint32 * time_rec,
 
545
            gss_cred_id_t *delegated_cred_handle
 
546
           )
 
547
{
 
548
    OM_uint32 ret, junk;
 
549
    NegotiationToken nt;
 
550
    size_t nt_len;
 
551
    NegTokenInit *ni;
 
552
    int i;
 
553
    gss_buffer_desc data;
 
554
    gss_buffer_t mech_input_token = GSS_C_NO_BUFFER;
 
555
    gss_buffer_desc mech_output_token;
 
556
    gss_buffer_desc mech_buf;
 
557
    gss_OID preferred_mech_type = GSS_C_NO_OID;
 
558
    gssspnego_ctx ctx;
 
559
    gssspnego_cred acceptor_cred = (gssspnego_cred)acceptor_cred_handle;
 
560
    int get_mic = 0;
 
561
    int first_ok = 0;
 
562
 
 
563
    mech_output_token.value = NULL;
 
564
    mech_output_token.length = 0;
 
565
    mech_buf.value = NULL;
 
566
 
 
567
    if (input_token_buffer->length == 0)
 
568
        return send_supported_mechs (minor_status, output_token);
 
569
        
 
570
    ret = _gss_spnego_alloc_sec_context(minor_status, context_handle);
 
571
    if (ret != GSS_S_COMPLETE)
 
572
        return ret;
 
573
 
 
574
    ctx = (gssspnego_ctx)*context_handle;
 
575
 
 
576
    /*
 
577
     * The GSS-API encapsulation is only present on the initial
 
578
     * context token (negTokenInit).
 
579
     */
 
580
    ret = gss_decapsulate_token (input_token_buffer,
 
581
                                 GSS_SPNEGO_MECHANISM,
 
582
                                 &data);
 
583
    if (ret)
 
584
        return ret;
 
585
 
 
586
    ret = decode_NegotiationToken(data.value, data.length, &nt, &nt_len);
 
587
    gss_release_buffer(minor_status, &data);
 
588
    if (ret) {
 
589
        *minor_status = ret;
 
590
        return GSS_S_DEFECTIVE_TOKEN;
 
591
    }
 
592
    if (nt.element != choice_NegotiationToken_negTokenInit) {
 
593
        *minor_status = 0;
 
594
        return GSS_S_DEFECTIVE_TOKEN;
 
595
    }
 
596
    ni = &nt.u.negTokenInit;
 
597
 
 
598
    if (ni->mechTypes.len < 1) {
 
599
        free_NegotiationToken(&nt);
 
600
        *minor_status = 0;
 
601
        return GSS_S_DEFECTIVE_TOKEN;
 
602
    }
 
603
 
 
604
    HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
 
605
 
 
606
    ret = copy_MechTypeList(&ni->mechTypes, &ctx->initiator_mech_types);
 
607
    if (ret) {
 
608
        HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
 
609
        free_NegotiationToken(&nt);
 
610
        *minor_status = ret;
 
611
        return GSS_S_FAILURE;
 
612
    }
 
613
 
 
614
    /*
 
615
     * First we try the opportunistic token if we have support for it,
 
616
     * don't try to verify we have credential for the token,
 
617
     * gss_accept_sec_context() will (hopefully) tell us that.
 
618
     * If that failes,
 
619
     */
 
620
 
 
621
    ret = select_mech(minor_status,
 
622
                      &ni->mechTypes.val[0],
 
623
                      0,
 
624
                      &preferred_mech_type);
 
625
 
 
626
    if (ret == 0 && ni->mechToken != NULL) {
 
627
        gss_cred_id_t mech_delegated_cred = GSS_C_NO_CREDENTIAL;
 
628
        gss_cred_id_t mech_cred;
 
629
        gss_buffer_desc ibuf;
 
630
 
 
631
        ibuf.length = ni->mechToken->length;
 
632
        ibuf.value = ni->mechToken->data;
 
633
        mech_input_token = &ibuf;
 
634
 
 
635
        if (acceptor_cred != NULL)
 
636
            mech_cred = acceptor_cred->negotiated_cred_id;
 
637
        else
 
638
            mech_cred = GSS_C_NO_CREDENTIAL;
 
639
        
 
640
        if (ctx->mech_src_name != GSS_C_NO_NAME)
 
641
            gss_release_name(&junk, &ctx->mech_src_name);
 
642
        
 
643
        ret = gss_accept_sec_context(minor_status,
 
644
                                     &ctx->negotiated_ctx_id,
 
645
                                     mech_cred,
 
646
                                     mech_input_token,
 
647
                                     input_chan_bindings,
 
648
                                     &ctx->mech_src_name,
 
649
                                     &ctx->negotiated_mech_type,
 
650
                                     &mech_output_token,
 
651
                                     &ctx->mech_flags,
 
652
                                     &ctx->mech_time_rec,
 
653
                                     &mech_delegated_cred);
 
654
 
 
655
        if (mech_delegated_cred && delegated_cred_handle) {
 
656
            _gss_spnego_alloc_cred(&junk,
 
657
                                   mech_delegated_cred,
 
658
                                   delegated_cred_handle);
 
659
        } else if (mech_delegated_cred != GSS_C_NO_CREDENTIAL)
 
660
            gss_release_cred(&junk, &mech_delegated_cred);
 
661
 
 
662
        if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
 
663
            ctx->preferred_mech_type = preferred_mech_type;
 
664
            ctx->negotiated_mech_type = preferred_mech_type;
 
665
            if (ret == GSS_S_COMPLETE)
 
666
                ctx->open = 1;
 
667
 
 
668
            ret = acceptor_complete(minor_status,
 
669
                                    ctx,
 
670
                                    &get_mic,
 
671
                                    &mech_buf,
 
672
                                    mech_input_token,
 
673
                                    &mech_output_token,
 
674
                                    ni->mechListMIC,
 
675
                                    output_token);
 
676
            if (ret != GSS_S_COMPLETE)
 
677
                goto out;
 
678
 
 
679
            first_ok = 1;
 
680
        } else {
 
681
            gss_mg_collect_error(preferred_mech_type, ret, *minor_status);
 
682
        }
 
683
    }
 
684
 
 
685
    /*
 
686
     * If opportunistic token failed, lets try the other mechs.
 
687
     */
 
688
 
 
689
    if (!first_ok && ni->mechToken != NULL) {
 
690
 
 
691
        preferred_mech_type = GSS_C_NO_OID;
 
692
 
 
693
        /* Call glue layer to find first mech we support */
 
694
        for (i = 1; i < ni->mechTypes.len; ++i) {
 
695
            ret = select_mech(minor_status,
 
696
                              &ni->mechTypes.val[i],
 
697
                              1,
 
698
                              &preferred_mech_type);
 
699
            if (ret == 0)
 
700
                break;
 
701
        }
 
702
        if (preferred_mech_type == GSS_C_NO_OID) {
 
703
            HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
 
704
            free_NegotiationToken(&nt);
 
705
            return ret;
 
706
        }
 
707
 
 
708
        ctx->preferred_mech_type = preferred_mech_type;
 
709
        ctx->negotiated_mech_type = preferred_mech_type;
 
710
    }
 
711
 
 
712
    /*
 
713
     * The initial token always have a response
 
714
     */
 
715
 
 
716
    ret = send_accept (minor_status,
 
717
                       ctx,
 
718
                       &mech_output_token,
 
719
                       1,
 
720
                       get_mic ? &mech_buf : NULL,
 
721
                       output_token);
 
722
    if (ret)
 
723
        goto out;
 
724
 
 
725
out:
 
726
    if (mech_output_token.value != NULL)
 
727
        gss_release_buffer(&junk, &mech_output_token);
 
728
    if (mech_buf.value != NULL) {
 
729
        free(mech_buf.value);
 
730
        mech_buf.value = NULL;
 
731
    }
 
732
    free_NegotiationToken(&nt);
 
733
 
 
734
 
 
735
    if (ret == GSS_S_COMPLETE) {
 
736
        if (src_name != NULL && ctx->mech_src_name != NULL) {
 
737
            spnego_name name;
 
738
 
 
739
            name = calloc(1, sizeof(*name));
 
740
            if (name) {
 
741
                name->mech = ctx->mech_src_name;
 
742
                ctx->mech_src_name = NULL;
 
743
                *src_name = (gss_name_t)name;
 
744
            }
 
745
        }
 
746
    }
 
747
 
 
748
    if (mech_type != NULL)
 
749
        *mech_type = ctx->negotiated_mech_type;
 
750
    if (ret_flags != NULL)
 
751
        *ret_flags = ctx->mech_flags;
 
752
    if (time_rec != NULL)
 
753
        *time_rec = ctx->mech_time_rec;
 
754
 
 
755
    if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
 
756
        HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
 
757
        return ret;
 
758
    }
 
759
 
 
760
    _gss_spnego_internal_delete_sec_context(&junk, context_handle,
 
761
                                            GSS_C_NO_BUFFER);
 
762
 
 
763
    return ret;
 
764
}
 
765
 
 
766
 
 
767
static OM_uint32
 
768
acceptor_continue
 
769
           (OM_uint32 * minor_status,
 
770
            gss_ctx_id_t * context_handle,
 
771
            const gss_cred_id_t acceptor_cred_handle,
 
772
            const gss_buffer_t input_token_buffer,
 
773
            const gss_channel_bindings_t input_chan_bindings,
 
774
            gss_name_t * src_name,
 
775
            gss_OID * mech_type,
 
776
            gss_buffer_t output_token,
 
777
            OM_uint32 * ret_flags,
 
778
            OM_uint32 * time_rec,
 
779
            gss_cred_id_t *delegated_cred_handle
 
780
           )
 
781
{
 
782
    OM_uint32 ret, ret2, minor, junk;
 
783
    NegotiationToken nt;
 
784
    size_t nt_len;
 
785
    NegTokenResp *na;
 
786
    unsigned int negResult = accept_incomplete;
 
787
    gss_buffer_t mech_input_token = GSS_C_NO_BUFFER;
 
788
    gss_buffer_t mech_output_token = GSS_C_NO_BUFFER;
 
789
    gss_buffer_desc mech_buf;
 
790
    gssspnego_ctx ctx;
 
791
    gssspnego_cred acceptor_cred = (gssspnego_cred)acceptor_cred_handle;
 
792
 
 
793
    mech_buf.value = NULL;
 
794
 
 
795
    ctx = (gssspnego_ctx)*context_handle;
 
796
 
 
797
    /*
 
798
     * The GSS-API encapsulation is only present on the initial
 
799
     * context token (negTokenInit).
 
800
     */
 
801
 
 
802
    ret = decode_NegotiationToken(input_token_buffer->value,
 
803
                                  input_token_buffer->length,
 
804
                                  &nt, &nt_len);
 
805
    if (ret) {
 
806
        *minor_status = ret;
 
807
        return GSS_S_DEFECTIVE_TOKEN;
 
808
    }
 
809
    if (nt.element != choice_NegotiationToken_negTokenResp) {
 
810
        *minor_status = 0;
 
811
        return GSS_S_DEFECTIVE_TOKEN;
 
812
    }
 
813
    na = &nt.u.negTokenResp;
 
814
 
 
815
    if (na->negResult != NULL) {
 
816
        negResult = *(na->negResult);
 
817
    }
 
818
 
 
819
    HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
 
820
 
 
821
    {
 
822
        gss_buffer_desc ibuf, obuf;
 
823
        int require_mic, get_mic = 0;
 
824
        int require_response;
 
825
        heim_octet_string *mic;
 
826
 
 
827
        if (na->responseToken != NULL) {
 
828
            ibuf.length = na->responseToken->length;
 
829
            ibuf.value = na->responseToken->data;
 
830
            mech_input_token = &ibuf;
 
831
        } else {
 
832
            ibuf.value = NULL;
 
833
            ibuf.length = 0;
 
834
        }
 
835
 
 
836
        if (mech_input_token != GSS_C_NO_BUFFER) {
 
837
            gss_cred_id_t mech_cred;
 
838
            gss_cred_id_t mech_delegated_cred = GSS_C_NO_CREDENTIAL;
 
839
 
 
840
            if (acceptor_cred != NULL)
 
841
                mech_cred = acceptor_cred->negotiated_cred_id;
 
842
            else
 
843
                mech_cred = GSS_C_NO_CREDENTIAL;
 
844
 
 
845
            if (ctx->mech_src_name != GSS_C_NO_NAME)
 
846
                gss_release_name(&minor, &ctx->mech_src_name);
 
847
 
 
848
            ret = gss_accept_sec_context(&minor,
 
849
                                         &ctx->negotiated_ctx_id,
 
850
                                         mech_cred,
 
851
                                         mech_input_token,
 
852
                                         input_chan_bindings,
 
853
                                         &ctx->mech_src_name,
 
854
                                         &ctx->negotiated_mech_type,
 
855
                                         &obuf,
 
856
                                         &ctx->mech_flags,
 
857
                                         &ctx->mech_time_rec,
 
858
                                         &mech_delegated_cred);
 
859
 
 
860
            if (mech_delegated_cred && delegated_cred_handle) {
 
861
                _gss_spnego_alloc_cred(&junk,
 
862
                                       mech_delegated_cred,
 
863
                                       delegated_cred_handle);
 
864
            } else if (mech_delegated_cred != GSS_C_NO_CREDENTIAL)
 
865
                gss_release_cred(&junk, &mech_delegated_cred);
 
866
 
 
867
            if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
 
868
                mech_output_token = &obuf;
 
869
            }
 
870
            if (ret != GSS_S_COMPLETE && ret != GSS_S_CONTINUE_NEEDED) {
 
871
                free_NegotiationToken(&nt);
 
872
                gss_mg_collect_error(ctx->negotiated_mech_type, ret, minor);
 
873
                send_reject (minor_status, output_token);
 
874
                HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
 
875
                return ret;
 
876
            }
 
877
            if (ret == GSS_S_COMPLETE)
 
878
                ctx->open = 1;
 
879
        } else
 
880
            ret = GSS_S_COMPLETE;
 
881
 
 
882
        ret2 = _gss_spnego_require_mechlist_mic(minor_status,
 
883
                                                ctx,
 
884
                                                &require_mic);
 
885
        if (ret2)
 
886
            goto out;
 
887
 
 
888
        ctx->require_mic = require_mic;
 
889
 
 
890
        mic = na->mechListMIC;
 
891
        if (mic != NULL)
 
892
            require_mic = 1;
 
893
 
 
894
        if (ret == GSS_S_COMPLETE)
 
895
            ret = acceptor_complete(minor_status,
 
896
                                    ctx,
 
897
                                    &get_mic,
 
898
                                    &mech_buf,
 
899
                                    mech_input_token,
 
900
                                    mech_output_token,
 
901
                                    na->mechListMIC,
 
902
                                    output_token);
 
903
 
 
904
        if (ctx->mech_flags & GSS_C_DCE_STYLE)
 
905
            require_response = (negResult != accept_completed);
 
906
        else
 
907
            require_response = 0;
 
908
 
 
909
        /*
 
910
         * Check whether we need to send a result: there should be only
 
911
         * one accept_completed response sent in the entire negotiation
 
912
         */
 
913
        if ((mech_output_token != GSS_C_NO_BUFFER &&
 
914
             mech_output_token->length != 0)
 
915
            || (ctx->open && negResult == accept_incomplete)
 
916
            || require_response
 
917
            || get_mic) {
 
918
            ret2 = send_accept (minor_status,
 
919
                                ctx,
 
920
                                mech_output_token,
 
921
                                0,
 
922
                                get_mic ? &mech_buf : NULL,
 
923
                                output_token);
 
924
            if (ret2)
 
925
                goto out;
 
926
        }
 
927
 
 
928
     out:
 
929
        if (ret2 != GSS_S_COMPLETE)
 
930
            ret = ret2;
 
931
        if (mech_output_token != NULL)
 
932
            gss_release_buffer(&minor, mech_output_token);
 
933
        if (mech_buf.value != NULL)
 
934
            free(mech_buf.value);
 
935
        free_NegotiationToken(&nt);
 
936
    }
 
937
 
 
938
    if (ret == GSS_S_COMPLETE) {
 
939
        if (src_name != NULL && ctx->mech_src_name != NULL) {
 
940
            spnego_name name;
 
941
 
 
942
            name = calloc(1, sizeof(*name));
 
943
            if (name) {
 
944
                name->mech = ctx->mech_src_name;
 
945
                ctx->mech_src_name = NULL;
 
946
                *src_name = (gss_name_t)name;
 
947
            }
 
948
        }
 
949
    }
 
950
 
 
951
    if (mech_type != NULL)
 
952
        *mech_type = ctx->negotiated_mech_type;
 
953
    if (ret_flags != NULL)
 
954
        *ret_flags = ctx->mech_flags;
 
955
    if (time_rec != NULL)
 
956
        *time_rec = ctx->mech_time_rec;
 
957
 
 
958
    if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED) {
 
959
        HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
 
960
        return ret;
 
961
    }
 
962
 
 
963
    _gss_spnego_internal_delete_sec_context(&minor, context_handle,
 
964
                                   GSS_C_NO_BUFFER);
 
965
 
 
966
    return ret;
 
967
}
 
968
 
 
969
OM_uint32
 
970
_gss_spnego_accept_sec_context
 
971
           (OM_uint32 * minor_status,
 
972
            gss_ctx_id_t * context_handle,
 
973
            const gss_cred_id_t acceptor_cred_handle,
 
974
            const gss_buffer_t input_token_buffer,
 
975
            const gss_channel_bindings_t input_chan_bindings,
 
976
            gss_name_t * src_name,
 
977
            gss_OID * mech_type,
 
978
            gss_buffer_t output_token,
 
979
            OM_uint32 * ret_flags,
 
980
            OM_uint32 * time_rec,
 
981
            gss_cred_id_t *delegated_cred_handle
 
982
           )
 
983
{
 
984
    _gss_accept_sec_context_t *func;
 
985
 
 
986
    *minor_status = 0;
 
987
 
 
988
    output_token->length = 0;
 
989
    output_token->value  = NULL;
 
990
 
 
991
    if (src_name != NULL)
 
992
        *src_name = GSS_C_NO_NAME;
 
993
    if (mech_type != NULL)
 
994
        *mech_type = GSS_C_NO_OID;
 
995
    if (ret_flags != NULL)
 
996
        *ret_flags = 0;
 
997
    if (time_rec != NULL)
 
998
        *time_rec = 0;
 
999
    if (delegated_cred_handle != NULL)
 
1000
        *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
 
1001
 
 
1002
 
 
1003
    if (*context_handle == GSS_C_NO_CONTEXT)
 
1004
        func = acceptor_start;
 
1005
    else
 
1006
        func = acceptor_continue;
 
1007
 
 
1008
 
 
1009
    return (*func)(minor_status, context_handle, acceptor_cred_handle,
 
1010
                   input_token_buffer, input_chan_bindings,
 
1011
                   src_name, mech_type, output_token, ret_flags,
 
1012
                   time_rec, delegated_cred_handle);
 
1013
}