Coverage Report

Created: 2024-07-23 07:27

/src/krb5/src/lib/gssapi/krb5/init_sec_context.c
Line
Count
Source (jump to first uncovered line)
1
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
/*
3
 * Copyright 2000, 2002, 2003, 2007, 2008 by the Massachusetts Institute of
4
 * Technology.  All Rights Reserved.
5
 *
6
 * Export of this software from the United States of America may
7
 *   require a specific license from the United States Government.
8
 *   It is the responsibility of any person or organization contemplating
9
 *   export to obtain such a license before exporting.
10
 *
11
 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
12
 * distribute this software and its documentation for any purpose and
13
 * without fee is hereby granted, provided that the above copyright
14
 * notice appear in all copies and that both that copyright notice and
15
 * this permission notice appear in supporting documentation, and that
16
 * the name of M.I.T. not be used in advertising or publicity pertaining
17
 * to distribution of the software without specific, written prior
18
 * permission.  Furthermore if you modify this software you must label
19
 * your software as modified software and not distribute it in such a
20
 * fashion that it might be confused with the original M.I.T. software.
21
 * M.I.T. makes no representations about the suitability of
22
 * this software for any purpose.  It is provided "as is" without express
23
 * or implied warranty.
24
 */
25
/*
26
 * Copyright 1993 by OpenVision Technologies, Inc.
27
 *
28
 * Permission to use, copy, modify, distribute, and sell this software
29
 * and its documentation for any purpose is hereby granted without fee,
30
 * provided that the above copyright notice appears in all copies and
31
 * that both that copyright notice and this permission notice appear in
32
 * supporting documentation, and that the name of OpenVision not be used
33
 * in advertising or publicity pertaining to distribution of the software
34
 * without specific, written prior permission. OpenVision makes no
35
 * representations about the suitability of this software for any
36
 * purpose.  It is provided "as is" without express or implied warranty.
37
 *
38
 * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
39
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
40
 * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
41
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
42
 * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
43
 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
44
 * PERFORMANCE OF THIS SOFTWARE.
45
 */
46
47
/*
48
 * Copyright (C) 1998 by the FundsXpress, INC.
49
 *
50
 * All rights reserved.
51
 *
52
 * Export of this software from the United States of America may require
53
 * a specific license from the United States Government.  It is the
54
 * responsibility of any person or organization contemplating export to
55
 * obtain such a license before exporting.
56
 *
57
 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
58
 * distribute this software and its documentation for any purpose and
59
 * without fee is hereby granted, provided that the above copyright
60
 * notice appear in all copies and that both that copyright notice and
61
 * this permission notice appear in supporting documentation, and that
62
 * the name of FundsXpress. not be used in advertising or publicity pertaining
63
 * to distribution of the software without specific, written prior
64
 * permission.  FundsXpress makes no representations about the suitability of
65
 * this software for any purpose.  It is provided "as is" without express
66
 * or implied warranty.
67
 *
68
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
69
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
70
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
71
 */
72
/*
73
 * Copyright (c) 2006-2008, Novell, Inc.
74
 * All rights reserved.
75
 *
76
 * Redistribution and use in source and binary forms, with or without
77
 * modification, are permitted provided that the following conditions are met:
78
 *
79
 *   * Redistributions of source code must retain the above copyright notice,
80
 *       this list of conditions and the following disclaimer.
81
 *   * Redistributions in binary form must reproduce the above copyright
82
 *       notice, this list of conditions and the following disclaimer in the
83
 *       documentation and/or other materials provided with the distribution.
84
 *   * The copyright holder's name is not used to endorse or promote products
85
 *       derived from this software without specific prior written permission.
86
 *
87
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
88
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
89
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
90
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
91
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
92
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
93
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
94
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
95
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
96
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
97
 * POSSIBILITY OF SUCH DAMAGE.
98
 */
99
100
#include "k5-int.h"
101
#include "gssapiP_krb5.h"
102
#ifdef HAVE_MEMORY_H
103
#include <memory.h>
104
#endif
105
#include <stdlib.h>
106
#include <assert.h>
107
108
/*
109
 * $Id$
110
 */
111
112
/* XXX This is for debugging only!!!  Should become a real bitfield
113
   at some point */
114
int krb5_gss_dbg_client_expcreds = 0;
115
116
/*
117
 * Common code which fetches the correct krb5 credentials from the
118
 * ccache.
119
 */
120
static krb5_error_code
121
get_credentials(krb5_context context, krb5_gss_cred_id_t cred,
122
                krb5_gss_name_t server, krb5_timestamp now,
123
                krb5_timestamp endtime, krb5_creds **out_creds)
124
0
{
125
0
    krb5_error_code     code;
126
0
    krb5_creds          in_creds, evidence_creds, mcreds, *result_creds = NULL;
127
0
    krb5_flags          flags = 0;
128
0
    krb5_principal_data server_data;
129
130
0
    *out_creds = NULL;
131
132
0
    k5_mutex_assert_locked(&cred->lock);
133
0
    memset(&in_creds, 0, sizeof(krb5_creds));
134
0
    memset(&evidence_creds, 0, sizeof(krb5_creds));
135
0
    in_creds.client = in_creds.server = NULL;
136
137
0
    assert(cred->name != NULL);
138
139
    /* Remove assumed realm from host-based S4U2Proxy requests as they must
140
     * start in the client realm. */
141
0
    server_data = *server->princ;
142
0
    if (cred->impersonator != NULL && server_data.type == KRB5_NT_SRV_HST)
143
0
        server_data.realm = empty_data();
144
0
    in_creds.server = &server_data;
145
146
0
    in_creds.client = cred->name->princ;
147
0
    in_creds.times.endtime = endtime;
148
0
    in_creds.authdata = NULL;
149
0
    in_creds.keyblock.enctype = 0;
150
151
    /*
152
     * cred->name is immutable, so there is no need to acquire
153
     * cred->name->lock.
154
     */
155
0
    if (cred->name->ad_context != NULL) {
156
0
        code = krb5_authdata_export_authdata(context,
157
0
                                             cred->name->ad_context,
158
0
                                             AD_USAGE_TGS_REQ,
159
0
                                             &in_creds.authdata);
160
0
        if (code != 0)
161
0
            goto cleanup;
162
0
    }
163
164
    /* Try constrained delegation if we have proxy credentials. */
165
0
    if (cred->impersonator != NULL) {
166
        /* If we are trying to get a ticket to ourselves, we should use the
167
         * the evidence ticket directly from cache. */
168
0
        if (krb5_principal_compare(context, cred->impersonator,
169
0
                                   server->princ)) {
170
0
            flags |= KRB5_GC_CACHED;
171
0
        } else {
172
0
            memset(&mcreds, 0, sizeof(mcreds));
173
0
            mcreds.magic = KV5M_CREDS;
174
0
            mcreds.server = cred->impersonator;
175
0
            mcreds.client = cred->name->princ;
176
0
            code = krb5_cc_retrieve_cred(context, cred->ccache,
177
0
                                         KRB5_TC_MATCH_AUTHDATA, &mcreds,
178
0
                                         &evidence_creds);
179
0
            if (code)
180
0
                goto cleanup;
181
182
0
            in_creds.client = cred->impersonator;
183
0
            in_creds.second_ticket = evidence_creds.ticket;
184
0
            flags = KRB5_GC_CANONICALIZE | KRB5_GC_CONSTRAINED_DELEGATION;
185
0
        }
186
0
    }
187
188
    /* For IAKERB, only check the cache in this step.  We will ask the server
189
     * to make any necessary TGS requests. */
190
0
    if (cred->iakerb_mech)
191
0
        flags |= KRB5_GC_CACHED;
192
193
0
    code = krb5_get_credentials(context, flags, cred->ccache,
194
0
                                &in_creds, &result_creds);
195
0
    if (code)
196
0
        goto cleanup;
197
198
0
    if (flags & KRB5_GC_CONSTRAINED_DELEGATION) {
199
0
        if (!krb5_principal_compare(context, cred->name->princ,
200
0
                                    result_creds->client)) {
201
            /* server did not support constrained delegation */
202
0
            code = KRB5_KDCREP_MODIFIED;
203
0
            goto cleanup;
204
0
        }
205
0
    }
206
207
    /*
208
     * Enforce a stricter limit (without timeskew forgiveness at the
209
     * boundaries) because accept_sec_context code is also similarly
210
     * non-forgiving.
211
     */
212
0
    if (!krb5_gss_dbg_client_expcreds &&
213
0
        ts_after(now, result_creds->times.endtime)) {
214
0
        code = KRB5KRB_AP_ERR_TKT_EXPIRED;
215
0
        goto cleanup;
216
0
    }
217
218
0
    *out_creds = result_creds;
219
0
    result_creds = NULL;
220
221
0
cleanup:
222
0
    krb5_free_authdata(context, in_creds.authdata);
223
0
    krb5_free_cred_contents(context, &evidence_creds);
224
0
    krb5_free_creds(context, result_creds);
225
226
0
    return code;
227
0
}
228
struct gss_checksum_data {
229
    krb5_gss_ctx_id_rec *ctx;
230
    krb5_gss_cred_id_t cred;
231
    krb5_checksum md5;
232
    krb5_data checksum_data;
233
    krb5_gss_ctx_ext_t exts;
234
};
235
236
#ifdef CFX_EXERCISE
237
#include "../../krb5/krb/auth_con.h"
238
#endif
239
static krb5_error_code KRB5_CALLCONV
240
make_gss_checksum (krb5_context context, krb5_auth_context auth_context,
241
                   void *cksum_data, krb5_data **out)
242
0
{
243
0
    krb5_error_code code;
244
0
    krb5_int32 con_flags;
245
0
    struct gss_checksum_data *data = cksum_data;
246
0
    krb5_data credmsg;
247
0
    unsigned int junk;
248
0
    krb5_data *finished = NULL;
249
0
    krb5_key send_subkey;
250
0
    struct k5buf buf;
251
252
0
    data->checksum_data = empty_data();
253
0
    credmsg.data = 0;
254
    /* build the checksum field */
255
256
0
    if (data->ctx->gss_flags & GSS_C_DELEG_FLAG) {
257
        /* first get KRB_CRED message, so we know its length */
258
259
        /* clear the time check flag that was set in krb5_auth_con_init() */
260
0
        krb5_auth_con_getflags(context, auth_context, &con_flags);
261
0
        krb5_auth_con_setflags(context, auth_context,
262
0
                               con_flags & ~KRB5_AUTH_CONTEXT_DO_TIME);
263
264
0
        assert(data->cred->name != NULL);
265
266
        /*
267
         * RFC 4121 4.1.1 specifies forwarded credentials must be encrypted in
268
         * the session key, but krb5_fwd_tgt_creds will use the send subkey if
269
         * it's set in the auth context.  Suppress the send subkey
270
         * temporarily.
271
         */
272
0
        krb5_auth_con_getsendsubkey_k(context, auth_context, &send_subkey);
273
0
        krb5_auth_con_setsendsubkey_k(context, auth_context, NULL);
274
275
0
        code = krb5_fwd_tgt_creds(context, auth_context, 0,
276
0
                                  data->cred->name->princ, data->ctx->there->princ,
277
0
                                  data->cred->ccache, 1,
278
0
                                  &credmsg);
279
280
        /* Turn KRB5_AUTH_CONTEXT_DO_TIME back on and reset the send subkey. */
281
0
        krb5_auth_con_setflags(context, auth_context, con_flags);
282
0
        krb5_auth_con_setsendsubkey_k(context, auth_context, send_subkey);
283
0
        krb5_k_free_key(context, send_subkey);
284
285
0
        if (code) {
286
            /* don't fail here; just don't accept/do the delegation
287
               request */
288
0
            data->ctx->gss_flags &= ~(GSS_C_DELEG_FLAG |
289
0
                                      GSS_C_DELEG_POLICY_FLAG);
290
0
        } else {
291
0
            if (credmsg.length+28 > KRB5_INT16_MAX) {
292
0
                code = KRB5KRB_ERR_FIELD_TOOLONG;
293
0
                goto cleanup;
294
0
            }
295
0
        }
296
0
    }
297
#ifdef CFX_EXERCISE
298
    if (data->ctx->auth_context->keyblock != NULL
299
        && data->ctx->auth_context->keyblock->enctype == 18) {
300
        srand(time(0) ^ getpid());
301
        /* Our ftp client code stupidly assumes a base64-encoded
302
           version of the token will fit in 10K, so don't make this
303
           too big.  */
304
        junk = rand() & 0xff;
305
    } else
306
        junk = 0;
307
#else
308
0
    junk = 0;
309
0
#endif
310
311
0
    assert(data->exts != NULL);
312
313
0
    if (data->exts->iakerb.conv) {
314
0
        krb5_key key;
315
316
0
        code = krb5_auth_con_getsendsubkey_k(context, auth_context, &key);
317
0
        if (code != 0)
318
0
            goto cleanup;
319
320
0
        code = iakerb_make_finished(context, key, data->exts->iakerb.conv,
321
0
                                    &finished);
322
0
        if (code != 0) {
323
0
            krb5_k_free_key(context, key);
324
0
            goto cleanup;
325
0
        }
326
327
0
        krb5_k_free_key(context, key);
328
0
    }
329
330
    /* now allocate a buffer to hold the checksum data and
331
       (maybe) KRB_CRED msg */
332
0
    k5_buf_init_dynamic(&buf);
333
0
    k5_buf_add_uint32_le(&buf, data->md5.length);
334
0
    k5_buf_add_len(&buf, data->md5.contents, data->md5.length);
335
0
    k5_buf_add_uint32_le(&buf, data->ctx->gss_flags);
336
0
    if (credmsg.data != NULL) {
337
0
        k5_buf_add_uint16_le(&buf, KRB5_GSS_FOR_CREDS_OPTION);
338
0
        k5_buf_add_uint16_le(&buf, credmsg.length);
339
0
        k5_buf_add_len(&buf, credmsg.data, credmsg.length);
340
0
    }
341
0
    if (data->exts->iakerb.conv != NULL) {
342
0
        k5_buf_add_uint32_be(&buf, GSS_EXTS_FINISHED);
343
0
        k5_buf_add_uint32_be(&buf, finished->length);
344
0
        k5_buf_add_len(&buf, finished->data, finished->length);
345
0
    }
346
0
    while (junk--)
347
0
        k5_buf_add_byte(&buf, 'i');
348
349
0
    code = k5_buf_status(&buf);
350
0
    if (code)
351
0
        goto cleanup;
352
353
0
    data->checksum_data = make_data(buf.data, buf.len);
354
0
    *out = &data->checksum_data;
355
0
    code = 0;
356
357
0
cleanup:
358
0
    krb5_free_data_contents(context, &credmsg);
359
0
    krb5_free_data(context, finished);
360
0
    return code;
361
0
}
362
363
static krb5_error_code
364
make_ap_req_v1(krb5_context context, krb5_gss_ctx_id_rec *ctx,
365
               krb5_gss_cred_id_t cred, krb5_creds *k_cred,
366
               krb5_authdata_context ad_context,
367
               gss_channel_bindings_t chan_bindings, gss_OID mech_type,
368
               int cbt_flag, gss_buffer_t token, krb5_gss_ctx_ext_t exts)
369
0
{
370
0
    krb5_flags mk_req_flags = 0;
371
0
    krb5_error_code code;
372
0
    struct gss_checksum_data cksum_struct;
373
0
    krb5_checksum md5;
374
0
    krb5_data ap_req;
375
0
    unsigned char *t;
376
0
    unsigned int tlen;
377
0
    struct k5buf buf;
378
379
0
    k5_mutex_assert_locked(&cred->lock);
380
0
    ap_req.data = 0;
381
382
    /* compute the hash of the channel bindings */
383
384
0
    if ((code = kg_checksum_channel_bindings(context, chan_bindings, &md5)))
385
0
        return(code);
386
387
0
    krb5_auth_con_set_req_cksumtype(context, ctx->auth_context,
388
0
                                    CKSUMTYPE_KG_CB);
389
0
    cksum_struct.md5 = md5;
390
0
    cksum_struct.ctx = ctx;
391
0
    cksum_struct.cred = cred;
392
0
    cksum_struct.checksum_data.data = NULL;
393
0
    cksum_struct.exts = exts;
394
0
    krb5_auth_con_set_checksum_func(context, ctx->auth_context,
395
0
                                    make_gss_checksum, &cksum_struct);
396
397
    /* call mk_req.  subkey and ap_req need to be used or destroyed */
398
399
0
    mk_req_flags = AP_OPTS_USE_SUBKEY;
400
401
0
    if (ctx->gss_flags & GSS_C_MUTUAL_FLAG)
402
0
        mk_req_flags |= AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_ETYPE_NEGOTIATION;
403
0
    if (cbt_flag)
404
0
        mk_req_flags |= AP_OPTS_CBT_FLAG;
405
406
0
    krb5_auth_con_set_authdata_context(context, ctx->auth_context, ad_context);
407
0
    code = krb5_mk_req_extended(context, &ctx->auth_context, mk_req_flags,
408
0
                                NULL, k_cred, &ap_req);
409
0
    krb5_auth_con_set_authdata_context(context, ctx->auth_context, NULL);
410
0
    krb5_free_checksum_contents(context, &cksum_struct.md5);
411
0
    krb5_free_data_contents(context, &cksum_struct.checksum_data);
412
0
    if (code)
413
0
        goto cleanup;
414
415
    /* store the interesting stuff from creds and authent */
416
0
    ctx->krb_times = k_cred->times;
417
0
    ctx->krb_flags = k_cred->ticket_flags;
418
419
    /* build up the token */
420
0
    if (ctx->gss_flags & GSS_C_DCE_STYLE) {
421
        /*
422
         * For DCE RPC, do not encapsulate the AP-REQ in the
423
         * typical GSS wrapping.
424
         */
425
0
        code = data_to_gss(&ap_req, token);
426
0
        if (code)
427
0
            goto cleanup;
428
0
    } else {
429
        /* allocate space for the token */
430
0
        tlen = g_token_size((gss_OID) mech_type, ap_req.length);
431
0
        t = gssalloc_malloc(tlen);
432
0
        if (t == NULL) {
433
0
            code = ENOMEM;
434
0
            goto cleanup;
435
0
        }
436
0
        k5_buf_init_fixed(&buf, t, tlen);
437
0
        g_make_token_header(&buf, mech_type, ap_req.length, KG_TOK_CTX_AP_REQ);
438
0
        k5_buf_add_len(&buf, ap_req.data, ap_req.length);
439
0
        assert(buf.len == tlen);
440
441
        /* pass it back */
442
443
0
        token->length = tlen;
444
0
        token->value = (void *) t;
445
0
    }
446
447
0
    code = 0;
448
449
0
cleanup:
450
0
    if (ap_req.data)
451
0
        krb5_free_data_contents(context, &ap_req);
452
453
0
    return (code);
454
0
}
455
456
/*
457
 * new_connection
458
 *
459
 * Do the grunt work of setting up a new context.
460
 */
461
static OM_uint32
462
kg_new_connection(
463
    OM_uint32 *minor_status,
464
    krb5_gss_cred_id_t cred,
465
    gss_ctx_id_t *context_handle,
466
    gss_name_t target_name,
467
    gss_OID mech_type,
468
    OM_uint32 req_flags,
469
    OM_uint32 time_req,
470
    gss_channel_bindings_t input_chan_bindings,
471
    gss_buffer_t input_token,
472
    gss_OID *actual_mech_type,
473
    gss_buffer_t output_token,
474
    OM_uint32 *ret_flags,
475
    OM_uint32 *time_rec,
476
    krb5_context context,
477
    krb5_gss_ctx_ext_t exts)
478
0
{
479
0
    OM_uint32 major_status;
480
0
    krb5_error_code code;
481
0
    krb5_creds *k_cred = NULL;
482
0
    krb5_gss_ctx_id_rec *ctx, *ctx_free;
483
0
    krb5_timestamp now;
484
0
    gss_buffer_desc token;
485
0
    krb5_keyblock *keyblock;
486
0
    int cbt_flag = (req_flags & GSS_C_CHANNEL_BOUND_FLAG) != 0;
487
488
0
    k5_mutex_assert_locked(&cred->lock);
489
0
    major_status = GSS_S_FAILURE;
490
0
    token.length = 0;
491
0
    token.value = NULL;
492
493
    /* make sure the cred is usable for init */
494
495
0
    if ((cred->usage != GSS_C_INITIATE) &&
496
0
        (cred->usage != GSS_C_BOTH)) {
497
0
        *minor_status = 0;
498
0
        return(GSS_S_NO_CRED);
499
0
    }
500
501
    /* complain if the input token is non-null */
502
503
0
    if (input_token != GSS_C_NO_BUFFER && input_token->length != 0) {
504
0
        *minor_status = 0;
505
0
        return(GSS_S_DEFECTIVE_TOKEN);
506
0
    }
507
508
    /* create the ctx */
509
510
0
    if ((ctx = (krb5_gss_ctx_id_rec *) xmalloc(sizeof(krb5_gss_ctx_id_rec)))
511
0
        == NULL) {
512
0
        *minor_status = ENOMEM;
513
0
        return(GSS_S_FAILURE);
514
0
    }
515
516
    /* fill in the ctx */
517
0
    memset(ctx, 0, sizeof(krb5_gss_ctx_id_rec));
518
0
    ctx->magic = KG_CONTEXT;
519
0
    ctx_free = ctx;
520
0
    if ((code = krb5_auth_con_init(context, &ctx->auth_context)))
521
0
        goto cleanup;
522
0
    krb5_auth_con_setflags(context, ctx->auth_context,
523
0
                           KRB5_AUTH_CONTEXT_DO_SEQUENCE);
524
525
    /* limit the encryption types negotiated (if requested) */
526
0
    if (cred->req_enctypes) {
527
0
        if ((code = krb5_set_default_tgs_enctypes(context,
528
0
                                                  cred->req_enctypes))) {
529
0
            goto cleanup;
530
0
        }
531
0
    }
532
533
0
    ctx->initiate = 1;
534
0
    ctx->seed_init = 0;
535
0
    ctx->seqstate = 0;
536
537
    /* enforce_ok_as_delegate causes GSS_C_DELEG_FLAG to be treated as
538
     * GSS_C_DELEG_POLICY_FLAG (so ok-as-delegate is always enforced). */
539
0
    if (context->enforce_ok_as_delegate && (req_flags & GSS_C_DELEG_FLAG)) {
540
0
        req_flags &= ~GSS_C_DELEG_FLAG;
541
0
        req_flags |= GSS_C_DELEG_POLICY_FLAG;
542
0
    }
543
544
    /* Don't include GSS_C_CHANNEL_BOUND_FLAG here; we don't want to put it on
545
     * the wire, and we only need to know about it for the first token. */
546
0
    ctx->gss_flags = req_flags & (GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG |
547
0
                                  GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG |
548
0
                                  GSS_C_SEQUENCE_FLAG | GSS_C_DELEG_FLAG |
549
0
                                  GSS_C_DCE_STYLE | GSS_C_IDENTIFY_FLAG |
550
0
                                  GSS_C_EXTENDED_ERROR_FLAG);
551
0
    ctx->gss_flags |= GSS_C_TRANS_FLAG;
552
0
    if (!cred->suppress_ci_flags)
553
0
        ctx->gss_flags |= (GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG);
554
0
    if (req_flags & GSS_C_DCE_STYLE)
555
0
        ctx->gss_flags |= GSS_C_MUTUAL_FLAG;
556
557
0
    if ((code = krb5_timeofday(context, &now)))
558
0
        goto cleanup;
559
560
0
    if (time_req == 0 || time_req == GSS_C_INDEFINITE) {
561
0
        ctx->krb_times.endtime = 0;
562
0
    } else {
563
0
        ctx->krb_times.endtime = ts_incr(now, time_req);
564
0
    }
565
566
0
    if ((code = kg_duplicate_name(context, cred->name, &ctx->here)))
567
0
        goto cleanup;
568
569
0
    if ((code = kg_duplicate_name(context, (krb5_gss_name_t)target_name,
570
0
                                  &ctx->there)))
571
0
        goto cleanup;
572
573
0
    code = get_credentials(context, cred, ctx->there, now,
574
0
                           ctx->krb_times.endtime, &k_cred);
575
0
    if (code)
576
0
        goto cleanup;
577
578
0
    ctx->krb_times = k_cred->times;
579
580
    /*
581
     * GSS_C_DELEG_POLICY_FLAG means to delegate only if the
582
     * ok-as-delegate ticket flag is set.
583
     */
584
0
    if ((req_flags & GSS_C_DELEG_POLICY_FLAG)
585
0
        && (k_cred->ticket_flags & TKT_FLG_OK_AS_DELEGATE))
586
0
        ctx->gss_flags |= GSS_C_DELEG_FLAG | GSS_C_DELEG_POLICY_FLAG;
587
588
0
    if (generic_gss_copy_oid(minor_status, mech_type, &ctx->mech_used)
589
0
        != GSS_S_COMPLETE) {
590
0
        code = *minor_status;
591
0
        goto cleanup;
592
0
    }
593
    /*
594
     * Now try to make it static if at all possible....
595
     */
596
0
    ctx->mech_used = krb5_gss_convert_static_mech_oid(ctx->mech_used);
597
598
0
    {
599
        /* gsskrb5 v1 */
600
0
        krb5_int32 seq_temp;
601
0
        if ((code = make_ap_req_v1(context, ctx,
602
0
                                   cred, k_cred, ctx->here->ad_context,
603
0
                                   input_chan_bindings, mech_type, cbt_flag,
604
0
                                   &token, exts))) {
605
0
            if ((code == KRB5_FCC_NOFILE) || (code == KRB5_CC_NOTFOUND) ||
606
0
                (code == KG_EMPTY_CCACHE))
607
0
                major_status = GSS_S_NO_CRED;
608
0
            if (code == KRB5KRB_AP_ERR_TKT_EXPIRED)
609
0
                major_status = GSS_S_CREDENTIALS_EXPIRED;
610
0
            goto cleanup;
611
0
        }
612
613
0
        krb5_auth_con_getlocalseqnumber(context, ctx->auth_context, &seq_temp);
614
0
        ctx->seq_send = (uint32_t)seq_temp;
615
0
        code = krb5_auth_con_getsendsubkey(context, ctx->auth_context,
616
0
                                           &keyblock);
617
0
        if (code != 0)
618
0
            goto cleanup;
619
0
        code = krb5_k_create_key(context, keyblock, &ctx->subkey);
620
0
        krb5_free_keyblock(context, keyblock);
621
0
        if (code != 0)
622
0
            goto cleanup;
623
0
    }
624
625
0
    ctx->enc = NULL;
626
0
    ctx->seq = NULL;
627
0
    ctx->have_acceptor_subkey = 0;
628
0
    code = kg_setup_keys(context, ctx, ctx->subkey, &ctx->cksumtype);
629
0
    if (code != 0)
630
0
        goto cleanup;
631
632
0
    if (!(ctx->gss_flags & GSS_C_MUTUAL_FLAG)) {
633
        /* There will be no AP-REP, so set up sequence state now. */
634
0
        ctx->seq_recv = ctx->seq_send;
635
0
        code = g_seqstate_init(&ctx->seqstate, ctx->seq_recv,
636
0
                               (ctx->gss_flags & GSS_C_REPLAY_FLAG) != 0,
637
0
                               (ctx->gss_flags & GSS_C_SEQUENCE_FLAG) != 0,
638
0
                               ctx->proto);
639
0
        if (code != 0)
640
0
            goto cleanup;
641
0
    }
642
643
    /* compute time_rec */
644
0
    if (time_rec) {
645
0
        if ((code = krb5_timeofday(context, &now)))
646
0
            goto cleanup;
647
0
        *time_rec = ts_interval(now, ctx->krb_times.endtime);
648
0
    }
649
650
    /* set the other returns */
651
0
    *output_token = token;
652
653
0
    if (ret_flags)
654
0
        *ret_flags = ctx->gss_flags;
655
656
0
    if (actual_mech_type)
657
0
        *actual_mech_type = mech_type;
658
659
    /* return successfully */
660
661
0
    *context_handle = (gss_ctx_id_t) ctx;
662
0
    ctx_free = NULL;
663
0
    if (ctx->gss_flags & GSS_C_MUTUAL_FLAG) {
664
0
        ctx->established = 0;
665
0
        major_status = GSS_S_CONTINUE_NEEDED;
666
0
    } else {
667
0
        ctx->gss_flags |= GSS_C_PROT_READY_FLAG;
668
0
        ctx->established = 1;
669
0
        major_status = GSS_S_COMPLETE;
670
0
    }
671
672
0
cleanup:
673
0
    krb5_free_creds(context, k_cred);
674
0
    if (ctx_free) {
675
0
        if (ctx_free->auth_context)
676
0
            krb5_auth_con_free(context, ctx_free->auth_context);
677
0
        if (ctx_free->here)
678
0
            kg_release_name(context, &ctx_free->here);
679
0
        if (ctx_free->there)
680
0
            kg_release_name(context, &ctx_free->there);
681
0
        if (ctx_free->subkey)
682
0
            krb5_k_free_key(context, ctx_free->subkey);
683
0
        xfree(ctx_free);
684
0
    }
685
686
0
    *minor_status = code;
687
0
    return (major_status);
688
0
}
689
690
/*
691
 * mutual_auth
692
 *
693
 * Handle the reply from the acceptor, if we're doing mutual auth.
694
 */
695
static OM_uint32
696
mutual_auth(
697
    OM_uint32 *minor_status,
698
    gss_ctx_id_t *context_handle,
699
    gss_name_t target_name,
700
    gss_OID mech_type,
701
    OM_uint32 req_flags,
702
    OM_uint32 time_req,
703
    gss_channel_bindings_t input_chan_bindings,
704
    gss_buffer_t input_token,
705
    gss_OID *actual_mech_type,
706
    gss_buffer_t output_token,
707
    OM_uint32 *ret_flags,
708
    OM_uint32 *time_rec,
709
    krb5_context context)
710
0
{
711
0
    OM_uint32 major_status;
712
0
    unsigned char *ptr;
713
0
    krb5_data ap_rep;
714
0
    krb5_ap_rep_enc_part *ap_rep_data;
715
0
    krb5_timestamp now;
716
0
    krb5_gss_ctx_id_rec *ctx;
717
0
    krb5_error *krb_error;
718
0
    krb5_error_code code;
719
0
    krb5int_access kaccess;
720
721
0
    major_status = GSS_S_FAILURE;
722
723
0
    code = krb5int_accessor (&kaccess, KRB5INT_ACCESS_VERSION);
724
0
    if (code)
725
0
        goto fail;
726
727
0
    ctx = (krb5_gss_ctx_id_t) *context_handle;
728
729
    /* make sure the context is non-established, and that certain
730
       arguments are unchanged */
731
732
0
    if ((ctx->established) ||
733
0
        ((ctx->gss_flags & GSS_C_MUTUAL_FLAG) == 0)) {
734
0
        code = KG_CONTEXT_ESTABLISHED;
735
0
        goto fail;
736
0
    }
737
738
0
    if (! kg_compare_name(context, ctx->there, (krb5_gss_name_t)target_name)) {
739
0
        (void)krb5_gss_delete_sec_context(minor_status,
740
0
                                          context_handle, NULL);
741
0
        code = 0;
742
0
        major_status = GSS_S_BAD_NAME;
743
0
        goto fail;
744
0
    }
745
746
    /* verify the token and leave the AP_REP message in ap_rep */
747
748
0
    if (input_token == GSS_C_NO_BUFFER) {
749
0
        (void)krb5_gss_delete_sec_context(minor_status,
750
0
                                          context_handle, NULL);
751
0
        code = 0;
752
0
        major_status = GSS_S_DEFECTIVE_TOKEN;
753
0
        goto fail;
754
0
    }
755
756
0
    ptr = (unsigned char *) input_token->value;
757
758
0
    if (ctx->gss_flags & GSS_C_DCE_STYLE) {
759
        /* Raw AP-REP */
760
0
        ap_rep.length = input_token->length;
761
0
    } else if (g_verify_token_header(ctx->mech_used,
762
0
                                     &(ap_rep.length),
763
0
                                     &ptr, KG_TOK_CTX_AP_REP,
764
0
                                     input_token->length, 1)) {
765
0
        if (g_verify_token_header((gss_OID) ctx->mech_used,
766
0
                                  &(ap_rep.length),
767
0
                                  &ptr, KG_TOK_CTX_ERROR,
768
0
                                  input_token->length, 1) == 0) {
769
770
            /* Handle a KRB_ERROR message from the server */
771
772
0
            ap_rep.data = (char *)ptr;
773
0
            code = krb5_rd_error(context, &ap_rep, &krb_error);
774
0
            if (code)
775
0
                goto fail;
776
0
            if (krb_error->error)
777
0
                code = (krb5_error_code)krb_error->error + ERROR_TABLE_BASE_krb5;
778
0
            else
779
0
                code = 0;
780
0
            krb5_free_error(context, krb_error);
781
0
            goto fail;
782
0
        } else {
783
0
            *minor_status = 0;
784
0
            return(GSS_S_DEFECTIVE_TOKEN);
785
0
        }
786
0
    }
787
0
    ap_rep.data = (char *)ptr;
788
789
    /* decode the ap_rep */
790
0
    if ((code = krb5_rd_rep(context, ctx->auth_context, &ap_rep,
791
0
                            &ap_rep_data))) {
792
        /*
793
         * XXX A hack for backwards compatibility.
794
         * To be removed in 1999 -- proven
795
         */
796
0
        krb5_auth_con_setuseruserkey(context, ctx->auth_context,
797
0
                                     &ctx->subkey->keyblock);
798
0
        if ((krb5_rd_rep(context, ctx->auth_context, &ap_rep,
799
0
                         &ap_rep_data)))
800
0
            goto fail;
801
0
    }
802
803
    /* store away the sequence number */
804
0
    ctx->seq_recv = ap_rep_data->seq_number;
805
0
    code = g_seqstate_init(&ctx->seqstate, ctx->seq_recv,
806
0
                           (ctx->gss_flags & GSS_C_REPLAY_FLAG) != 0,
807
0
                           (ctx->gss_flags & GSS_C_SEQUENCE_FLAG) != 0,
808
0
                           ctx->proto);
809
0
    if (code) {
810
0
        krb5_free_ap_rep_enc_part(context, ap_rep_data);
811
0
        goto fail;
812
0
    }
813
814
0
    if (ap_rep_data->subkey != NULL &&
815
0
        (ctx->proto == 1 || (ctx->gss_flags & GSS_C_DCE_STYLE) ||
816
0
         ap_rep_data->subkey->enctype != ctx->subkey->keyblock.enctype)) {
817
        /* Keep acceptor's subkey.  */
818
0
        ctx->have_acceptor_subkey = 1;
819
0
        code = krb5_k_create_key(context, ap_rep_data->subkey,
820
0
                                 &ctx->acceptor_subkey);
821
0
        if (code) {
822
0
            krb5_free_ap_rep_enc_part(context, ap_rep_data);
823
0
            goto fail;
824
0
        }
825
0
        code = kg_setup_keys(context, ctx, ctx->acceptor_subkey,
826
0
                             &ctx->acceptor_subkey_cksumtype);
827
0
        if (code) {
828
0
            krb5_free_ap_rep_enc_part(context, ap_rep_data);
829
0
            goto fail;
830
0
        }
831
0
    }
832
    /* free the ap_rep_data */
833
0
    krb5_free_ap_rep_enc_part(context, ap_rep_data);
834
835
0
    if (ctx->gss_flags & GSS_C_DCE_STYLE) {
836
0
        krb5_data outbuf;
837
838
0
        code = krb5_mk_rep_dce(context, ctx->auth_context, &outbuf);
839
0
        if (code)
840
0
            goto fail;
841
842
0
        code = data_to_gss(&outbuf, output_token);
843
0
        if (code)
844
0
            goto fail;
845
0
    }
846
847
    /* set established */
848
0
    ctx->established = 1;
849
850
    /* set returns */
851
852
0
    if (time_rec) {
853
0
        if ((code = krb5_timeofday(context, &now)))
854
0
            goto fail;
855
0
        *time_rec = ts_interval(now, ctx->krb_times.endtime);
856
0
    }
857
858
0
    if (ret_flags)
859
0
        *ret_flags = ctx->gss_flags;
860
861
0
    if (actual_mech_type)
862
0
        *actual_mech_type = mech_type;
863
864
    /* success */
865
866
0
    *minor_status = 0;
867
0
    return GSS_S_COMPLETE;
868
869
0
fail:
870
0
    (void)krb5_gss_delete_sec_context(minor_status, context_handle, NULL);
871
872
0
    *minor_status = code;
873
0
    return (major_status);
874
0
}
875
876
OM_uint32
877
krb5_gss_init_sec_context_ext(
878
    OM_uint32 *minor_status,
879
    gss_cred_id_t claimant_cred_handle,
880
    gss_ctx_id_t *context_handle,
881
    gss_name_t target_name,
882
    gss_OID mech_type,
883
    OM_uint32 req_flags,
884
    OM_uint32 time_req,
885
    gss_channel_bindings_t input_chan_bindings,
886
    gss_buffer_t input_token,
887
    gss_OID *actual_mech_type,
888
    gss_buffer_t output_token,
889
    OM_uint32 *ret_flags,
890
    OM_uint32 *time_rec,
891
    krb5_gss_ctx_ext_t exts)
892
0
{
893
0
    krb5_context context;
894
0
    gss_cred_id_t defcred = GSS_C_NO_CREDENTIAL;
895
0
    krb5_gss_cred_id_t cred;
896
0
    krb5_error_code kerr;
897
0
    OM_uint32 major_status;
898
0
    OM_uint32 tmp_min_stat;
899
900
0
    if (*context_handle == GSS_C_NO_CONTEXT) {
901
0
        kerr = krb5_gss_init_context(&context);
902
0
        if (kerr) {
903
0
            *minor_status = kerr;
904
0
            return GSS_S_FAILURE;
905
0
        }
906
0
        if (GSS_ERROR(kg_sync_ccache_name(context, minor_status))) {
907
0
            save_error_info(*minor_status, context);
908
0
            krb5_free_context(context);
909
0
            return GSS_S_FAILURE;
910
0
        }
911
0
    } else {
912
0
        context = ((krb5_gss_ctx_id_rec *)*context_handle)->k5_context;
913
0
    }
914
915
    /* set up return values so they can be "freed" successfully */
916
917
0
    major_status = GSS_S_FAILURE; /* Default major code */
918
0
    output_token->length = 0;
919
0
    output_token->value = NULL;
920
0
    if (actual_mech_type)
921
0
        *actual_mech_type = NULL;
922
923
    /* verify the mech_type */
924
925
0
    if (mech_type == GSS_C_NULL_OID || g_OID_equal(mech_type, gss_mech_krb5)) {
926
0
        mech_type = (gss_OID) gss_mech_krb5;
927
0
    } else if (g_OID_equal(mech_type, gss_mech_krb5_old)) {
928
0
        mech_type = (gss_OID) gss_mech_krb5_old;
929
0
    } else if (g_OID_equal(mech_type, gss_mech_krb5_wrong)) {
930
0
        mech_type = (gss_OID) gss_mech_krb5_wrong;
931
0
    } else if (g_OID_equal(mech_type, gss_mech_iakerb)) {
932
0
        mech_type = (gss_OID) gss_mech_iakerb;
933
0
    } else {
934
0
        *minor_status = 0;
935
0
        if (*context_handle == GSS_C_NO_CONTEXT)
936
0
            krb5_free_context(context);
937
0
        return(GSS_S_BAD_MECH);
938
0
    }
939
940
    /* is this a new connection or not? */
941
942
    /*SUPPRESS 29*/
943
0
    if (*context_handle == GSS_C_NO_CONTEXT) {
944
        /* verify the credential, or use the default */
945
        /*SUPPRESS 29*/
946
0
        if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) {
947
0
            major_status = kg_get_defcred(minor_status, &defcred);
948
0
            if (major_status && GSS_ERROR(major_status)) {
949
0
                if (*context_handle == GSS_C_NO_CONTEXT)
950
0
                    krb5_free_context(context);
951
0
                return(major_status);
952
0
            }
953
0
            claimant_cred_handle = defcred;
954
0
        }
955
956
0
        major_status = kg_cred_resolve(minor_status, context,
957
0
                                       claimant_cred_handle, target_name);
958
0
        if (GSS_ERROR(major_status)) {
959
0
            save_error_info(*minor_status, context);
960
0
            krb5_gss_release_cred(&tmp_min_stat, &defcred);
961
0
            if (*context_handle == GSS_C_NO_CONTEXT)
962
0
                krb5_free_context(context);
963
0
            return(major_status);
964
0
        }
965
0
        cred = (krb5_gss_cred_id_t)claimant_cred_handle;
966
967
0
        major_status = kg_new_connection(minor_status, cred, context_handle,
968
0
                                         target_name, mech_type, req_flags,
969
0
                                         time_req, input_chan_bindings,
970
0
                                         input_token, actual_mech_type,
971
0
                                         output_token, ret_flags, time_rec,
972
0
                                         context, exts);
973
0
        k5_mutex_unlock(&cred->lock);
974
0
        krb5_gss_release_cred(&tmp_min_stat, &defcred);
975
0
        if (*context_handle == GSS_C_NO_CONTEXT) {
976
0
            save_error_info (*minor_status, context);
977
0
            krb5_free_context(context);
978
0
        } else
979
0
            ((krb5_gss_ctx_id_rec *) *context_handle)->k5_context = context;
980
0
    } else {
981
        /* mutual_auth doesn't care about the credentials */
982
0
        major_status = mutual_auth(minor_status, context_handle,
983
0
                                   target_name, mech_type, req_flags,
984
0
                                   time_req, input_chan_bindings,
985
0
                                   input_token, actual_mech_type,
986
0
                                   output_token, ret_flags, time_rec,
987
0
                                   context);
988
        /* If context_handle is now NO_CONTEXT, mutual_auth called
989
           delete_sec_context, which would've zapped the krb5 context
990
           too.  */
991
0
    }
992
993
0
    return(major_status);
994
0
}
995
996
#ifndef _WIN32
997
k5_mutex_t kg_kdc_flag_mutex = K5_MUTEX_PARTIAL_INITIALIZER;
998
static int kdc_flag = 0;
999
#endif
1000
1001
krb5_error_code
1002
krb5_gss_init_context (krb5_context *ctxp)
1003
53
{
1004
53
    krb5_error_code err;
1005
53
#ifndef _WIN32
1006
53
    int is_kdc;
1007
53
#endif
1008
1009
53
    err = gss_krb5int_initialize_library();
1010
53
    if (err)
1011
0
        return err;
1012
53
#ifndef _WIN32
1013
53
    k5_mutex_lock(&kg_kdc_flag_mutex);
1014
53
    is_kdc = kdc_flag;
1015
53
    k5_mutex_unlock(&kg_kdc_flag_mutex);
1016
1017
53
    if (is_kdc)
1018
0
        return krb5int_init_context_kdc(ctxp);
1019
53
#endif
1020
1021
53
    return krb5_init_context(ctxp);
1022
53
}
1023
1024
#ifndef _WIN32
1025
OM_uint32
1026
krb5int_gss_use_kdc_context(OM_uint32 *minor_status,
1027
                            const gss_OID desired_mech,
1028
                            const gss_OID desired_object,
1029
                            gss_buffer_t value)
1030
0
{
1031
0
    OM_uint32 err;
1032
1033
0
    *minor_status = 0;
1034
1035
0
    err = gss_krb5int_initialize_library();
1036
0
    if (err)
1037
0
        return err;
1038
0
    k5_mutex_lock(&kg_kdc_flag_mutex);
1039
0
    kdc_flag = 1;
1040
0
    k5_mutex_unlock(&kg_kdc_flag_mutex);
1041
0
    return GSS_S_COMPLETE;
1042
0
}
1043
#endif
1044
1045
OM_uint32 KRB5_CALLCONV
1046
krb5_gss_init_sec_context(OM_uint32 *minor_status,
1047
                          gss_cred_id_t claimant_cred_handle,
1048
                          gss_ctx_id_t *context_handle,
1049
                          gss_name_t target_name, gss_OID mech_type,
1050
                          OM_uint32 req_flags, OM_uint32 time_req,
1051
                          gss_channel_bindings_t input_chan_bindings,
1052
                          gss_buffer_t input_token, gss_OID *actual_mech_type,
1053
                          gss_buffer_t output_token, OM_uint32 *ret_flags,
1054
                          OM_uint32 *time_rec)
1055
0
{
1056
0
    krb5_gss_ctx_ext_rec exts;
1057
1058
0
    memset(&exts, 0, sizeof(exts));
1059
1060
0
    return krb5_gss_init_sec_context_ext(minor_status,
1061
0
                                         claimant_cred_handle,
1062
0
                                         context_handle,
1063
0
                                         target_name,
1064
0
                                         mech_type,
1065
0
                                         req_flags,
1066
0
                                         time_req,
1067
0
                                         input_chan_bindings,
1068
0
                                         input_token,
1069
0
                                         actual_mech_type,
1070
0
                                         output_token,
1071
0
                                         ret_flags,
1072
0
                                         time_rec,
1073
0
                                         &exts);
1074
0
}