~hartmans/ubuntu/trusty/krb5/gss-infinite-loop

« back to all changes in this revision

Viewing changes to .pc/CVE-2014-4344.patch/src/lib/gssapi/spnego/spnego_mech.c

  • Committer: Sam Hartman
  • Date: 2014-08-12 11:31:13 UTC
  • mfrom: (59.1.1 krb5)
  • Revision ID: hartmans@debian.org-20140812113113-wxcusslnf8u2pjhc
* SECURITY UPDATE: denial of service via invalid tokens
  - debian/patches/CVE-2014-4341-4342.patch: handle invalid tokens in
    src/lib/gssapi/krb5/k5unseal.c, src/lib/gssapi/krb5/k5unsealiov.c.
  - CVE-2014-4341
  - CVE-2014-4342
* SECURITY UPDATE: denial of service via double-free in SPNEGO
  - debian/patches/CVE-2014-4343.patch: fix double-free in
    src/lib/gssapi/spnego/spnego_mech.c.
  - CVE-2014-4343
* SECURITY UPDATE: denial of service via null deref in SPNEGO acceptor
  - debian/patches/CVE-2014-4344.patch: validate REMAIN in
    src/lib/gssapi/spnego/spnego_mech.c.
  - CVE-2014-4344
* SECURITY UPDATE: denial of service and possible code execution in
  kadmind with LDAP backend
  - debian/patches/CVE-2014-4345.patch: fix off-by-one in
    src/plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c
  - CVE-2014-4345

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2006,2008 by the Massachusetts Institute of Technology.
 
3
 * All rights reserved.
 
4
 *
 
5
 * Export of this software from the United States of America may
 
6
 *   require a specific license from the United States Government.
 
7
 *   It is the responsibility of any person or organization contemplating
 
8
 *   export to obtain such a license before exporting.
 
9
 *
 
10
 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
 
11
 * distribute this software and its documentation for any purpose and
 
12
 * without fee is hereby granted, provided that the above copyright
 
13
 * notice appear in all copies and that both that copyright notice and
 
14
 * this permission notice appear in supporting documentation, and that
 
15
 * the name of M.I.T. not be used in advertising or publicity pertaining
 
16
 * to distribution of the software without specific, written prior
 
17
 * permission.  Furthermore if you modify this software you must label
 
18
 * your software as modified software and not distribute it in such a
 
19
 * fashion that it might be confused with the original M.I.T. software.
 
20
 * M.I.T. makes no representations about the suitability of
 
21
 * this software for any purpose.  It is provided "as is" without express
 
22
 * or implied warranty.
 
23
 */
 
24
/*
 
25
 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
 
26
 * Use is subject to license terms.
 
27
 *
 
28
 * A module that implements the spnego security mechanism.
 
29
 * It is used to negotiate the security mechanism between
 
30
 * peers using the GSS-API.  SPNEGO is specified in RFC 4178.
 
31
 *
 
32
 */
 
33
/*
 
34
 * Copyright (c) 2006-2008, Novell, Inc.
 
35
 * All rights reserved.
 
36
 *
 
37
 * Redistribution and use in source and binary forms, with or without
 
38
 * modification, are permitted provided that the following conditions are met:
 
39
 *
 
40
 *   * Redistributions of source code must retain the above copyright notice,
 
41
 *       this list of conditions and the following disclaimer.
 
42
 *   * Redistributions in binary form must reproduce the above copyright
 
43
 *       notice, this list of conditions and the following disclaimer in the
 
44
 *       documentation and/or other materials provided with the distribution.
 
45
 *   * The copyright holder's name is not used to endorse or promote products
 
46
 *       derived from this software without specific prior written permission.
 
47
 *
 
48
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 
49
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 
50
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 
51
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 
52
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 
53
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 
54
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 
55
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 
56
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 
57
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 
58
 * POSSIBILITY OF SUCH DAMAGE.
 
59
 */
 
60
/* #pragma ident        "@(#)spnego_mech.c      1.7     04/09/28 SMI" */
 
61
 
 
62
#include        <assert.h>
 
63
#include        <stdio.h>
 
64
#include        <stdlib.h>
 
65
#include        <string.h>
 
66
#include        <k5-int.h>
 
67
#include        <krb5.h>
 
68
#include        <mglueP.h>
 
69
#include        "gssapiP_spnego.h"
 
70
#include        <gssapi_err_generic.h>
 
71
 
 
72
#ifndef MAXHOSTNAMELEN
 
73
#define MAXHOSTNAMELEN 64
 
74
#endif
 
75
 
 
76
#undef g_token_size
 
77
#undef g_verify_token_header
 
78
#undef g_make_token_header
 
79
 
 
80
#define HARD_ERROR(v) ((v) != GSS_S_COMPLETE && (v) != GSS_S_CONTINUE_NEEDED)
 
81
typedef const gss_OID_desc *gss_OID_const;
 
82
 
 
83
/* der routines defined in libgss */
 
84
extern unsigned int gssint_der_length_size(unsigned int);
 
85
extern int gssint_get_der_length(unsigned char **, unsigned int,
 
86
                                 unsigned int*);
 
87
extern int gssint_put_der_length(unsigned int, unsigned char **, unsigned int);
 
88
 
 
89
 
 
90
/* private routines for spnego_mechanism */
 
91
static spnego_token_t make_spnego_token(char *);
 
92
static gss_buffer_desc make_err_msg(char *);
 
93
static int g_token_size(gss_OID_const, unsigned int);
 
94
static int g_make_token_header(gss_OID_const, unsigned int,
 
95
                               unsigned char **, unsigned int);
 
96
static int g_verify_token_header(gss_OID_const, unsigned int *,
 
97
                                 unsigned char **,
 
98
                                 int, unsigned int);
 
99
static int g_verify_neg_token_init(unsigned char **, unsigned int);
 
100
static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);
 
101
static gss_buffer_t get_input_token(unsigned char **, unsigned int);
 
102
static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);
 
103
static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);
 
104
static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t,
 
105
                                     gss_const_key_value_set_t,
 
106
                                     gss_cred_id_t *, gss_OID_set *);
 
107
static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_cred_id_t,
 
108
                                      gss_cred_usage_t, gss_OID_set *);
 
109
static void release_spnego_ctx(spnego_gss_ctx_id_t *);
 
110
static void check_spnego_options(spnego_gss_ctx_id_t);
 
111
static spnego_gss_ctx_id_t create_spnego_ctx(void);
 
112
static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);
 
113
static int put_input_token(unsigned char **, gss_buffer_t, unsigned int);
 
114
static int put_mech_oid(unsigned char **, gss_OID_const, unsigned int);
 
115
static int put_negResult(unsigned char **, OM_uint32, unsigned int);
 
116
 
 
117
static OM_uint32
 
118
process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
 
119
            gss_buffer_t *, OM_uint32 *, send_token_flag *);
 
120
static OM_uint32
 
121
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
 
122
           gss_buffer_t *, OM_uint32 *, send_token_flag *);
 
123
 
 
124
static OM_uint32
 
125
init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, gss_ctx_id_t *,
 
126
             send_token_flag *);
 
127
static OM_uint32
 
128
init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
 
129
              gss_buffer_t *, gss_buffer_t *,
 
130
              OM_uint32 *, send_token_flag *);
 
131
static OM_uint32
 
132
init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
 
133
              gss_buffer_t *, gss_buffer_t *,
 
134
              OM_uint32 *, send_token_flag *);
 
135
static OM_uint32
 
136
init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
 
137
                  gss_OID, gss_buffer_t *, gss_buffer_t *,
 
138
                  OM_uint32 *, send_token_flag *);
 
139
static OM_uint32
 
140
init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
 
141
                   gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
 
142
                   gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
 
143
                   OM_uint32 *, send_token_flag *);
 
144
 
 
145
static OM_uint32
 
146
acc_ctx_new(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
 
147
            spnego_gss_cred_id_t, gss_buffer_t *,
 
148
            gss_buffer_t *, OM_uint32 *, send_token_flag *);
 
149
static OM_uint32
 
150
acc_ctx_cont(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
 
151
             gss_buffer_t *, gss_buffer_t *,
 
152
             OM_uint32 *, send_token_flag *);
 
153
static OM_uint32
 
154
acc_ctx_vfy_oid(OM_uint32 *, spnego_gss_ctx_id_t, gss_OID,
 
155
                OM_uint32 *, send_token_flag *);
 
156
static OM_uint32
 
157
acc_ctx_call_acc(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
 
158
                 gss_buffer_t, gss_OID *, gss_buffer_t,
 
159
                 OM_uint32 *, OM_uint32 *, gss_cred_id_t *,
 
160
                 OM_uint32 *, send_token_flag *);
 
161
 
 
162
static gss_OID
 
163
negotiate_mech(gss_OID_set, gss_OID_set, OM_uint32 *);
 
164
static int
 
165
g_get_tag_and_length(unsigned char **, int, unsigned int, unsigned int *);
 
166
 
 
167
static int
 
168
make_spnego_tokenInit_msg(spnego_gss_ctx_id_t,
 
169
                        int,
 
170
                        gss_buffer_t,
 
171
                        OM_uint32, gss_buffer_t, send_token_flag,
 
172
                        gss_buffer_t);
 
173
static int
 
174
make_spnego_tokenTarg_msg(OM_uint32, gss_OID, gss_buffer_t,
 
175
                        gss_buffer_t, send_token_flag,
 
176
                        gss_buffer_t);
 
177
 
 
178
static OM_uint32
 
179
get_negTokenInit(OM_uint32 *, gss_buffer_t, gss_buffer_t,
 
180
                 gss_OID_set *, OM_uint32 *, gss_buffer_t *,
 
181
                 gss_buffer_t *);
 
182
static OM_uint32
 
183
get_negTokenResp(OM_uint32 *, unsigned char *, unsigned int,
 
184
                 OM_uint32 *, gss_OID *, gss_buffer_t *, gss_buffer_t *);
 
185
 
 
186
static int
 
187
is_kerb_mech(gss_OID oid);
 
188
 
 
189
/* SPNEGO oid structure */
 
190
static const gss_OID_desc spnego_oids[] = {
 
191
        {SPNEGO_OID_LENGTH, SPNEGO_OID},
 
192
};
 
193
 
 
194
const gss_OID_desc * const gss_mech_spnego = spnego_oids+0;
 
195
static const gss_OID_set_desc spnego_oidsets[] = {
 
196
        {1, (gss_OID) spnego_oids+0},
 
197
};
 
198
const gss_OID_set_desc * const gss_mech_set_spnego = spnego_oidsets+0;
 
199
 
 
200
static int make_NegHints(OM_uint32 *, spnego_gss_cred_id_t, gss_buffer_t *);
 
201
static int put_neg_hints(unsigned char **, gss_buffer_t, unsigned int);
 
202
static OM_uint32
 
203
acc_ctx_hints(OM_uint32 *, gss_ctx_id_t *, spnego_gss_cred_id_t,
 
204
              gss_buffer_t *, OM_uint32 *, send_token_flag *);
 
205
 
 
206
/*
 
207
 * The Mech OID for SPNEGO:
 
208
 * { iso(1) org(3) dod(6) internet(1) security(5)
 
209
 *  mechanism(5) spnego(2) }
 
210
 */
 
211
static struct gss_config spnego_mechanism =
 
212
{
 
213
        {SPNEGO_OID_LENGTH, SPNEGO_OID},
 
214
        NULL,
 
215
        spnego_gss_acquire_cred,
 
216
        spnego_gss_release_cred,
 
217
        spnego_gss_init_sec_context,
 
218
#ifndef LEAN_CLIENT
 
219
        spnego_gss_accept_sec_context,
 
220
#else
 
221
        NULL,
 
222
#endif  /* LEAN_CLIENT */
 
223
        NULL,                           /* gss_process_context_token */
 
224
        spnego_gss_delete_sec_context,  /* gss_delete_sec_context */
 
225
        spnego_gss_context_time,        /* gss_context_time */
 
226
        spnego_gss_get_mic,             /* gss_get_mic */
 
227
        spnego_gss_verify_mic,          /* gss_verify_mic */
 
228
        spnego_gss_wrap,                /* gss_wrap */
 
229
        spnego_gss_unwrap,              /* gss_unwrap */
 
230
        spnego_gss_display_status,
 
231
        NULL,                           /* gss_indicate_mechs */
 
232
        spnego_gss_compare_name,
 
233
        spnego_gss_display_name,
 
234
        spnego_gss_import_name,
 
235
        spnego_gss_release_name,
 
236
        spnego_gss_inquire_cred,        /* gss_inquire_cred */
 
237
        NULL,                           /* gss_add_cred */
 
238
#ifndef LEAN_CLIENT
 
239
        spnego_gss_export_sec_context,          /* gss_export_sec_context */
 
240
        spnego_gss_import_sec_context,          /* gss_import_sec_context */
 
241
#else
 
242
        NULL,                           /* gss_export_sec_context */
 
243
        NULL,                           /* gss_import_sec_context */
 
244
#endif /* LEAN_CLIENT */
 
245
        NULL,                           /* gss_inquire_cred_by_mech */
 
246
        spnego_gss_inquire_names_for_mech,
 
247
        spnego_gss_inquire_context,     /* gss_inquire_context */
 
248
        NULL,                           /* gss_internal_release_oid */
 
249
        spnego_gss_wrap_size_limit,     /* gss_wrap_size_limit */
 
250
        NULL,                           /* gssd_pname_to_uid */
 
251
        NULL,                           /* gss_userok */
 
252
        NULL,                           /* gss_export_name */
 
253
        spnego_gss_duplicate_name,      /* gss_duplicate_name */
 
254
        NULL,                           /* gss_store_cred */
 
255
        spnego_gss_inquire_sec_context_by_oid, /* gss_inquire_sec_context_by_oid */
 
256
        spnego_gss_inquire_cred_by_oid, /* gss_inquire_cred_by_oid */
 
257
        spnego_gss_set_sec_context_option, /* gss_set_sec_context_option */
 
258
        spnego_gss_set_cred_option,     /* gssspi_set_cred_option */
 
259
        NULL,                           /* gssspi_mech_invoke */
 
260
        spnego_gss_wrap_aead,
 
261
        spnego_gss_unwrap_aead,
 
262
        spnego_gss_wrap_iov,
 
263
        spnego_gss_unwrap_iov,
 
264
        spnego_gss_wrap_iov_length,
 
265
        spnego_gss_complete_auth_token,
 
266
        spnego_gss_acquire_cred_impersonate_name,
 
267
        NULL,                           /* gss_add_cred_impersonate_name */
 
268
        spnego_gss_display_name_ext,
 
269
        spnego_gss_inquire_name,
 
270
        spnego_gss_get_name_attribute,
 
271
        spnego_gss_set_name_attribute,
 
272
        spnego_gss_delete_name_attribute,
 
273
        spnego_gss_export_name_composite,
 
274
        spnego_gss_map_name_to_any,
 
275
        spnego_gss_release_any_name_mapping,
 
276
        spnego_gss_pseudo_random,
 
277
        spnego_gss_set_neg_mechs,
 
278
        spnego_gss_inquire_saslname_for_mech,
 
279
        spnego_gss_inquire_mech_for_saslname,
 
280
        spnego_gss_inquire_attrs_for_mech,
 
281
        spnego_gss_acquire_cred_from,
 
282
        NULL,                           /* gss_store_cred_into */
 
283
        spnego_gss_acquire_cred_with_password,
 
284
        spnego_gss_export_cred,
 
285
        spnego_gss_import_cred,
 
286
        NULL,                           /* gssspi_import_sec_context_by_mech */
 
287
        NULL,                           /* gssspi_import_name_by_mech */
 
288
        NULL,                           /* gssspi_import_cred_by_mech */
 
289
        spnego_gss_get_mic_iov,
 
290
        spnego_gss_verify_mic_iov,
 
291
        spnego_gss_get_mic_iov_length
 
292
};
 
293
 
 
294
#ifdef _GSS_STATIC_LINK
 
295
#include "mglueP.h"
 
296
 
 
297
static int gss_spnegomechglue_init(void)
 
298
{
 
299
        struct gss_mech_config mech_spnego;
 
300
 
 
301
        memset(&mech_spnego, 0, sizeof(mech_spnego));
 
302
        mech_spnego.mech = &spnego_mechanism;
 
303
        mech_spnego.mechNameStr = "spnego";
 
304
        mech_spnego.mech_type = GSS_C_NO_OID;
 
305
 
 
306
        return gssint_register_mechinfo(&mech_spnego);
 
307
}
 
308
#else
 
309
gss_mechanism KRB5_CALLCONV
 
310
gss_mech_initialize(void)
 
311
{
 
312
        return (&spnego_mechanism);
 
313
}
 
314
 
 
315
MAKE_INIT_FUNCTION(gss_krb5int_lib_init);
 
316
MAKE_FINI_FUNCTION(gss_krb5int_lib_fini);
 
317
int gss_krb5int_lib_init(void);
 
318
#endif /* _GSS_STATIC_LINK */
 
319
 
 
320
int gss_spnegoint_lib_init(void)
 
321
{
 
322
#ifdef _GSS_STATIC_LINK
 
323
        return gss_spnegomechglue_init();
 
324
#else
 
325
        return 0;
 
326
#endif
 
327
}
 
328
 
 
329
void gss_spnegoint_lib_fini(void)
 
330
{
 
331
}
 
332
 
 
333
/*ARGSUSED*/
 
334
OM_uint32 KRB5_CALLCONV
 
335
spnego_gss_acquire_cred(OM_uint32 *minor_status,
 
336
                        gss_name_t desired_name,
 
337
                        OM_uint32 time_req,
 
338
                        gss_OID_set desired_mechs,
 
339
                        gss_cred_usage_t cred_usage,
 
340
                        gss_cred_id_t *output_cred_handle,
 
341
                        gss_OID_set *actual_mechs,
 
342
                        OM_uint32 *time_rec)
 
343
{
 
344
    return spnego_gss_acquire_cred_from(minor_status, desired_name, time_req,
 
345
                                        desired_mechs, cred_usage, NULL,
 
346
                                        output_cred_handle, actual_mechs,
 
347
                                        time_rec);
 
348
}
 
349
 
 
350
/*ARGSUSED*/
 
351
OM_uint32 KRB5_CALLCONV
 
352
spnego_gss_acquire_cred_from(OM_uint32 *minor_status,
 
353
                             const gss_name_t desired_name,
 
354
                             OM_uint32 time_req,
 
355
                             const gss_OID_set desired_mechs,
 
356
                             gss_cred_usage_t cred_usage,
 
357
                             gss_const_key_value_set_t cred_store,
 
358
                             gss_cred_id_t *output_cred_handle,
 
359
                             gss_OID_set *actual_mechs,
 
360
                             OM_uint32 *time_rec)
 
361
{
 
362
        OM_uint32 status, tmpmin;
 
363
        gss_OID_set amechs;
 
364
        gss_cred_id_t mcred = NULL;
 
365
        spnego_gss_cred_id_t spcred = NULL;
 
366
        dsyslog("Entering spnego_gss_acquire_cred\n");
 
367
 
 
368
        if (actual_mechs)
 
369
                *actual_mechs = NULL;
 
370
 
 
371
        if (time_rec)
 
372
                *time_rec = 0;
 
373
 
 
374
        /* We will obtain a mechglue credential and wrap it in a
 
375
         * spnego_gss_cred_id_rec structure.  Allocate the wrapper. */
 
376
        spcred = malloc(sizeof(spnego_gss_cred_id_rec));
 
377
        if (spcred == NULL) {
 
378
                *minor_status = ENOMEM;
 
379
                return (GSS_S_FAILURE);
 
380
        }
 
381
        spcred->neg_mechs = GSS_C_NULL_OID_SET;
 
382
 
 
383
        /*
 
384
         * Always use get_available_mechs to collect a list of
 
385
         * mechs for which creds are available.
 
386
         */
 
387
        status = get_available_mechs(minor_status, desired_name,
 
388
                                     cred_usage, cred_store, &mcred,
 
389
                                     &amechs);
 
390
 
 
391
        if (actual_mechs && amechs != GSS_C_NULL_OID_SET) {
 
392
                (void) gssint_copy_oid_set(&tmpmin, amechs, actual_mechs);
 
393
        }
 
394
        (void) gss_release_oid_set(&tmpmin, &amechs);
 
395
 
 
396
        if (status == GSS_S_COMPLETE) {
 
397
                spcred->mcred = mcred;
 
398
                *output_cred_handle = (gss_cred_id_t)spcred;
 
399
        } else {
 
400
                free(spcred);
 
401
                *output_cred_handle = GSS_C_NO_CREDENTIAL;
 
402
        }
 
403
 
 
404
        dsyslog("Leaving spnego_gss_acquire_cred\n");
 
405
        return (status);
 
406
}
 
407
 
 
408
/*ARGSUSED*/
 
409
OM_uint32 KRB5_CALLCONV
 
410
spnego_gss_release_cred(OM_uint32 *minor_status,
 
411
                        gss_cred_id_t *cred_handle)
 
412
{
 
413
        spnego_gss_cred_id_t spcred = NULL;
 
414
 
 
415
        dsyslog("Entering spnego_gss_release_cred\n");
 
416
 
 
417
        if (minor_status == NULL || cred_handle == NULL)
 
418
                return (GSS_S_CALL_INACCESSIBLE_WRITE);
 
419
 
 
420
        *minor_status = 0;
 
421
 
 
422
        if (*cred_handle == GSS_C_NO_CREDENTIAL)
 
423
                return (GSS_S_COMPLETE);
 
424
 
 
425
        spcred = (spnego_gss_cred_id_t)*cred_handle;
 
426
        *cred_handle = GSS_C_NO_CREDENTIAL;
 
427
        gss_release_oid_set(minor_status, &spcred->neg_mechs);
 
428
        gss_release_cred(minor_status, &spcred->mcred);
 
429
        free(spcred);
 
430
 
 
431
        dsyslog("Leaving spnego_gss_release_cred\n");
 
432
        return (GSS_S_COMPLETE);
 
433
}
 
434
 
 
435
static void
 
436
check_spnego_options(spnego_gss_ctx_id_t spnego_ctx)
 
437
{
 
438
        spnego_ctx->optionStr = gssint_get_modOptions(
 
439
                (const gss_OID)&spnego_oids[0]);
 
440
}
 
441
 
 
442
static spnego_gss_ctx_id_t
 
443
create_spnego_ctx(void)
 
444
{
 
445
        spnego_gss_ctx_id_t spnego_ctx = NULL;
 
446
        spnego_ctx = (spnego_gss_ctx_id_t)
 
447
                malloc(sizeof (spnego_gss_ctx_id_rec));
 
448
 
 
449
        if (spnego_ctx == NULL) {
 
450
                return (NULL);
 
451
        }
 
452
 
 
453
        spnego_ctx->magic_num = SPNEGO_MAGIC_ID;
 
454
        spnego_ctx->ctx_handle = GSS_C_NO_CONTEXT;
 
455
        spnego_ctx->mech_set = NULL;
 
456
        spnego_ctx->internal_mech = NULL;
 
457
        spnego_ctx->optionStr = NULL;
 
458
        spnego_ctx->DER_mechTypes.length = 0;
 
459
        spnego_ctx->DER_mechTypes.value = NULL;
 
460
        spnego_ctx->default_cred = GSS_C_NO_CREDENTIAL;
 
461
        spnego_ctx->mic_reqd = 0;
 
462
        spnego_ctx->mic_sent = 0;
 
463
        spnego_ctx->mic_rcvd = 0;
 
464
        spnego_ctx->mech_complete = 0;
 
465
        spnego_ctx->nego_done = 0;
 
466
        spnego_ctx->internal_name = GSS_C_NO_NAME;
 
467
        spnego_ctx->actual_mech = GSS_C_NO_OID;
 
468
 
 
469
        check_spnego_options(spnego_ctx);
 
470
 
 
471
        return (spnego_ctx);
 
472
}
 
473
 
 
474
/*
 
475
 * Both initiator and acceptor call here to verify and/or create mechListMIC,
 
476
 * and to consistency-check the MIC state.  handle_mic is invoked only if the
 
477
 * negotiated mech has completed and supports MICs.
 
478
 */
 
479
static OM_uint32
 
480
handle_mic(OM_uint32 *minor_status, gss_buffer_t mic_in,
 
481
           int send_mechtok, spnego_gss_ctx_id_t sc,
 
482
           gss_buffer_t *mic_out,
 
483
           OM_uint32 *negState, send_token_flag *tokflag)
 
484
{
 
485
        OM_uint32 ret;
 
486
 
 
487
        ret = GSS_S_FAILURE;
 
488
        *mic_out = GSS_C_NO_BUFFER;
 
489
        if (mic_in != GSS_C_NO_BUFFER) {
 
490
                if (sc->mic_rcvd) {
 
491
                        /* Reject MIC if we've already received a MIC. */
 
492
                        *negState = REJECT;
 
493
                        *tokflag = ERROR_TOKEN_SEND;
 
494
                        return GSS_S_DEFECTIVE_TOKEN;
 
495
                }
 
496
        } else if (sc->mic_reqd && !send_mechtok) {
 
497
                /*
 
498
                 * If the peer sends the final mechanism token, it
 
499
                 * must send the MIC with that token if the
 
500
                 * negotiation requires MICs.
 
501
                 */
 
502
                *negState = REJECT;
 
503
                *tokflag = ERROR_TOKEN_SEND;
 
504
                return GSS_S_DEFECTIVE_TOKEN;
 
505
        }
 
506
        ret = process_mic(minor_status, mic_in, sc, mic_out,
 
507
                          negState, tokflag);
 
508
        if (ret != GSS_S_COMPLETE) {
 
509
                return ret;
 
510
        }
 
511
        if (sc->mic_reqd) {
 
512
                assert(sc->mic_sent || sc->mic_rcvd);
 
513
        }
 
514
        if (sc->mic_sent && sc->mic_rcvd) {
 
515
                ret = GSS_S_COMPLETE;
 
516
                *negState = ACCEPT_COMPLETE;
 
517
                if (*mic_out == GSS_C_NO_BUFFER) {
 
518
                        /*
 
519
                         * We sent a MIC on the previous pass; we
 
520
                         * shouldn't be sending a mechanism token.
 
521
                         */
 
522
                        assert(!send_mechtok);
 
523
                        *tokflag = NO_TOKEN_SEND;
 
524
                } else {
 
525
                        *tokflag = CONT_TOKEN_SEND;
 
526
                }
 
527
        } else if (sc->mic_reqd) {
 
528
                *negState = ACCEPT_INCOMPLETE;
 
529
                ret = GSS_S_CONTINUE_NEEDED;
 
530
        } else if (*negState == ACCEPT_COMPLETE) {
 
531
                ret = GSS_S_COMPLETE;
 
532
        } else {
 
533
                ret = GSS_S_CONTINUE_NEEDED;
 
534
        }
 
535
        return ret;
 
536
}
 
537
 
 
538
/*
 
539
 * Perform the actual verification and/or generation of mechListMIC.
 
540
 */
 
541
static OM_uint32
 
542
process_mic(OM_uint32 *minor_status, gss_buffer_t mic_in,
 
543
            spnego_gss_ctx_id_t sc, gss_buffer_t *mic_out,
 
544
            OM_uint32 *negState, send_token_flag *tokflag)
 
545
{
 
546
        OM_uint32 ret, tmpmin;
 
547
        gss_qop_t qop_state;
 
548
        gss_buffer_desc tmpmic = GSS_C_EMPTY_BUFFER;
 
549
 
 
550
        ret = GSS_S_FAILURE;
 
551
        if (mic_in != GSS_C_NO_BUFFER) {
 
552
                ret = gss_verify_mic(minor_status, sc->ctx_handle,
 
553
                                     &sc->DER_mechTypes,
 
554
                                     mic_in, &qop_state);
 
555
                if (ret != GSS_S_COMPLETE) {
 
556
                        *negState = REJECT;
 
557
                        *tokflag = ERROR_TOKEN_SEND;
 
558
                        return ret;
 
559
                }
 
560
                /* If we got a MIC, we must send a MIC. */
 
561
                sc->mic_reqd = 1;
 
562
                sc->mic_rcvd = 1;
 
563
        }
 
564
        if (sc->mic_reqd && !sc->mic_sent) {
 
565
                ret = gss_get_mic(minor_status, sc->ctx_handle,
 
566
                                  GSS_C_QOP_DEFAULT,
 
567
                                  &sc->DER_mechTypes,
 
568
                                  &tmpmic);
 
569
                if (ret != GSS_S_COMPLETE) {
 
570
                        gss_release_buffer(&tmpmin, &tmpmic);
 
571
                        *tokflag = NO_TOKEN_SEND;
 
572
                        return ret;
 
573
                }
 
574
                *mic_out = malloc(sizeof(gss_buffer_desc));
 
575
                if (*mic_out == GSS_C_NO_BUFFER) {
 
576
                        gss_release_buffer(&tmpmin, &tmpmic);
 
577
                        *tokflag = NO_TOKEN_SEND;
 
578
                        return GSS_S_FAILURE;
 
579
                }
 
580
                **mic_out = tmpmic;
 
581
                sc->mic_sent = 1;
 
582
        }
 
583
        return GSS_S_COMPLETE;
 
584
}
 
585
 
 
586
/*
 
587
 * Initial call to spnego_gss_init_sec_context().
 
588
 */
 
589
static OM_uint32
 
590
init_ctx_new(OM_uint32 *minor_status,
 
591
             spnego_gss_cred_id_t spcred,
 
592
             gss_ctx_id_t *ctx,
 
593
             send_token_flag *tokflag)
 
594
{
 
595
        OM_uint32 ret;
 
596
        spnego_gss_ctx_id_t sc = NULL;
 
597
 
 
598
        sc = create_spnego_ctx();
 
599
        if (sc == NULL)
 
600
                return GSS_S_FAILURE;
 
601
 
 
602
        /* determine negotiation mech set */
 
603
        ret = get_negotiable_mechs(minor_status, spcred, GSS_C_INITIATE,
 
604
                                   &sc->mech_set);
 
605
        if (ret != GSS_S_COMPLETE)
 
606
                goto cleanup;
 
607
 
 
608
        /* Set an initial internal mech to make the first context token. */
 
609
        sc->internal_mech = &sc->mech_set->elements[0];
 
610
 
 
611
        if (put_mech_set(sc->mech_set, &sc->DER_mechTypes) < 0) {
 
612
                ret = GSS_S_FAILURE;
 
613
                goto cleanup;
 
614
        }
 
615
        /*
 
616
         * The actual context is not yet determined, set the output
 
617
         * context handle to refer to the spnego context itself.
 
618
         */
 
619
        sc->ctx_handle = GSS_C_NO_CONTEXT;
 
620
        *ctx = (gss_ctx_id_t)sc;
 
621
        sc = NULL;
 
622
        *tokflag = INIT_TOKEN_SEND;
 
623
        ret = GSS_S_CONTINUE_NEEDED;
 
624
 
 
625
cleanup:
 
626
        release_spnego_ctx(&sc);
 
627
        return ret;
 
628
}
 
629
 
 
630
/*
 
631
 * Called by second and later calls to spnego_gss_init_sec_context()
 
632
 * to decode reply and update state.
 
633
 */
 
634
static OM_uint32
 
635
init_ctx_cont(OM_uint32 *minor_status, gss_ctx_id_t *ctx, gss_buffer_t buf,
 
636
              gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
 
637
              OM_uint32 *negState, send_token_flag *tokflag)
 
638
{
 
639
        OM_uint32 ret, tmpmin, acc_negState;
 
640
        unsigned char *ptr;
 
641
        spnego_gss_ctx_id_t sc;
 
642
        gss_OID supportedMech = GSS_C_NO_OID;
 
643
 
 
644
        sc = (spnego_gss_ctx_id_t)*ctx;
 
645
        *negState = REJECT;
 
646
        *tokflag = ERROR_TOKEN_SEND;
 
647
 
 
648
        ptr = buf->value;
 
649
        ret = get_negTokenResp(minor_status, ptr, buf->length,
 
650
                               &acc_negState, &supportedMech,
 
651
                               responseToken, mechListMIC);
 
652
        if (ret != GSS_S_COMPLETE)
 
653
                goto cleanup;
 
654
        if (acc_negState == ACCEPT_DEFECTIVE_TOKEN &&
 
655
            supportedMech == GSS_C_NO_OID &&
 
656
            *responseToken == GSS_C_NO_BUFFER &&
 
657
            *mechListMIC == GSS_C_NO_BUFFER) {
 
658
                /* Reject "empty" token. */
 
659
                ret = GSS_S_DEFECTIVE_TOKEN;
 
660
        }
 
661
        if (acc_negState == REJECT) {
 
662
                *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
 
663
                map_errcode(minor_status);
 
664
                *tokflag = NO_TOKEN_SEND;
 
665
                ret = GSS_S_FAILURE;
 
666
                goto cleanup;
 
667
        }
 
668
        /*
 
669
         * nego_done is false for the first call to init_ctx_cont()
 
670
         */
 
671
        if (!sc->nego_done) {
 
672
                ret = init_ctx_nego(minor_status, sc,
 
673
                                    acc_negState,
 
674
                                    supportedMech, responseToken,
 
675
                                    mechListMIC,
 
676
                                    negState, tokflag);
 
677
        } else if ((!sc->mech_complete && *responseToken == GSS_C_NO_BUFFER) ||
 
678
                   (sc->mech_complete && *responseToken != GSS_C_NO_BUFFER)) {
 
679
                /* Missing or spurious token from acceptor. */
 
680
                ret = GSS_S_DEFECTIVE_TOKEN;
 
681
        } else if (!sc->mech_complete ||
 
682
                   (sc->mic_reqd &&
 
683
                    (sc->ctx_flags & GSS_C_INTEG_FLAG))) {
 
684
                /* Not obviously done; we may decide we're done later in
 
685
                 * init_ctx_call_init or handle_mic. */
 
686
                *negState = ACCEPT_INCOMPLETE;
 
687
                *tokflag = CONT_TOKEN_SEND;
 
688
                ret = GSS_S_CONTINUE_NEEDED;
 
689
        } else {
 
690
                /* mech finished on last pass and no MIC required, so done. */
 
691
                *negState = ACCEPT_COMPLETE;
 
692
                *tokflag = NO_TOKEN_SEND;
 
693
                ret = GSS_S_COMPLETE;
 
694
        }
 
695
cleanup:
 
696
        if (supportedMech != GSS_C_NO_OID)
 
697
                generic_gss_release_oid(&tmpmin, &supportedMech);
 
698
        return ret;
 
699
}
 
700
 
 
701
/*
 
702
 * Consistency checking and mechanism negotiation handling for second
 
703
 * call of spnego_gss_init_sec_context().  Call init_ctx_reselect() to
 
704
 * update internal state if acceptor has counter-proposed.
 
705
 */
 
706
static OM_uint32
 
707
init_ctx_nego(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
 
708
              OM_uint32 acc_negState, gss_OID supportedMech,
 
709
              gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
 
710
              OM_uint32 *negState, send_token_flag *tokflag)
 
711
{
 
712
        OM_uint32 ret;
 
713
 
 
714
        *negState = REJECT;
 
715
        *tokflag = ERROR_TOKEN_SEND;
 
716
        ret = GSS_S_DEFECTIVE_TOKEN;
 
717
        /*
 
718
         * Both supportedMech and negState must be present in first
 
719
         * acceptor token.
 
720
         */
 
721
        if (supportedMech == GSS_C_NO_OID) {
 
722
                *minor_status = ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR;
 
723
                map_errcode(minor_status);
 
724
                return GSS_S_DEFECTIVE_TOKEN;
 
725
        }
 
726
        if (acc_negState == ACCEPT_DEFECTIVE_TOKEN) {
 
727
                *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
 
728
                map_errcode(minor_status);
 
729
                return GSS_S_DEFECTIVE_TOKEN;
 
730
        }
 
731
 
 
732
        /*
 
733
         * If the mechanism we sent is not the mechanism returned from
 
734
         * the server, we need to handle the server's counter
 
735
         * proposal.  There is a bug in SAMBA servers that always send
 
736
         * the old Kerberos mech OID, even though we sent the new one.
 
737
         * So we will treat all the Kerberos mech OIDS as the same.
 
738
         */
 
739
        if (!(is_kerb_mech(supportedMech) &&
 
740
              is_kerb_mech(sc->internal_mech)) &&
 
741
            !g_OID_equal(supportedMech, sc->internal_mech)) {
 
742
                ret = init_ctx_reselect(minor_status, sc,
 
743
                                        acc_negState, supportedMech,
 
744
                                        responseToken, mechListMIC,
 
745
                                        negState, tokflag);
 
746
 
 
747
        } else if (*responseToken == GSS_C_NO_BUFFER) {
 
748
                if (sc->mech_complete) {
 
749
                        /*
 
750
                         * Mech completed on first call to its
 
751
                         * init_sec_context().  Acceptor sends no mech
 
752
                         * token.
 
753
                         */
 
754
                        *negState = ACCEPT_COMPLETE;
 
755
                        *tokflag = NO_TOKEN_SEND;
 
756
                        ret = GSS_S_COMPLETE;
 
757
                } else {
 
758
                        /*
 
759
                         * Reject missing mech token when optimistic
 
760
                         * mech selected.
 
761
                         */
 
762
                        *minor_status = ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR;
 
763
                        map_errcode(minor_status);
 
764
                        ret = GSS_S_DEFECTIVE_TOKEN;
 
765
                }
 
766
        } else if (sc->mech_complete) {
 
767
                /* Reject spurious mech token. */
 
768
                ret = GSS_S_DEFECTIVE_TOKEN;
 
769
        } else {
 
770
                *negState = ACCEPT_INCOMPLETE;
 
771
                *tokflag = CONT_TOKEN_SEND;
 
772
                ret = GSS_S_CONTINUE_NEEDED;
 
773
        }
 
774
        sc->nego_done = 1;
 
775
        return ret;
 
776
}
 
777
 
 
778
/*
 
779
 * Handle acceptor's counter-proposal of an alternative mechanism.
 
780
 */
 
781
static OM_uint32
 
782
init_ctx_reselect(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
 
783
                  OM_uint32 acc_negState, gss_OID supportedMech,
 
784
                  gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
 
785
                  OM_uint32 *negState, send_token_flag *tokflag)
 
786
{
 
787
        OM_uint32 tmpmin;
 
788
        size_t i;
 
789
 
 
790
        gss_delete_sec_context(&tmpmin, &sc->ctx_handle,
 
791
                               GSS_C_NO_BUFFER);
 
792
 
 
793
        /* Find supportedMech in sc->mech_set. */
 
794
        for (i = 0; i < sc->mech_set->count; i++) {
 
795
                if (g_OID_equal(supportedMech, &sc->mech_set->elements[i]))
 
796
                        break;
 
797
        }
 
798
        if (i == sc->mech_set->count)
 
799
                return GSS_S_DEFECTIVE_TOKEN;
 
800
        sc->internal_mech = &sc->mech_set->elements[i];
 
801
 
 
802
        /*
 
803
         * Windows 2003 and earlier don't correctly send a
 
804
         * negState of request-mic when counter-proposing a
 
805
         * mechanism.  They probably don't handle mechListMICs
 
806
         * properly either.
 
807
         */
 
808
        if (acc_negState != REQUEST_MIC)
 
809
                return GSS_S_DEFECTIVE_TOKEN;
 
810
 
 
811
        sc->mech_complete = 0;
 
812
        sc->mic_reqd = 1;
 
813
        *negState = REQUEST_MIC;
 
814
        *tokflag = CONT_TOKEN_SEND;
 
815
        return GSS_S_CONTINUE_NEEDED;
 
816
}
 
817
 
 
818
/*
 
819
 * Wrap call to mechanism gss_init_sec_context() and update state
 
820
 * accordingly.
 
821
 */
 
822
static OM_uint32
 
823
init_ctx_call_init(OM_uint32 *minor_status,
 
824
                   spnego_gss_ctx_id_t sc,
 
825
                   spnego_gss_cred_id_t spcred,
 
826
                   gss_name_t target_name,
 
827
                   OM_uint32 req_flags,
 
828
                   OM_uint32 time_req,
 
829
                   gss_buffer_t mechtok_in,
 
830
                   gss_OID *actual_mech,
 
831
                   gss_buffer_t mechtok_out,
 
832
                   OM_uint32 *ret_flags,
 
833
                   OM_uint32 *time_rec,
 
834
                   OM_uint32 *negState,
 
835
                   send_token_flag *send_token)
 
836
{
 
837
        OM_uint32 ret, tmpret, tmpmin;
 
838
        gss_cred_id_t mcred;
 
839
 
 
840
        mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
 
841
        ret = gss_init_sec_context(minor_status,
 
842
                                   mcred,
 
843
                                   &sc->ctx_handle,
 
844
                                   target_name,
 
845
                                   sc->internal_mech,
 
846
                                   (req_flags | GSS_C_INTEG_FLAG),
 
847
                                   time_req,
 
848
                                   GSS_C_NO_CHANNEL_BINDINGS,
 
849
                                   mechtok_in,
 
850
                                   &sc->actual_mech,
 
851
                                   mechtok_out,
 
852
                                   &sc->ctx_flags,
 
853
                                   time_rec);
 
854
        if (ret == GSS_S_COMPLETE) {
 
855
                sc->mech_complete = 1;
 
856
                if (ret_flags != NULL)
 
857
                        *ret_flags = sc->ctx_flags;
 
858
                /*
 
859
                 * Microsoft SPNEGO implementations expect an even number of
 
860
                 * token exchanges.  So if we're sending a final token, ask for
 
861
                 * a zero-length token back from the server.  Also ask for a
 
862
                 * token back if this is the first token or if a MIC exchange
 
863
                 * is required.
 
864
                 */
 
865
                if (*send_token == CONT_TOKEN_SEND &&
 
866
                    mechtok_out->length == 0 &&
 
867
                    (!sc->mic_reqd ||
 
868
                     !(sc->ctx_flags & GSS_C_INTEG_FLAG))) {
 
869
                        /* The exchange is complete. */
 
870
                        *negState = ACCEPT_COMPLETE;
 
871
                        ret = GSS_S_COMPLETE;
 
872
                        *send_token = NO_TOKEN_SEND;
 
873
                } else {
 
874
                        /* Ask for one more hop. */
 
875
                        *negState = ACCEPT_INCOMPLETE;
 
876
                        ret = GSS_S_CONTINUE_NEEDED;
 
877
                }
 
878
                return ret;
 
879
        }
 
880
 
 
881
        if (ret == GSS_S_CONTINUE_NEEDED)
 
882
                return ret;
 
883
 
 
884
        if (*send_token != INIT_TOKEN_SEND) {
 
885
                *send_token = ERROR_TOKEN_SEND;
 
886
                *negState = REJECT;
 
887
                return ret;
 
888
        }
 
889
 
 
890
        /*
 
891
         * Since this is the first token, we can fall back to later mechanisms
 
892
         * in the list.  Since the mechanism list is expected to be short, we
 
893
         * can do this with recursion.  If all mechanisms produce errors, the
 
894
         * caller should get the error from the first mech in the list.
 
895
         */
 
896
        memmove(sc->mech_set->elements, sc->mech_set->elements + 1,
 
897
                --sc->mech_set->count * sizeof(*sc->mech_set->elements));
 
898
        if (sc->mech_set->count == 0)
 
899
                goto fail;
 
900
        gss_release_buffer(&tmpmin, &sc->DER_mechTypes);
 
901
        if (put_mech_set(sc->mech_set, &sc->DER_mechTypes) < 0)
 
902
                goto fail;
 
903
        tmpret = init_ctx_call_init(&tmpmin, sc, spcred, target_name,
 
904
                                    req_flags, time_req, mechtok_in,
 
905
                                    actual_mech, mechtok_out, ret_flags,
 
906
                                    time_rec, negState, send_token);
 
907
        if (HARD_ERROR(tmpret))
 
908
                goto fail;
 
909
        *minor_status = tmpmin;
 
910
        return tmpret;
 
911
 
 
912
fail:
 
913
        /* Don't output token on error from first call. */
 
914
        *send_token = NO_TOKEN_SEND;
 
915
        *negState = REJECT;
 
916
        return ret;
 
917
}
 
918
 
 
919
/*ARGSUSED*/
 
920
OM_uint32 KRB5_CALLCONV
 
921
spnego_gss_init_sec_context(
 
922
                        OM_uint32 *minor_status,
 
923
                        gss_cred_id_t claimant_cred_handle,
 
924
                        gss_ctx_id_t *context_handle,
 
925
                        gss_name_t target_name,
 
926
                        gss_OID mech_type,
 
927
                        OM_uint32 req_flags,
 
928
                        OM_uint32 time_req,
 
929
                        gss_channel_bindings_t input_chan_bindings,
 
930
                        gss_buffer_t input_token,
 
931
                        gss_OID *actual_mech,
 
932
                        gss_buffer_t output_token,
 
933
                        OM_uint32 *ret_flags,
 
934
                        OM_uint32 *time_rec)
 
935
{
 
936
        send_token_flag send_token = NO_TOKEN_SEND;
 
937
        OM_uint32 tmpmin, ret, negState;
 
938
        gss_buffer_t mechtok_in, mechListMIC_in, mechListMIC_out;
 
939
        gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER;
 
940
        spnego_gss_cred_id_t spcred = NULL;
 
941
        spnego_gss_ctx_id_t spnego_ctx = NULL;
 
942
 
 
943
        dsyslog("Entering init_sec_context\n");
 
944
 
 
945
        mechtok_in = mechListMIC_out = mechListMIC_in = GSS_C_NO_BUFFER;
 
946
        negState = REJECT;
 
947
 
 
948
        /*
 
949
         * This function works in three steps:
 
950
         *
 
951
         *   1. Perform mechanism negotiation.
 
952
         *   2. Invoke the negotiated or optimistic mech's gss_init_sec_context
 
953
         *      function and examine the results.
 
954
         *   3. Process or generate MICs if necessary.
 
955
         *
 
956
         * The three steps share responsibility for determining when the
 
957
         * exchange is complete.  If the selected mech completed in a previous
 
958
         * call and no MIC exchange is expected, then step 1 will decide.  If
 
959
         * the selected mech completes in this call and no MIC exchange is
 
960
         * expected, then step 2 will decide.  If a MIC exchange is expected,
 
961
         * then step 3 will decide.  If an error occurs in any step, the
 
962
         * exchange will be aborted, possibly with an error token.
 
963
         *
 
964
         * negState determines the state of the negotiation, and is
 
965
         * communicated to the acceptor if a continuing token is sent.
 
966
         * send_token is used to indicate what type of token, if any, should be
 
967
         * generated.
 
968
         */
 
969
 
 
970
        /* Validate arguments. */
 
971
        if (minor_status != NULL)
 
972
                *minor_status = 0;
 
973
        if (output_token != GSS_C_NO_BUFFER) {
 
974
                output_token->length = 0;
 
975
                output_token->value = NULL;
 
976
        }
 
977
        if (minor_status == NULL ||
 
978
            output_token == GSS_C_NO_BUFFER ||
 
979
            context_handle == NULL)
 
980
                return GSS_S_CALL_INACCESSIBLE_WRITE;
 
981
 
 
982
        if (actual_mech != NULL)
 
983
                *actual_mech = GSS_C_NO_OID;
 
984
 
 
985
        /* Step 1: perform mechanism negotiation. */
 
986
        spcred = (spnego_gss_cred_id_t)claimant_cred_handle;
 
987
        if (*context_handle == GSS_C_NO_CONTEXT) {
 
988
                ret = init_ctx_new(minor_status, spcred,
 
989
                                   context_handle, &send_token);
 
990
                if (ret != GSS_S_CONTINUE_NEEDED) {
 
991
                        goto cleanup;
 
992
                }
 
993
        } else {
 
994
                ret = init_ctx_cont(minor_status, context_handle,
 
995
                                    input_token, &mechtok_in,
 
996
                                    &mechListMIC_in, &negState, &send_token);
 
997
                if (HARD_ERROR(ret)) {
 
998
                        goto cleanup;
 
999
                }
 
1000
        }
 
1001
 
 
1002
        /* Step 2: invoke the selected or optimistic mechanism's
 
1003
         * gss_init_sec_context function, if it didn't complete previously. */
 
1004
        spnego_ctx = (spnego_gss_ctx_id_t)*context_handle;
 
1005
        if (!spnego_ctx->mech_complete) {
 
1006
                ret = init_ctx_call_init(
 
1007
                        minor_status, spnego_ctx, spcred,
 
1008
                        target_name, req_flags,
 
1009
                        time_req, mechtok_in,
 
1010
                        actual_mech, &mechtok_out,
 
1011
                        ret_flags, time_rec,
 
1012
                        &negState, &send_token);
 
1013
        }
 
1014
 
 
1015
        /* Step 3: process or generate the MIC, if the negotiated mech is
 
1016
         * complete and supports MICs. */
 
1017
        if (!HARD_ERROR(ret) && spnego_ctx->mech_complete &&
 
1018
            (spnego_ctx->ctx_flags & GSS_C_INTEG_FLAG)) {
 
1019
 
 
1020
                ret = handle_mic(minor_status,
 
1021
                                 mechListMIC_in,
 
1022
                                 (mechtok_out.length != 0),
 
1023
                                 spnego_ctx, &mechListMIC_out,
 
1024
                                 &negState, &send_token);
 
1025
        }
 
1026
cleanup:
 
1027
        if (send_token == INIT_TOKEN_SEND) {
 
1028
                if (make_spnego_tokenInit_msg(spnego_ctx,
 
1029
                                              0,
 
1030
                                              mechListMIC_out,
 
1031
                                              req_flags,
 
1032
                                              &mechtok_out, send_token,
 
1033
                                              output_token) < 0) {
 
1034
                        ret = GSS_S_FAILURE;
 
1035
                }
 
1036
        } else if (send_token != NO_TOKEN_SEND) {
 
1037
                if (make_spnego_tokenTarg_msg(negState, GSS_C_NO_OID,
 
1038
                                              &mechtok_out, mechListMIC_out,
 
1039
                                              send_token,
 
1040
                                              output_token) < 0) {
 
1041
                        ret = GSS_S_FAILURE;
 
1042
                }
 
1043
        }
 
1044
        gss_release_buffer(&tmpmin, &mechtok_out);
 
1045
        if (ret == GSS_S_COMPLETE) {
 
1046
                /*
 
1047
                 * Now, switch the output context to refer to the
 
1048
                 * negotiated mechanism's context.
 
1049
                 */
 
1050
                *context_handle = (gss_ctx_id_t)spnego_ctx->ctx_handle;
 
1051
                if (actual_mech != NULL)
 
1052
                        *actual_mech = spnego_ctx->actual_mech;
 
1053
                if (ret_flags != NULL)
 
1054
                        *ret_flags = spnego_ctx->ctx_flags;
 
1055
                release_spnego_ctx(&spnego_ctx);
 
1056
        } else if (ret != GSS_S_CONTINUE_NEEDED) {
 
1057
                if (spnego_ctx != NULL) {
 
1058
                        gss_delete_sec_context(&tmpmin,
 
1059
                                               &spnego_ctx->ctx_handle,
 
1060
                                               GSS_C_NO_BUFFER);
 
1061
                        release_spnego_ctx(&spnego_ctx);
 
1062
                }
 
1063
                *context_handle = GSS_C_NO_CONTEXT;
 
1064
        }
 
1065
        if (mechtok_in != GSS_C_NO_BUFFER) {
 
1066
                gss_release_buffer(&tmpmin, mechtok_in);
 
1067
                free(mechtok_in);
 
1068
        }
 
1069
        if (mechListMIC_in != GSS_C_NO_BUFFER) {
 
1070
                gss_release_buffer(&tmpmin, mechListMIC_in);
 
1071
                free(mechListMIC_in);
 
1072
        }
 
1073
        if (mechListMIC_out != GSS_C_NO_BUFFER) {
 
1074
                gss_release_buffer(&tmpmin, mechListMIC_out);
 
1075
                free(mechListMIC_out);
 
1076
        }
 
1077
        return ret;
 
1078
} /* init_sec_context */
 
1079
 
 
1080
/* We don't want to import KRB5 headers here */
 
1081
static const gss_OID_desc gss_mech_krb5_oid =
 
1082
        { 9, "\052\206\110\206\367\022\001\002\002" };
 
1083
static const gss_OID_desc gss_mech_krb5_wrong_oid =
 
1084
        { 9, "\052\206\110\202\367\022\001\002\002" };
 
1085
 
 
1086
/*
 
1087
 * verify that the input token length is not 0. If it is, just return.
 
1088
 * If the token length is greater than 0, der encode as a sequence
 
1089
 * and place in buf_out, advancing buf_out.
 
1090
 */
 
1091
 
 
1092
static int
 
1093
put_neg_hints(unsigned char **buf_out, gss_buffer_t input_token,
 
1094
              unsigned int buflen)
 
1095
{
 
1096
        int ret;
 
1097
 
 
1098
        /* if token length is 0, we do not want to send */
 
1099
        if (input_token->length == 0)
 
1100
                return (0);
 
1101
 
 
1102
        if (input_token->length > buflen)
 
1103
                return (-1);
 
1104
 
 
1105
        *(*buf_out)++ = SEQUENCE;
 
1106
        if ((ret = gssint_put_der_length(input_token->length, buf_out,
 
1107
                            input_token->length)))
 
1108
                return (ret);
 
1109
        TWRITE_STR(*buf_out, input_token->value, input_token->length);
 
1110
        return (0);
 
1111
}
 
1112
 
 
1113
/*
 
1114
 * NegHints ::= SEQUENCE {
 
1115
 *    hintName       [0]  GeneralString      OPTIONAL,
 
1116
 *    hintAddress    [1]  OCTET STRING       OPTIONAL
 
1117
 * }
 
1118
 */
 
1119
 
 
1120
#define HOST_PREFIX     "host@"
 
1121
#define HOST_PREFIX_LEN (sizeof(HOST_PREFIX) - 1)
 
1122
 
 
1123
static int
 
1124
make_NegHints(OM_uint32 *minor_status,
 
1125
              spnego_gss_cred_id_t spcred, gss_buffer_t *outbuf)
 
1126
{
 
1127
        gss_buffer_desc hintNameBuf;
 
1128
        gss_name_t hintName = GSS_C_NO_NAME;
 
1129
        gss_name_t hintKerberosName;
 
1130
        gss_OID hintNameType;
 
1131
        OM_uint32 major_status;
 
1132
        OM_uint32 minor;
 
1133
        unsigned int tlen = 0;
 
1134
        unsigned int hintNameSize = 0;
 
1135
        unsigned char *ptr;
 
1136
        unsigned char *t;
 
1137
 
 
1138
        *outbuf = GSS_C_NO_BUFFER;
 
1139
 
 
1140
        if (spcred != NULL) {
 
1141
                major_status = gss_inquire_cred(minor_status,
 
1142
                                                spcred->mcred,
 
1143
                                                &hintName,
 
1144
                                                NULL,
 
1145
                                                NULL,
 
1146
                                                NULL);
 
1147
                if (major_status != GSS_S_COMPLETE)
 
1148
                        return (major_status);
 
1149
        }
 
1150
 
 
1151
        if (hintName == GSS_C_NO_NAME) {
 
1152
                krb5_error_code code;
 
1153
                krb5int_access kaccess;
 
1154
                char hostname[HOST_PREFIX_LEN + MAXHOSTNAMELEN + 1] = HOST_PREFIX;
 
1155
 
 
1156
                code = krb5int_accessor(&kaccess, KRB5INT_ACCESS_VERSION);
 
1157
                if (code != 0) {
 
1158
                        *minor_status = code;
 
1159
                        return (GSS_S_FAILURE);
 
1160
                }
 
1161
 
 
1162
                /* this breaks mutual authentication but Samba relies on it */
 
1163
                code = (*kaccess.clean_hostname)(NULL, NULL,
 
1164
                                                 &hostname[HOST_PREFIX_LEN],
 
1165
                                                 MAXHOSTNAMELEN);
 
1166
                if (code != 0) {
 
1167
                        *minor_status = code;
 
1168
                        return (GSS_S_FAILURE);
 
1169
                }
 
1170
 
 
1171
                hintNameBuf.value = hostname;
 
1172
                hintNameBuf.length = strlen(hostname);
 
1173
 
 
1174
                major_status = gss_import_name(minor_status,
 
1175
                                               &hintNameBuf,
 
1176
                                               GSS_C_NT_HOSTBASED_SERVICE,
 
1177
                                               &hintName);
 
1178
                if (major_status != GSS_S_COMPLETE) {
 
1179
                        return (major_status);
 
1180
                }
 
1181
        }
 
1182
 
 
1183
        hintNameBuf.value = NULL;
 
1184
        hintNameBuf.length = 0;
 
1185
 
 
1186
        major_status = gss_canonicalize_name(minor_status,
 
1187
                                             hintName,
 
1188
                                             (gss_OID)&gss_mech_krb5_oid,
 
1189
                                             &hintKerberosName);
 
1190
        if (major_status != GSS_S_COMPLETE) {
 
1191
                gss_release_name(&minor, &hintName);
 
1192
                return (major_status);
 
1193
        }
 
1194
        gss_release_name(&minor, &hintName);
 
1195
 
 
1196
        major_status = gss_display_name(minor_status,
 
1197
                                        hintKerberosName,
 
1198
                                        &hintNameBuf,
 
1199
                                        &hintNameType);
 
1200
        if (major_status != GSS_S_COMPLETE) {
 
1201
                gss_release_name(&minor, &hintName);
 
1202
                return (major_status);
 
1203
        }
 
1204
        gss_release_name(&minor, &hintKerberosName);
 
1205
 
 
1206
        /*
 
1207
         * Now encode the name hint into a NegHints ASN.1 type
 
1208
         */
 
1209
        major_status = GSS_S_FAILURE;
 
1210
 
 
1211
        /* Length of DER encoded GeneralString */
 
1212
        tlen = 1 + gssint_der_length_size(hintNameBuf.length) +
 
1213
                hintNameBuf.length;
 
1214
        hintNameSize = tlen;
 
1215
 
 
1216
        /* Length of DER encoded hintName */
 
1217
        tlen += 1 + gssint_der_length_size(hintNameSize);
 
1218
 
 
1219
        t = gssalloc_malloc(tlen);
 
1220
        if (t == NULL) {
 
1221
                *minor_status = ENOMEM;
 
1222
                goto errout;
 
1223
        }
 
1224
 
 
1225
        ptr = t;
 
1226
 
 
1227
        *ptr++ = CONTEXT | 0x00; /* hintName identifier */
 
1228
        if (gssint_put_der_length(hintNameSize,
 
1229
                                  &ptr, tlen - (int)(ptr-t)))
 
1230
                goto errout;
 
1231
 
 
1232
        *ptr++ = GENERAL_STRING;
 
1233
        if (gssint_put_der_length(hintNameBuf.length,
 
1234
                                  &ptr, tlen - (int)(ptr-t)))
 
1235
                goto errout;
 
1236
 
 
1237
        memcpy(ptr, hintNameBuf.value, hintNameBuf.length);
 
1238
        ptr += hintNameBuf.length;
 
1239
 
 
1240
        *outbuf = (gss_buffer_t)malloc(sizeof(gss_buffer_desc));
 
1241
        if (*outbuf == NULL) {
 
1242
                *minor_status = ENOMEM;
 
1243
                goto errout;
 
1244
        }
 
1245
        (*outbuf)->value = (void *)t;
 
1246
        (*outbuf)->length = ptr - t;
 
1247
 
 
1248
        t = NULL; /* don't free */
 
1249
 
 
1250
        *minor_status = 0;
 
1251
        major_status = GSS_S_COMPLETE;
 
1252
 
 
1253
errout:
 
1254
        if (t != NULL) {
 
1255
                free(t);
 
1256
        }
 
1257
 
 
1258
        gss_release_buffer(&minor, &hintNameBuf);
 
1259
 
 
1260
        return (major_status);
 
1261
}
 
1262
 
 
1263
/*
 
1264
 * Support the Microsoft NegHints extension to SPNEGO for compatibility with
 
1265
 * some versions of Samba.  See:
 
1266
 *   http://msdn.microsoft.com/en-us/library/cc247039(PROT.10).aspx
 
1267
 */
 
1268
static OM_uint32
 
1269
acc_ctx_hints(OM_uint32 *minor_status,
 
1270
              gss_ctx_id_t *ctx,
 
1271
              spnego_gss_cred_id_t spcred,
 
1272
              gss_buffer_t *mechListMIC,
 
1273
              OM_uint32 *negState,
 
1274
              send_token_flag *return_token)
 
1275
{
 
1276
        OM_uint32 tmpmin, ret;
 
1277
        gss_OID_set supported_mechSet;
 
1278
        spnego_gss_ctx_id_t sc = NULL;
 
1279
 
 
1280
        *mechListMIC = GSS_C_NO_BUFFER;
 
1281
        supported_mechSet = GSS_C_NO_OID_SET;
 
1282
        *return_token = NO_TOKEN_SEND;
 
1283
        *negState = REJECT;
 
1284
        *minor_status = 0;
 
1285
 
 
1286
        /* A hint request must be the first token received. */
 
1287
        if (*ctx != GSS_C_NO_CONTEXT)
 
1288
            return GSS_S_DEFECTIVE_TOKEN;
 
1289
 
 
1290
        ret = get_negotiable_mechs(minor_status, spcred, GSS_C_ACCEPT,
 
1291
                                   &supported_mechSet);
 
1292
        if (ret != GSS_S_COMPLETE)
 
1293
                goto cleanup;
 
1294
 
 
1295
        ret = make_NegHints(minor_status, spcred, mechListMIC);
 
1296
        if (ret != GSS_S_COMPLETE)
 
1297
                goto cleanup;
 
1298
 
 
1299
        sc = create_spnego_ctx();
 
1300
        if (sc == NULL) {
 
1301
                ret = GSS_S_FAILURE;
 
1302
                goto cleanup;
 
1303
        }
 
1304
        if (put_mech_set(supported_mechSet, &sc->DER_mechTypes) < 0) {
 
1305
                ret = GSS_S_FAILURE;
 
1306
                goto cleanup;
 
1307
        }
 
1308
        sc->internal_mech = GSS_C_NO_OID;
 
1309
 
 
1310
        *negState = ACCEPT_INCOMPLETE;
 
1311
        *return_token = INIT_TOKEN_SEND;
 
1312
        sc->firstpass = 1;
 
1313
        *ctx = (gss_ctx_id_t)sc;
 
1314
        sc = NULL;
 
1315
        ret = GSS_S_COMPLETE;
 
1316
 
 
1317
cleanup:
 
1318
        release_spnego_ctx(&sc);
 
1319
        gss_release_oid_set(&tmpmin, &supported_mechSet);
 
1320
 
 
1321
        return ret;
 
1322
}
 
1323
 
 
1324
/*
 
1325
 * Set negState to REJECT if the token is defective, else
 
1326
 * ACCEPT_INCOMPLETE or REQUEST_MIC, depending on whether initiator's
 
1327
 * preferred mechanism is supported.
 
1328
 */
 
1329
static OM_uint32
 
1330
acc_ctx_new(OM_uint32 *minor_status,
 
1331
            gss_buffer_t buf,
 
1332
            gss_ctx_id_t *ctx,
 
1333
            spnego_gss_cred_id_t spcred,
 
1334
            gss_buffer_t *mechToken,
 
1335
            gss_buffer_t *mechListMIC,
 
1336
            OM_uint32 *negState,
 
1337
            send_token_flag *return_token)
 
1338
{
 
1339
        OM_uint32 tmpmin, ret, req_flags;
 
1340
        gss_OID_set supported_mechSet, mechTypes;
 
1341
        gss_buffer_desc der_mechTypes;
 
1342
        gss_OID mech_wanted;
 
1343
        spnego_gss_ctx_id_t sc = NULL;
 
1344
 
 
1345
        ret = GSS_S_DEFECTIVE_TOKEN;
 
1346
        der_mechTypes.length = 0;
 
1347
        der_mechTypes.value = NULL;
 
1348
        *mechToken = *mechListMIC = GSS_C_NO_BUFFER;
 
1349
        supported_mechSet = mechTypes = GSS_C_NO_OID_SET;
 
1350
        *return_token = ERROR_TOKEN_SEND;
 
1351
        *negState = REJECT;
 
1352
        *minor_status = 0;
 
1353
 
 
1354
        ret = get_negTokenInit(minor_status, buf, &der_mechTypes,
 
1355
                               &mechTypes, &req_flags,
 
1356
                               mechToken, mechListMIC);
 
1357
        if (ret != GSS_S_COMPLETE) {
 
1358
                goto cleanup;
 
1359
        }
 
1360
        ret = get_negotiable_mechs(minor_status, spcred, GSS_C_ACCEPT,
 
1361
                                   &supported_mechSet);
 
1362
        if (ret != GSS_S_COMPLETE) {
 
1363
                *return_token = NO_TOKEN_SEND;
 
1364
                goto cleanup;
 
1365
        }
 
1366
        /*
 
1367
         * Select the best match between the list of mechs
 
1368
         * that the initiator requested and the list that
 
1369
         * the acceptor will support.
 
1370
         */
 
1371
        mech_wanted = negotiate_mech(supported_mechSet, mechTypes, negState);
 
1372
        if (*negState == REJECT) {
 
1373
                ret = GSS_S_BAD_MECH;
 
1374
                goto cleanup;
 
1375
        }
 
1376
        sc = (spnego_gss_ctx_id_t)*ctx;
 
1377
        if (sc != NULL) {
 
1378
                gss_release_buffer(&tmpmin, &sc->DER_mechTypes);
 
1379
                assert(mech_wanted != GSS_C_NO_OID);
 
1380
        } else
 
1381
                sc = create_spnego_ctx();
 
1382
        if (sc == NULL) {
 
1383
                ret = GSS_S_FAILURE;
 
1384
                *return_token = NO_TOKEN_SEND;
 
1385
                goto cleanup;
 
1386
        }
 
1387
        sc->mech_set = supported_mechSet;
 
1388
        supported_mechSet = GSS_C_NO_OID_SET;
 
1389
        sc->internal_mech = mech_wanted;
 
1390
        sc->DER_mechTypes = der_mechTypes;
 
1391
        der_mechTypes.length = 0;
 
1392
        der_mechTypes.value = NULL;
 
1393
 
 
1394
        if (*negState == REQUEST_MIC)
 
1395
                sc->mic_reqd = 1;
 
1396
 
 
1397
        *return_token = INIT_TOKEN_SEND;
 
1398
        sc->firstpass = 1;
 
1399
        *ctx = (gss_ctx_id_t)sc;
 
1400
        ret = GSS_S_COMPLETE;
 
1401
cleanup:
 
1402
        gss_release_oid_set(&tmpmin, &mechTypes);
 
1403
        gss_release_oid_set(&tmpmin, &supported_mechSet);
 
1404
        if (der_mechTypes.length != 0)
 
1405
                gss_release_buffer(&tmpmin, &der_mechTypes);
 
1406
 
 
1407
        return ret;
 
1408
}
 
1409
 
 
1410
static OM_uint32
 
1411
acc_ctx_cont(OM_uint32 *minstat,
 
1412
             gss_buffer_t buf,
 
1413
             gss_ctx_id_t *ctx,
 
1414
             gss_buffer_t *responseToken,
 
1415
             gss_buffer_t *mechListMIC,
 
1416
             OM_uint32 *negState,
 
1417
             send_token_flag *return_token)
 
1418
{
 
1419
        OM_uint32 ret, tmpmin;
 
1420
        gss_OID supportedMech;
 
1421
        spnego_gss_ctx_id_t sc;
 
1422
        unsigned int len;
 
1423
        unsigned char *ptr, *bufstart;
 
1424
 
 
1425
        sc = (spnego_gss_ctx_id_t)*ctx;
 
1426
        ret = GSS_S_DEFECTIVE_TOKEN;
 
1427
        *negState = REJECT;
 
1428
        *minstat = 0;
 
1429
        supportedMech = GSS_C_NO_OID;
 
1430
        *return_token = ERROR_TOKEN_SEND;
 
1431
        *responseToken = *mechListMIC = GSS_C_NO_BUFFER;
 
1432
 
 
1433
        ptr = bufstart = buf->value;
 
1434
#define REMAIN (buf->length - (ptr - bufstart))
 
1435
        if (REMAIN > INT_MAX)
 
1436
                return GSS_S_DEFECTIVE_TOKEN;
 
1437
 
 
1438
        /*
 
1439
         * Attempt to work with old Sun SPNEGO.
 
1440
         */
 
1441
        if (*ptr == HEADER_ID) {
 
1442
                ret = g_verify_token_header(gss_mech_spnego,
 
1443
                                            &len, &ptr, 0, REMAIN);
 
1444
                if (ret) {
 
1445
                        *minstat = ret;
 
1446
                        return GSS_S_DEFECTIVE_TOKEN;
 
1447
                }
 
1448
        }
 
1449
        if (*ptr != (CONTEXT | 0x01)) {
 
1450
                return GSS_S_DEFECTIVE_TOKEN;
 
1451
        }
 
1452
        ret = get_negTokenResp(minstat, ptr, REMAIN,
 
1453
                               negState, &supportedMech,
 
1454
                               responseToken, mechListMIC);
 
1455
        if (ret != GSS_S_COMPLETE)
 
1456
                goto cleanup;
 
1457
 
 
1458
        if (*responseToken == GSS_C_NO_BUFFER &&
 
1459
            *mechListMIC == GSS_C_NO_BUFFER) {
 
1460
 
 
1461
                ret = GSS_S_DEFECTIVE_TOKEN;
 
1462
                goto cleanup;
 
1463
        }
 
1464
        if (supportedMech != GSS_C_NO_OID) {
 
1465
                ret = GSS_S_DEFECTIVE_TOKEN;
 
1466
                goto cleanup;
 
1467
        }
 
1468
        sc->firstpass = 0;
 
1469
        *negState = ACCEPT_INCOMPLETE;
 
1470
        *return_token = CONT_TOKEN_SEND;
 
1471
cleanup:
 
1472
        if (supportedMech != GSS_C_NO_OID) {
 
1473
                generic_gss_release_oid(&tmpmin, &supportedMech);
 
1474
        }
 
1475
        return ret;
 
1476
#undef REMAIN
 
1477
}
 
1478
 
 
1479
/*
 
1480
 * Verify that mech OID is either exactly the same as the negotiated
 
1481
 * mech OID, or is a mech OID supported by the negotiated mech.  MS
 
1482
 * implementations can list a most preferred mech using an incorrect
 
1483
 * krb5 OID while emitting a krb5 initiator mech token having the
 
1484
 * correct krb5 mech OID.
 
1485
 */
 
1486
static OM_uint32
 
1487
acc_ctx_vfy_oid(OM_uint32 *minor_status,
 
1488
                spnego_gss_ctx_id_t sc, gss_OID mechoid,
 
1489
                OM_uint32 *negState, send_token_flag *tokflag)
 
1490
{
 
1491
        OM_uint32 ret, tmpmin;
 
1492
        gss_mechanism mech = NULL;
 
1493
        gss_OID_set mech_set = GSS_C_NO_OID_SET;
 
1494
        int present = 0;
 
1495
 
 
1496
        if (g_OID_equal(sc->internal_mech, mechoid))
 
1497
                return GSS_S_COMPLETE;
 
1498
 
 
1499
        mech = gssint_get_mechanism(sc->internal_mech);
 
1500
        if (mech == NULL || mech->gss_indicate_mechs == NULL) {
 
1501
                *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
 
1502
                map_errcode(minor_status);
 
1503
                *negState = REJECT;
 
1504
                *tokflag = ERROR_TOKEN_SEND;
 
1505
                return GSS_S_BAD_MECH;
 
1506
        }
 
1507
        ret = mech->gss_indicate_mechs(minor_status, &mech_set);
 
1508
        if (ret != GSS_S_COMPLETE) {
 
1509
                *tokflag = NO_TOKEN_SEND;
 
1510
                map_error(minor_status, mech);
 
1511
                goto cleanup;
 
1512
        }
 
1513
        ret = gss_test_oid_set_member(minor_status, mechoid,
 
1514
                                      mech_set, &present);
 
1515
        if (ret != GSS_S_COMPLETE)
 
1516
                goto cleanup;
 
1517
        if (!present) {
 
1518
                *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
 
1519
                map_errcode(minor_status);
 
1520
                *negState = REJECT;
 
1521
                *tokflag = ERROR_TOKEN_SEND;
 
1522
                ret = GSS_S_BAD_MECH;
 
1523
        }
 
1524
cleanup:
 
1525
        gss_release_oid_set(&tmpmin, &mech_set);
 
1526
        return ret;
 
1527
}
 
1528
#ifndef LEAN_CLIENT
 
1529
/*
 
1530
 * Wrap call to gss_accept_sec_context() and update state
 
1531
 * accordingly.
 
1532
 */
 
1533
static OM_uint32
 
1534
acc_ctx_call_acc(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
 
1535
                 spnego_gss_cred_id_t spcred, gss_buffer_t mechtok_in,
 
1536
                 gss_OID *mech_type, gss_buffer_t mechtok_out,
 
1537
                 OM_uint32 *ret_flags, OM_uint32 *time_rec,
 
1538
                 gss_cred_id_t *delegated_cred_handle,
 
1539
                 OM_uint32 *negState, send_token_flag *tokflag)
 
1540
{
 
1541
        OM_uint32 ret;
 
1542
        gss_OID_desc mechoid;
 
1543
        gss_cred_id_t mcred;
 
1544
 
 
1545
        if (sc->ctx_handle == GSS_C_NO_CONTEXT) {
 
1546
                /*
 
1547
                 * mechoid is an alias; don't free it.
 
1548
                 */
 
1549
                ret = gssint_get_mech_type(&mechoid, mechtok_in);
 
1550
                if (ret != GSS_S_COMPLETE) {
 
1551
                        *tokflag = NO_TOKEN_SEND;
 
1552
                        return ret;
 
1553
                }
 
1554
                ret = acc_ctx_vfy_oid(minor_status, sc, &mechoid,
 
1555
                                      negState, tokflag);
 
1556
                if (ret != GSS_S_COMPLETE)
 
1557
                        return ret;
 
1558
        }
 
1559
 
 
1560
        mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
 
1561
        ret = gss_accept_sec_context(minor_status,
 
1562
                                     &sc->ctx_handle,
 
1563
                                     mcred,
 
1564
                                     mechtok_in,
 
1565
                                     GSS_C_NO_CHANNEL_BINDINGS,
 
1566
                                     &sc->internal_name,
 
1567
                                     mech_type,
 
1568
                                     mechtok_out,
 
1569
                                     &sc->ctx_flags,
 
1570
                                     time_rec,
 
1571
                                     delegated_cred_handle);
 
1572
        if (ret == GSS_S_COMPLETE) {
 
1573
#ifdef MS_BUG_TEST
 
1574
                /*
 
1575
                 * Force MIC to be not required even if we previously
 
1576
                 * requested a MIC.
 
1577
                 */
 
1578
                char *envstr = getenv("MS_FORCE_NO_MIC");
 
1579
 
 
1580
                if (envstr != NULL && strcmp(envstr, "1") == 0 &&
 
1581
                    !(sc->ctx_flags & GSS_C_MUTUAL_FLAG) &&
 
1582
                    sc->mic_reqd) {
 
1583
 
 
1584
                        sc->mic_reqd = 0;
 
1585
                }
 
1586
#endif
 
1587
                sc->mech_complete = 1;
 
1588
                if (ret_flags != NULL)
 
1589
                        *ret_flags = sc->ctx_flags;
 
1590
 
 
1591
                if (!sc->mic_reqd ||
 
1592
                    !(sc->ctx_flags & GSS_C_INTEG_FLAG)) {
 
1593
                        /* No MIC exchange required, so we're done. */
 
1594
                        *negState = ACCEPT_COMPLETE;
 
1595
                        ret = GSS_S_COMPLETE;
 
1596
                } else {
 
1597
                        /* handle_mic will decide if we're done. */
 
1598
                        ret = GSS_S_CONTINUE_NEEDED;
 
1599
                }
 
1600
        } else if (ret != GSS_S_CONTINUE_NEEDED) {
 
1601
                *negState = REJECT;
 
1602
                *tokflag = ERROR_TOKEN_SEND;
 
1603
        }
 
1604
        return ret;
 
1605
}
 
1606
 
 
1607
/*ARGSUSED*/
 
1608
OM_uint32 KRB5_CALLCONV
 
1609
spnego_gss_accept_sec_context(
 
1610
                            OM_uint32 *minor_status,
 
1611
                            gss_ctx_id_t *context_handle,
 
1612
                            gss_cred_id_t verifier_cred_handle,
 
1613
                            gss_buffer_t input_token,
 
1614
                            gss_channel_bindings_t input_chan_bindings,
 
1615
                            gss_name_t *src_name,
 
1616
                            gss_OID *mech_type,
 
1617
                            gss_buffer_t output_token,
 
1618
                            OM_uint32 *ret_flags,
 
1619
                            OM_uint32 *time_rec,
 
1620
                            gss_cred_id_t *delegated_cred_handle)
 
1621
{
 
1622
        OM_uint32 ret, tmpmin, negState;
 
1623
        send_token_flag return_token;
 
1624
        gss_buffer_t mechtok_in, mic_in, mic_out;
 
1625
        gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER;
 
1626
        spnego_gss_ctx_id_t sc = NULL;
 
1627
        spnego_gss_cred_id_t spcred = NULL;
 
1628
        int sendTokenInit = 0, tmpret;
 
1629
 
 
1630
        mechtok_in = mic_in = mic_out = GSS_C_NO_BUFFER;
 
1631
 
 
1632
        /*
 
1633
         * This function works in three steps:
 
1634
         *
 
1635
         *   1. Perform mechanism negotiation.
 
1636
         *   2. Invoke the negotiated mech's gss_accept_sec_context function
 
1637
         *      and examine the results.
 
1638
         *   3. Process or generate MICs if necessary.
 
1639
         *
 
1640
         * Step one determines whether the negotiation requires a MIC exchange,
 
1641
         * while steps two and three share responsibility for determining when
 
1642
         * the exchange is complete.  If the selected mech completes in this
 
1643
         * call and no MIC exchange is expected, then step 2 will decide.  If a
 
1644
         * MIC exchange is expected, then step 3 will decide.  If an error
 
1645
         * occurs in any step, the exchange will be aborted, possibly with an
 
1646
         * error token.
 
1647
         *
 
1648
         * negState determines the state of the negotiation, and is
 
1649
         * communicated to the acceptor if a continuing token is sent.
 
1650
         * return_token is used to indicate what type of token, if any, should
 
1651
         * be generated.
 
1652
         */
 
1653
 
 
1654
        /* Validate arguments. */
 
1655
        if (minor_status != NULL)
 
1656
                *minor_status = 0;
 
1657
        if (output_token != GSS_C_NO_BUFFER) {
 
1658
                output_token->length = 0;
 
1659
                output_token->value = NULL;
 
1660
        }
 
1661
 
 
1662
        if (minor_status == NULL ||
 
1663
            output_token == GSS_C_NO_BUFFER ||
 
1664
            context_handle == NULL)
 
1665
                return GSS_S_CALL_INACCESSIBLE_WRITE;
 
1666
 
 
1667
        if (input_token == GSS_C_NO_BUFFER)
 
1668
                return GSS_S_CALL_INACCESSIBLE_READ;
 
1669
 
 
1670
        /* Step 1: Perform mechanism negotiation. */
 
1671
        sc = (spnego_gss_ctx_id_t)*context_handle;
 
1672
        spcred = (spnego_gss_cred_id_t)verifier_cred_handle;
 
1673
        if (sc == NULL || sc->internal_mech == GSS_C_NO_OID) {
 
1674
                /* Process an initial token or request for NegHints. */
 
1675
                if (src_name != NULL)
 
1676
                        *src_name = GSS_C_NO_NAME;
 
1677
                if (mech_type != NULL)
 
1678
                        *mech_type = GSS_C_NO_OID;
 
1679
                if (time_rec != NULL)
 
1680
                        *time_rec = 0;
 
1681
                if (ret_flags != NULL)
 
1682
                        *ret_flags = 0;
 
1683
                if (delegated_cred_handle != NULL)
 
1684
                        *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
 
1685
                if (input_token->length == 0) {
 
1686
                        ret = acc_ctx_hints(minor_status,
 
1687
                                            context_handle, spcred,
 
1688
                                            &mic_out,
 
1689
                                            &negState,
 
1690
                                            &return_token);
 
1691
                        if (ret != GSS_S_COMPLETE)
 
1692
                                goto cleanup;
 
1693
                        sendTokenInit = 1;
 
1694
                        ret = GSS_S_CONTINUE_NEEDED;
 
1695
                } else {
 
1696
                        /* Can set negState to REQUEST_MIC */
 
1697
                        ret = acc_ctx_new(minor_status, input_token,
 
1698
                                          context_handle, spcred,
 
1699
                                          &mechtok_in, &mic_in,
 
1700
                                          &negState, &return_token);
 
1701
                        if (ret != GSS_S_COMPLETE)
 
1702
                                goto cleanup;
 
1703
                        ret = GSS_S_CONTINUE_NEEDED;
 
1704
                }
 
1705
        } else {
 
1706
                /* Process a response token.  Can set negState to
 
1707
                 * ACCEPT_INCOMPLETE. */
 
1708
                ret = acc_ctx_cont(minor_status, input_token,
 
1709
                                   context_handle, &mechtok_in,
 
1710
                                   &mic_in, &negState, &return_token);
 
1711
                if (ret != GSS_S_COMPLETE)
 
1712
                        goto cleanup;
 
1713
                ret = GSS_S_CONTINUE_NEEDED;
 
1714
        }
 
1715
 
 
1716
        /* Step 2: invoke the negotiated mechanism's gss_accept_sec_context
 
1717
         * function. */
 
1718
        sc = (spnego_gss_ctx_id_t)*context_handle;
 
1719
        /*
 
1720
         * Handle mechtok_in and mic_in only if they are
 
1721
         * present in input_token.  If neither is present, whether
 
1722
         * this is an error depends on whether this is the first
 
1723
         * round-trip.  RET is set to a default value according to
 
1724
         * whether it is the first round-trip.
 
1725
         */
 
1726
        if (negState != REQUEST_MIC && mechtok_in != GSS_C_NO_BUFFER) {
 
1727
                ret = acc_ctx_call_acc(minor_status, sc, spcred,
 
1728
                                       mechtok_in, mech_type, &mechtok_out,
 
1729
                                       ret_flags, time_rec,
 
1730
                                       delegated_cred_handle,
 
1731
                                       &negState, &return_token);
 
1732
        }
 
1733
 
 
1734
        /* Step 3: process or generate the MIC, if the negotiated mech is
 
1735
         * complete and supports MICs. */
 
1736
        if (!HARD_ERROR(ret) && sc->mech_complete &&
 
1737
            (sc->ctx_flags & GSS_C_INTEG_FLAG)) {
 
1738
 
 
1739
                ret = handle_mic(minor_status, mic_in,
 
1740
                                 (mechtok_out.length != 0),
 
1741
                                 sc, &mic_out,
 
1742
                                 &negState, &return_token);
 
1743
        }
 
1744
cleanup:
 
1745
        if (return_token == INIT_TOKEN_SEND && sendTokenInit) {
 
1746
                assert(sc != NULL);
 
1747
                tmpret = make_spnego_tokenInit_msg(sc, 1, mic_out, 0,
 
1748
                                                   GSS_C_NO_BUFFER,
 
1749
                                                   return_token, output_token);
 
1750
                if (tmpret < 0)
 
1751
                        ret = GSS_S_FAILURE;
 
1752
        } else if (return_token != NO_TOKEN_SEND &&
 
1753
                   return_token != CHECK_MIC) {
 
1754
                tmpret = make_spnego_tokenTarg_msg(negState,
 
1755
                                                   sc ? sc->internal_mech :
 
1756
                                                   GSS_C_NO_OID,
 
1757
                                                   &mechtok_out, mic_out,
 
1758
                                                   return_token,
 
1759
                                                   output_token);
 
1760
                if (tmpret < 0)
 
1761
                        ret = GSS_S_FAILURE;
 
1762
        }
 
1763
        if (ret == GSS_S_COMPLETE) {
 
1764
                *context_handle = (gss_ctx_id_t)sc->ctx_handle;
 
1765
                if (sc->internal_name != GSS_C_NO_NAME &&
 
1766
                    src_name != NULL) {
 
1767
                        *src_name = sc->internal_name;
 
1768
                        sc->internal_name = GSS_C_NO_NAME;
 
1769
                }
 
1770
                release_spnego_ctx(&sc);
 
1771
        } else if (ret != GSS_S_CONTINUE_NEEDED) {
 
1772
                if (sc != NULL) {
 
1773
                        gss_delete_sec_context(&tmpmin, &sc->ctx_handle,
 
1774
                                               GSS_C_NO_BUFFER);
 
1775
                        release_spnego_ctx(&sc);
 
1776
                }
 
1777
                *context_handle = GSS_C_NO_CONTEXT;
 
1778
        }
 
1779
        gss_release_buffer(&tmpmin, &mechtok_out);
 
1780
        if (mechtok_in != GSS_C_NO_BUFFER) {
 
1781
                gss_release_buffer(&tmpmin, mechtok_in);
 
1782
                free(mechtok_in);
 
1783
        }
 
1784
        if (mic_in != GSS_C_NO_BUFFER) {
 
1785
                gss_release_buffer(&tmpmin, mic_in);
 
1786
                free(mic_in);
 
1787
        }
 
1788
        if (mic_out != GSS_C_NO_BUFFER) {
 
1789
                gss_release_buffer(&tmpmin, mic_out);
 
1790
                free(mic_out);
 
1791
        }
 
1792
        return ret;
 
1793
}
 
1794
#endif /*  LEAN_CLIENT */
 
1795
 
 
1796
 
 
1797
/*ARGSUSED*/
 
1798
OM_uint32 KRB5_CALLCONV
 
1799
spnego_gss_display_status(
 
1800
                OM_uint32 *minor_status,
 
1801
                OM_uint32 status_value,
 
1802
                int status_type,
 
1803
                gss_OID mech_type,
 
1804
                OM_uint32 *message_context,
 
1805
                gss_buffer_t status_string)
 
1806
{
 
1807
        dsyslog("Entering display_status\n");
 
1808
 
 
1809
        *message_context = 0;
 
1810
        switch (status_value) {
 
1811
            case ERR_SPNEGO_NO_MECHS_AVAILABLE:
 
1812
                /* CSTYLED */
 
1813
                *status_string = make_err_msg(_("SPNEGO cannot find "
 
1814
                                                "mechanisms to negotiate"));
 
1815
                break;
 
1816
            case ERR_SPNEGO_NO_CREDS_ACQUIRED:
 
1817
                /* CSTYLED */
 
1818
                *status_string = make_err_msg(_("SPNEGO failed to acquire "
 
1819
                                                "creds"));
 
1820
                break;
 
1821
            case ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR:
 
1822
                /* CSTYLED */
 
1823
                *status_string = make_err_msg(_("SPNEGO acceptor did not "
 
1824
                                                "select a mechanism"));
 
1825
                break;
 
1826
            case ERR_SPNEGO_NEGOTIATION_FAILED:
 
1827
                /* CSTYLED */
 
1828
                *status_string = make_err_msg(_("SPNEGO failed to negotiate a "
 
1829
                                                "mechanism"));
 
1830
                break;
 
1831
            case ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR:
 
1832
                /* CSTYLED */
 
1833
                *status_string = make_err_msg(_("SPNEGO acceptor did not "
 
1834
                                                "return a valid token"));
 
1835
                break;
 
1836
            default:
 
1837
                status_string->length = 0;
 
1838
                status_string->value = "";
 
1839
                break;
 
1840
        }
 
1841
 
 
1842
        dsyslog("Leaving display_status\n");
 
1843
        return (GSS_S_COMPLETE);
 
1844
}
 
1845
 
 
1846
 
 
1847
/*ARGSUSED*/
 
1848
OM_uint32 KRB5_CALLCONV
 
1849
spnego_gss_import_name(
 
1850
                    OM_uint32 *minor_status,
 
1851
                    gss_buffer_t input_name_buffer,
 
1852
                    gss_OID input_name_type,
 
1853
                    gss_name_t *output_name)
 
1854
{
 
1855
        OM_uint32 status;
 
1856
 
 
1857
        dsyslog("Entering import_name\n");
 
1858
 
 
1859
        status = gss_import_name(minor_status, input_name_buffer,
 
1860
                        input_name_type, output_name);
 
1861
 
 
1862
        dsyslog("Leaving import_name\n");
 
1863
        return (status);
 
1864
}
 
1865
 
 
1866
/*ARGSUSED*/
 
1867
OM_uint32 KRB5_CALLCONV
 
1868
spnego_gss_release_name(
 
1869
                        OM_uint32 *minor_status,
 
1870
                        gss_name_t *input_name)
 
1871
{
 
1872
        OM_uint32 status;
 
1873
 
 
1874
        dsyslog("Entering release_name\n");
 
1875
 
 
1876
        status = gss_release_name(minor_status, input_name);
 
1877
 
 
1878
        dsyslog("Leaving release_name\n");
 
1879
        return (status);
 
1880
}
 
1881
 
 
1882
/*ARGSUSED*/
 
1883
OM_uint32 KRB5_CALLCONV
 
1884
spnego_gss_duplicate_name(
 
1885
                        OM_uint32 *minor_status,
 
1886
                        const gss_name_t input_name,
 
1887
                        gss_name_t *output_name)
 
1888
{
 
1889
        OM_uint32 status;
 
1890
 
 
1891
        dsyslog("Entering duplicate_name\n");
 
1892
 
 
1893
        status = gss_duplicate_name(minor_status, input_name, output_name);
 
1894
 
 
1895
        dsyslog("Leaving duplicate_name\n");
 
1896
        return (status);
 
1897
}
 
1898
 
 
1899
OM_uint32 KRB5_CALLCONV
 
1900
spnego_gss_inquire_cred(
 
1901
                        OM_uint32 *minor_status,
 
1902
                        gss_cred_id_t cred_handle,
 
1903
                        gss_name_t *name,
 
1904
                        OM_uint32 *lifetime,
 
1905
                        int *cred_usage,
 
1906
                        gss_OID_set *mechanisms)
 
1907
{
 
1908
        OM_uint32 status;
 
1909
        spnego_gss_cred_id_t spcred = NULL;
 
1910
        gss_cred_id_t creds = GSS_C_NO_CREDENTIAL;
 
1911
        OM_uint32 tmp_minor_status;
 
1912
        OM_uint32 initiator_lifetime, acceptor_lifetime;
 
1913
 
 
1914
        dsyslog("Entering inquire_cred\n");
 
1915
 
 
1916
        /*
 
1917
         * To avoid infinite recursion, if GSS_C_NO_CREDENTIAL is
 
1918
         * supplied we call gss_inquire_cred_by_mech() on the
 
1919
         * first non-SPNEGO mechanism.
 
1920
         */
 
1921
        spcred = (spnego_gss_cred_id_t)cred_handle;
 
1922
        if (spcred == NULL) {
 
1923
                status = get_available_mechs(minor_status,
 
1924
                        GSS_C_NO_NAME,
 
1925
                        GSS_C_BOTH,
 
1926
                        GSS_C_NO_CRED_STORE,
 
1927
                        &creds,
 
1928
                        mechanisms);
 
1929
                if (status != GSS_S_COMPLETE) {
 
1930
                        dsyslog("Leaving inquire_cred\n");
 
1931
                        return (status);
 
1932
                }
 
1933
 
 
1934
                if ((*mechanisms)->count == 0) {
 
1935
                        gss_release_cred(&tmp_minor_status, &creds);
 
1936
                        gss_release_oid_set(&tmp_minor_status, mechanisms);
 
1937
                        dsyslog("Leaving inquire_cred\n");
 
1938
                        return (GSS_S_DEFECTIVE_CREDENTIAL);
 
1939
                }
 
1940
 
 
1941
                assert((*mechanisms)->elements != NULL);
 
1942
 
 
1943
                status = gss_inquire_cred_by_mech(minor_status,
 
1944
                        creds,
 
1945
                        &(*mechanisms)->elements[0],
 
1946
                        name,
 
1947
                        &initiator_lifetime,
 
1948
                        &acceptor_lifetime,
 
1949
                        cred_usage);
 
1950
                if (status != GSS_S_COMPLETE) {
 
1951
                        gss_release_cred(&tmp_minor_status, &creds);
 
1952
                        dsyslog("Leaving inquire_cred\n");
 
1953
                        return (status);
 
1954
                }
 
1955
 
 
1956
                if (lifetime != NULL)
 
1957
                        *lifetime = (*cred_usage == GSS_C_ACCEPT) ?
 
1958
                                acceptor_lifetime : initiator_lifetime;
 
1959
 
 
1960
                gss_release_cred(&tmp_minor_status, &creds);
 
1961
        } else {
 
1962
                status = gss_inquire_cred(minor_status, spcred->mcred,
 
1963
                                          name, lifetime,
 
1964
                                          cred_usage, mechanisms);
 
1965
        }
 
1966
 
 
1967
        dsyslog("Leaving inquire_cred\n");
 
1968
 
 
1969
        return (status);
 
1970
}
 
1971
 
 
1972
/*ARGSUSED*/
 
1973
OM_uint32 KRB5_CALLCONV
 
1974
spnego_gss_compare_name(
 
1975
                        OM_uint32 *minor_status,
 
1976
                        const gss_name_t name1,
 
1977
                        const gss_name_t name2,
 
1978
                        int *name_equal)
 
1979
{
 
1980
        OM_uint32 status = GSS_S_COMPLETE;
 
1981
        dsyslog("Entering compare_name\n");
 
1982
 
 
1983
        status = gss_compare_name(minor_status, name1, name2, name_equal);
 
1984
 
 
1985
        dsyslog("Leaving compare_name\n");
 
1986
        return (status);
 
1987
}
 
1988
 
 
1989
/*ARGSUSED*/
 
1990
/*ARGSUSED*/
 
1991
OM_uint32 KRB5_CALLCONV
 
1992
spnego_gss_display_name(
 
1993
                        OM_uint32 *minor_status,
 
1994
                        gss_name_t input_name,
 
1995
                        gss_buffer_t output_name_buffer,
 
1996
                        gss_OID *output_name_type)
 
1997
{
 
1998
        OM_uint32 status = GSS_S_COMPLETE;
 
1999
        dsyslog("Entering display_name\n");
 
2000
 
 
2001
        status = gss_display_name(minor_status, input_name,
 
2002
                        output_name_buffer, output_name_type);
 
2003
 
 
2004
        dsyslog("Leaving display_name\n");
 
2005
        return (status);
 
2006
}
 
2007
 
 
2008
 
 
2009
/*ARGSUSED*/
 
2010
OM_uint32 KRB5_CALLCONV
 
2011
spnego_gss_inquire_names_for_mech(
 
2012
                                OM_uint32       *minor_status,
 
2013
                                gss_OID         mechanism,
 
2014
                                gss_OID_set     *name_types)
 
2015
{
 
2016
        OM_uint32   major, minor;
 
2017
 
 
2018
        dsyslog("Entering inquire_names_for_mech\n");
 
2019
        /*
 
2020
         * We only know how to handle our own mechanism.
 
2021
         */
 
2022
        if ((mechanism != GSS_C_NULL_OID) &&
 
2023
            !g_OID_equal(gss_mech_spnego, mechanism)) {
 
2024
                *minor_status = 0;
 
2025
                return (GSS_S_FAILURE);
 
2026
        }
 
2027
 
 
2028
        major = gss_create_empty_oid_set(minor_status, name_types);
 
2029
        if (major == GSS_S_COMPLETE) {
 
2030
                /* Now add our members. */
 
2031
                if (((major = gss_add_oid_set_member(minor_status,
 
2032
                                (gss_OID) GSS_C_NT_USER_NAME,
 
2033
                                name_types)) == GSS_S_COMPLETE) &&
 
2034
                    ((major = gss_add_oid_set_member(minor_status,
 
2035
                                (gss_OID) GSS_C_NT_MACHINE_UID_NAME,
 
2036
                                name_types)) == GSS_S_COMPLETE) &&
 
2037
                    ((major = gss_add_oid_set_member(minor_status,
 
2038
                                (gss_OID) GSS_C_NT_STRING_UID_NAME,
 
2039
                                name_types)) == GSS_S_COMPLETE)) {
 
2040
                        major = gss_add_oid_set_member(minor_status,
 
2041
                                (gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
 
2042
                                name_types);
 
2043
                }
 
2044
 
 
2045
                if (major != GSS_S_COMPLETE)
 
2046
                        (void) gss_release_oid_set(&minor, name_types);
 
2047
        }
 
2048
 
 
2049
        dsyslog("Leaving inquire_names_for_mech\n");
 
2050
        return (major);
 
2051
}
 
2052
 
 
2053
OM_uint32 KRB5_CALLCONV
 
2054
spnego_gss_unwrap(
 
2055
                OM_uint32 *minor_status,
 
2056
                gss_ctx_id_t context_handle,
 
2057
                gss_buffer_t input_message_buffer,
 
2058
                gss_buffer_t output_message_buffer,
 
2059
                int *conf_state,
 
2060
                gss_qop_t *qop_state)
 
2061
{
 
2062
        OM_uint32 ret;
 
2063
        ret = gss_unwrap(minor_status,
 
2064
                        context_handle,
 
2065
                        input_message_buffer,
 
2066
                        output_message_buffer,
 
2067
                        conf_state,
 
2068
                        qop_state);
 
2069
 
 
2070
        return (ret);
 
2071
}
 
2072
 
 
2073
OM_uint32 KRB5_CALLCONV
 
2074
spnego_gss_wrap(
 
2075
                OM_uint32 *minor_status,
 
2076
                gss_ctx_id_t context_handle,
 
2077
                int conf_req_flag,
 
2078
                gss_qop_t qop_req,
 
2079
                gss_buffer_t input_message_buffer,
 
2080
                int *conf_state,
 
2081
                gss_buffer_t output_message_buffer)
 
2082
{
 
2083
        OM_uint32 ret;
 
2084
        ret = gss_wrap(minor_status,
 
2085
                    context_handle,
 
2086
                    conf_req_flag,
 
2087
                    qop_req,
 
2088
                    input_message_buffer,
 
2089
                    conf_state,
 
2090
                    output_message_buffer);
 
2091
 
 
2092
        return (ret);
 
2093
}
 
2094
 
 
2095
OM_uint32 KRB5_CALLCONV
 
2096
spnego_gss_process_context_token(
 
2097
                                OM_uint32       *minor_status,
 
2098
                                const gss_ctx_id_t context_handle,
 
2099
                                const gss_buffer_t token_buffer)
 
2100
{
 
2101
        OM_uint32 ret;
 
2102
        ret = gss_process_context_token(minor_status,
 
2103
                                        context_handle,
 
2104
                                        token_buffer);
 
2105
 
 
2106
        return (ret);
 
2107
}
 
2108
 
 
2109
OM_uint32 KRB5_CALLCONV
 
2110
spnego_gss_delete_sec_context(
 
2111
                            OM_uint32 *minor_status,
 
2112
                            gss_ctx_id_t *context_handle,
 
2113
                            gss_buffer_t output_token)
 
2114
{
 
2115
        OM_uint32 ret = GSS_S_COMPLETE;
 
2116
        spnego_gss_ctx_id_t *ctx =
 
2117
                    (spnego_gss_ctx_id_t *)context_handle;
 
2118
 
 
2119
        *minor_status = 0;
 
2120
 
 
2121
        if (context_handle == NULL)
 
2122
                return (GSS_S_FAILURE);
 
2123
 
 
2124
        if (*ctx == NULL)
 
2125
                return (GSS_S_COMPLETE);
 
2126
 
 
2127
        /*
 
2128
         * If this is still an SPNEGO mech, release it locally.
 
2129
         */
 
2130
        if ((*ctx)->magic_num == SPNEGO_MAGIC_ID) {
 
2131
                (void) gss_delete_sec_context(minor_status,
 
2132
                                    &(*ctx)->ctx_handle,
 
2133
                                    output_token);
 
2134
                (void) release_spnego_ctx(ctx);
 
2135
        } else {
 
2136
                ret = gss_delete_sec_context(minor_status,
 
2137
                                    context_handle,
 
2138
                                    output_token);
 
2139
        }
 
2140
 
 
2141
        return (ret);
 
2142
}
 
2143
 
 
2144
OM_uint32 KRB5_CALLCONV
 
2145
spnego_gss_context_time(
 
2146
                        OM_uint32       *minor_status,
 
2147
                        const gss_ctx_id_t context_handle,
 
2148
                        OM_uint32       *time_rec)
 
2149
{
 
2150
        OM_uint32 ret;
 
2151
        ret = gss_context_time(minor_status,
 
2152
                            context_handle,
 
2153
                            time_rec);
 
2154
        return (ret);
 
2155
}
 
2156
#ifndef LEAN_CLIENT
 
2157
OM_uint32 KRB5_CALLCONV
 
2158
spnego_gss_export_sec_context(
 
2159
                            OM_uint32     *minor_status,
 
2160
                            gss_ctx_id_t *context_handle,
 
2161
                            gss_buffer_t interprocess_token)
 
2162
{
 
2163
        OM_uint32 ret;
 
2164
        ret = gss_export_sec_context(minor_status,
 
2165
                                    context_handle,
 
2166
                                    interprocess_token);
 
2167
        return (ret);
 
2168
}
 
2169
 
 
2170
OM_uint32 KRB5_CALLCONV
 
2171
spnego_gss_import_sec_context(
 
2172
        OM_uint32               *minor_status,
 
2173
        const gss_buffer_t      interprocess_token,
 
2174
        gss_ctx_id_t            *context_handle)
 
2175
{
 
2176
        OM_uint32 ret;
 
2177
        ret = gss_import_sec_context(minor_status,
 
2178
                                    interprocess_token,
 
2179
                                    context_handle);
 
2180
        return (ret);
 
2181
}
 
2182
#endif /* LEAN_CLIENT */
 
2183
 
 
2184
OM_uint32 KRB5_CALLCONV
 
2185
spnego_gss_inquire_context(
 
2186
                        OM_uint32       *minor_status,
 
2187
                        const gss_ctx_id_t context_handle,
 
2188
                        gss_name_t      *src_name,
 
2189
                        gss_name_t      *targ_name,
 
2190
                        OM_uint32       *lifetime_rec,
 
2191
                        gss_OID         *mech_type,
 
2192
                        OM_uint32       *ctx_flags,
 
2193
                        int             *locally_initiated,
 
2194
                        int             *opened)
 
2195
{
 
2196
        OM_uint32 ret = GSS_S_COMPLETE;
 
2197
 
 
2198
        ret = gss_inquire_context(minor_status,
 
2199
                                context_handle,
 
2200
                                src_name,
 
2201
                                targ_name,
 
2202
                                lifetime_rec,
 
2203
                                mech_type,
 
2204
                                ctx_flags,
 
2205
                                locally_initiated,
 
2206
                                opened);
 
2207
 
 
2208
        return (ret);
 
2209
}
 
2210
 
 
2211
OM_uint32 KRB5_CALLCONV
 
2212
spnego_gss_wrap_size_limit(
 
2213
        OM_uint32       *minor_status,
 
2214
        const gss_ctx_id_t context_handle,
 
2215
        int             conf_req_flag,
 
2216
        gss_qop_t       qop_req,
 
2217
        OM_uint32       req_output_size,
 
2218
        OM_uint32       *max_input_size)
 
2219
{
 
2220
        OM_uint32 ret;
 
2221
        ret = gss_wrap_size_limit(minor_status,
 
2222
                                context_handle,
 
2223
                                conf_req_flag,
 
2224
                                qop_req,
 
2225
                                req_output_size,
 
2226
                                max_input_size);
 
2227
        return (ret);
 
2228
}
 
2229
 
 
2230
OM_uint32 KRB5_CALLCONV
 
2231
spnego_gss_get_mic(
 
2232
                OM_uint32 *minor_status,
 
2233
                const gss_ctx_id_t context_handle,
 
2234
                gss_qop_t  qop_req,
 
2235
                const gss_buffer_t message_buffer,
 
2236
                gss_buffer_t message_token)
 
2237
{
 
2238
        OM_uint32 ret;
 
2239
        ret = gss_get_mic(minor_status,
 
2240
                    context_handle,
 
2241
                    qop_req,
 
2242
                    message_buffer,
 
2243
                    message_token);
 
2244
        return (ret);
 
2245
}
 
2246
 
 
2247
OM_uint32 KRB5_CALLCONV
 
2248
spnego_gss_verify_mic(
 
2249
                OM_uint32 *minor_status,
 
2250
                const gss_ctx_id_t context_handle,
 
2251
                const gss_buffer_t msg_buffer,
 
2252
                const gss_buffer_t token_buffer,
 
2253
                gss_qop_t *qop_state)
 
2254
{
 
2255
        OM_uint32 ret;
 
2256
        ret = gss_verify_mic(minor_status,
 
2257
                            context_handle,
 
2258
                            msg_buffer,
 
2259
                            token_buffer,
 
2260
                            qop_state);
 
2261
        return (ret);
 
2262
}
 
2263
 
 
2264
OM_uint32 KRB5_CALLCONV
 
2265
spnego_gss_inquire_sec_context_by_oid(
 
2266
                OM_uint32 *minor_status,
 
2267
                const gss_ctx_id_t context_handle,
 
2268
                const gss_OID desired_object,
 
2269
                gss_buffer_set_t *data_set)
 
2270
{
 
2271
        OM_uint32 ret;
 
2272
        ret = gss_inquire_sec_context_by_oid(minor_status,
 
2273
                            context_handle,
 
2274
                            desired_object,
 
2275
                            data_set);
 
2276
        return (ret);
 
2277
}
 
2278
 
 
2279
OM_uint32 KRB5_CALLCONV
 
2280
spnego_gss_inquire_cred_by_oid(
 
2281
                OM_uint32 *minor_status,
 
2282
                const gss_cred_id_t cred_handle,
 
2283
                const gss_OID desired_object,
 
2284
                gss_buffer_set_t *data_set)
 
2285
{
 
2286
        OM_uint32 ret;
 
2287
        spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)cred_handle;
 
2288
        gss_cred_id_t mcred;
 
2289
        mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
 
2290
        ret = gss_inquire_cred_by_oid(minor_status,
 
2291
                                mcred,
 
2292
                                desired_object,
 
2293
                                data_set);
 
2294
        return (ret);
 
2295
}
 
2296
 
 
2297
OM_uint32 KRB5_CALLCONV
 
2298
spnego_gss_set_cred_option(
 
2299
                OM_uint32 *minor_status,
 
2300
                gss_cred_id_t *cred_handle,
 
2301
                const gss_OID desired_object,
 
2302
                const gss_buffer_t value)
 
2303
{
 
2304
        OM_uint32 ret;
 
2305
        OM_uint32 tmp_minor_status;
 
2306
        spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)*cred_handle;
 
2307
        gss_cred_id_t mcred;
 
2308
 
 
2309
        mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
 
2310
        ret = gss_set_cred_option(minor_status,
 
2311
                                  &mcred,
 
2312
                                  desired_object,
 
2313
                                  value);
 
2314
        if (ret == GSS_S_COMPLETE && spcred == NULL) {
 
2315
                /*
 
2316
                 * If the mechanism allocated a new credential handle, then
 
2317
                 * we need to wrap it up in an SPNEGO credential handle.
 
2318
                 */
 
2319
 
 
2320
                spcred = malloc(sizeof(spnego_gss_cred_id_rec));
 
2321
                if (spcred == NULL) {
 
2322
                        gss_release_cred(&tmp_minor_status, &mcred);
 
2323
                        *minor_status = ENOMEM;
 
2324
                        return (GSS_S_FAILURE);
 
2325
                }
 
2326
                spcred->mcred = mcred;
 
2327
                spcred->neg_mechs = GSS_C_NULL_OID_SET;
 
2328
                *cred_handle = (gss_cred_id_t)spcred;
 
2329
        }
 
2330
 
 
2331
        return (ret);
 
2332
}
 
2333
 
 
2334
OM_uint32 KRB5_CALLCONV
 
2335
spnego_gss_set_sec_context_option(
 
2336
                OM_uint32 *minor_status,
 
2337
                gss_ctx_id_t *context_handle,
 
2338
                const gss_OID desired_object,
 
2339
                const gss_buffer_t value)
 
2340
{
 
2341
        OM_uint32 ret;
 
2342
        ret = gss_set_sec_context_option(minor_status,
 
2343
                            context_handle,
 
2344
                            desired_object,
 
2345
                            value);
 
2346
        return (ret);
 
2347
}
 
2348
 
 
2349
OM_uint32 KRB5_CALLCONV
 
2350
spnego_gss_wrap_aead(OM_uint32 *minor_status,
 
2351
                     gss_ctx_id_t context_handle,
 
2352
                     int conf_req_flag,
 
2353
                     gss_qop_t qop_req,
 
2354
                     gss_buffer_t input_assoc_buffer,
 
2355
                     gss_buffer_t input_payload_buffer,
 
2356
                     int *conf_state,
 
2357
                     gss_buffer_t output_message_buffer)
 
2358
{
 
2359
        OM_uint32 ret;
 
2360
        ret = gss_wrap_aead(minor_status,
 
2361
                            context_handle,
 
2362
                            conf_req_flag,
 
2363
                            qop_req,
 
2364
                            input_assoc_buffer,
 
2365
                            input_payload_buffer,
 
2366
                            conf_state,
 
2367
                            output_message_buffer);
 
2368
 
 
2369
        return (ret);
 
2370
}
 
2371
 
 
2372
OM_uint32 KRB5_CALLCONV
 
2373
spnego_gss_unwrap_aead(OM_uint32 *minor_status,
 
2374
                       gss_ctx_id_t context_handle,
 
2375
                       gss_buffer_t input_message_buffer,
 
2376
                       gss_buffer_t input_assoc_buffer,
 
2377
                       gss_buffer_t output_payload_buffer,
 
2378
                       int *conf_state,
 
2379
                       gss_qop_t *qop_state)
 
2380
{
 
2381
        OM_uint32 ret;
 
2382
        ret = gss_unwrap_aead(minor_status,
 
2383
                              context_handle,
 
2384
                              input_message_buffer,
 
2385
                              input_assoc_buffer,
 
2386
                              output_payload_buffer,
 
2387
                              conf_state,
 
2388
                              qop_state);
 
2389
        return (ret);
 
2390
}
 
2391
 
 
2392
OM_uint32 KRB5_CALLCONV
 
2393
spnego_gss_wrap_iov(OM_uint32 *minor_status,
 
2394
                    gss_ctx_id_t context_handle,
 
2395
                    int conf_req_flag,
 
2396
                    gss_qop_t qop_req,
 
2397
                    int *conf_state,
 
2398
                    gss_iov_buffer_desc *iov,
 
2399
                    int iov_count)
 
2400
{
 
2401
        OM_uint32 ret;
 
2402
        ret = gss_wrap_iov(minor_status,
 
2403
                           context_handle,
 
2404
                           conf_req_flag,
 
2405
                           qop_req,
 
2406
                           conf_state,
 
2407
                           iov,
 
2408
                           iov_count);
 
2409
        return (ret);
 
2410
}
 
2411
 
 
2412
OM_uint32 KRB5_CALLCONV
 
2413
spnego_gss_unwrap_iov(OM_uint32 *minor_status,
 
2414
                      gss_ctx_id_t context_handle,
 
2415
                      int *conf_state,
 
2416
                      gss_qop_t *qop_state,
 
2417
                      gss_iov_buffer_desc *iov,
 
2418
                      int iov_count)
 
2419
{
 
2420
        OM_uint32 ret;
 
2421
        ret = gss_unwrap_iov(minor_status,
 
2422
                             context_handle,
 
2423
                             conf_state,
 
2424
                             qop_state,
 
2425
                             iov,
 
2426
                             iov_count);
 
2427
        return (ret);
 
2428
}
 
2429
 
 
2430
OM_uint32 KRB5_CALLCONV
 
2431
spnego_gss_wrap_iov_length(OM_uint32 *minor_status,
 
2432
                           gss_ctx_id_t context_handle,
 
2433
                           int conf_req_flag,
 
2434
                           gss_qop_t qop_req,
 
2435
                           int *conf_state,
 
2436
                           gss_iov_buffer_desc *iov,
 
2437
                           int iov_count)
 
2438
{
 
2439
        OM_uint32 ret;
 
2440
        ret = gss_wrap_iov_length(minor_status,
 
2441
                                  context_handle,
 
2442
                                  conf_req_flag,
 
2443
                                  qop_req,
 
2444
                                  conf_state,
 
2445
                                  iov,
 
2446
                                  iov_count);
 
2447
        return (ret);
 
2448
}
 
2449
 
 
2450
 
 
2451
OM_uint32 KRB5_CALLCONV
 
2452
spnego_gss_complete_auth_token(
 
2453
                OM_uint32 *minor_status,
 
2454
                const gss_ctx_id_t context_handle,
 
2455
                gss_buffer_t input_message_buffer)
 
2456
{
 
2457
        OM_uint32 ret;
 
2458
        ret = gss_complete_auth_token(minor_status,
 
2459
                                      context_handle,
 
2460
                                      input_message_buffer);
 
2461
        return (ret);
 
2462
}
 
2463
 
 
2464
OM_uint32 KRB5_CALLCONV
 
2465
spnego_gss_acquire_cred_impersonate_name(OM_uint32 *minor_status,
 
2466
                                         const gss_cred_id_t impersonator_cred_handle,
 
2467
                                         const gss_name_t desired_name,
 
2468
                                         OM_uint32 time_req,
 
2469
                                         gss_OID_set desired_mechs,
 
2470
                                         gss_cred_usage_t cred_usage,
 
2471
                                         gss_cred_id_t *output_cred_handle,
 
2472
                                         gss_OID_set *actual_mechs,
 
2473
                                         OM_uint32 *time_rec)
 
2474
{
 
2475
        OM_uint32 status;
 
2476
        gss_OID_set amechs = GSS_C_NULL_OID_SET;
 
2477
        spnego_gss_cred_id_t imp_spcred = NULL, out_spcred = NULL;
 
2478
        gss_cred_id_t imp_mcred, out_mcred;
 
2479
 
 
2480
        dsyslog("Entering spnego_gss_acquire_cred_impersonate_name\n");
 
2481
 
 
2482
        if (actual_mechs)
 
2483
                *actual_mechs = NULL;
 
2484
 
 
2485
        if (time_rec)
 
2486
                *time_rec = 0;
 
2487
 
 
2488
        imp_spcred = (spnego_gss_cred_id_t)impersonator_cred_handle;
 
2489
        imp_mcred = imp_spcred ? imp_spcred->mcred : GSS_C_NO_CREDENTIAL;
 
2490
        if (desired_mechs == GSS_C_NO_OID_SET) {
 
2491
                status = gss_inquire_cred(minor_status, imp_mcred, NULL, NULL,
 
2492
                                          NULL, &amechs);
 
2493
                if (status != GSS_S_COMPLETE)
 
2494
                        return status;
 
2495
 
 
2496
                desired_mechs = amechs;
 
2497
        }
 
2498
 
 
2499
        status = gss_acquire_cred_impersonate_name(minor_status, imp_mcred,
 
2500
                                                   desired_name, time_req,
 
2501
                                                   desired_mechs, cred_usage,
 
2502
                                                   &out_mcred, actual_mechs,
 
2503
                                                   time_rec);
 
2504
 
 
2505
        if (amechs != GSS_C_NULL_OID_SET)
 
2506
                (void) gss_release_oid_set(minor_status, &amechs);
 
2507
 
 
2508
        out_spcred = malloc(sizeof(spnego_gss_cred_id_rec));
 
2509
        if (out_spcred == NULL) {
 
2510
                gss_release_cred(minor_status, &out_mcred);
 
2511
                *minor_status = ENOMEM;
 
2512
                return (GSS_S_FAILURE);
 
2513
        }
 
2514
        out_spcred->mcred = out_mcred;
 
2515
        out_spcred->neg_mechs = GSS_C_NULL_OID_SET;
 
2516
        *output_cred_handle = (gss_cred_id_t)out_spcred;
 
2517
 
 
2518
        dsyslog("Leaving spnego_gss_acquire_cred_impersonate_name\n");
 
2519
        return (status);
 
2520
}
 
2521
 
 
2522
OM_uint32 KRB5_CALLCONV
 
2523
spnego_gss_acquire_cred_with_password(OM_uint32 *minor_status,
 
2524
                                      const gss_name_t desired_name,
 
2525
                                      const gss_buffer_t password,
 
2526
                                      OM_uint32 time_req,
 
2527
                                      const gss_OID_set desired_mechs,
 
2528
                                      gss_cred_usage_t cred_usage,
 
2529
                                      gss_cred_id_t *output_cred_handle,
 
2530
                                      gss_OID_set *actual_mechs,
 
2531
                                      OM_uint32 *time_rec)
 
2532
{
 
2533
        OM_uint32 status, tmpmin;
 
2534
        gss_OID_set amechs = GSS_C_NULL_OID_SET;
 
2535
        gss_cred_id_t mcred = NULL;
 
2536
        spnego_gss_cred_id_t spcred = NULL;
 
2537
 
 
2538
        dsyslog("Entering spnego_gss_acquire_cred_with_password\n");
 
2539
 
 
2540
        if (actual_mechs)
 
2541
                *actual_mechs = NULL;
 
2542
 
 
2543
        if (time_rec)
 
2544
                *time_rec = 0;
 
2545
 
 
2546
        status = get_available_mechs(minor_status, desired_name,
 
2547
                                     cred_usage, GSS_C_NO_CRED_STORE,
 
2548
                                     NULL, &amechs);
 
2549
        if (status != GSS_S_COMPLETE)
 
2550
            goto cleanup;
 
2551
 
 
2552
        status = gss_acquire_cred_with_password(minor_status, desired_name,
 
2553
                                                password, time_req, amechs,
 
2554
                                                cred_usage, &mcred,
 
2555
                                                actual_mechs, time_rec);
 
2556
        if (status != GSS_S_COMPLETE)
 
2557
            goto cleanup;
 
2558
 
 
2559
        spcred = malloc(sizeof(spnego_gss_cred_id_rec));
 
2560
        if (spcred == NULL) {
 
2561
                *minor_status = ENOMEM;
 
2562
                status = GSS_S_FAILURE;
 
2563
                goto cleanup;
 
2564
        }
 
2565
        spcred->neg_mechs = GSS_C_NULL_OID_SET;
 
2566
        spcred->mcred = mcred;
 
2567
        mcred = GSS_C_NO_CREDENTIAL;
 
2568
        *output_cred_handle = (gss_cred_id_t)spcred;
 
2569
 
 
2570
cleanup:
 
2571
 
 
2572
        (void) gss_release_oid_set(&tmpmin, &amechs);
 
2573
        (void) gss_release_cred(&tmpmin, &mcred);
 
2574
 
 
2575
        dsyslog("Leaving spnego_gss_acquire_cred_with_password\n");
 
2576
        return (status);
 
2577
}
 
2578
 
 
2579
OM_uint32 KRB5_CALLCONV
 
2580
spnego_gss_display_name_ext(OM_uint32 *minor_status,
 
2581
                            gss_name_t name,
 
2582
                            gss_OID display_as_name_type,
 
2583
                            gss_buffer_t display_name)
 
2584
{
 
2585
        OM_uint32 ret;
 
2586
        ret = gss_display_name_ext(minor_status,
 
2587
                                   name,
 
2588
                                   display_as_name_type,
 
2589
                                   display_name);
 
2590
        return (ret);
 
2591
}
 
2592
 
 
2593
 
 
2594
OM_uint32 KRB5_CALLCONV
 
2595
spnego_gss_inquire_name(OM_uint32 *minor_status,
 
2596
                        gss_name_t name,
 
2597
                        int *name_is_MN,
 
2598
                        gss_OID *MN_mech,
 
2599
                        gss_buffer_set_t *attrs)
 
2600
{
 
2601
        OM_uint32 ret;
 
2602
        ret = gss_inquire_name(minor_status,
 
2603
                               name,
 
2604
                               name_is_MN,
 
2605
                               MN_mech,
 
2606
                               attrs);
 
2607
        return (ret);
 
2608
}
 
2609
 
 
2610
OM_uint32 KRB5_CALLCONV
 
2611
spnego_gss_get_name_attribute(OM_uint32 *minor_status,
 
2612
                              gss_name_t name,
 
2613
                              gss_buffer_t attr,
 
2614
                              int *authenticated,
 
2615
                              int *complete,
 
2616
                              gss_buffer_t value,
 
2617
                              gss_buffer_t display_value,
 
2618
                              int *more)
 
2619
{
 
2620
        OM_uint32 ret;
 
2621
        ret = gss_get_name_attribute(minor_status,
 
2622
                                     name,
 
2623
                                     attr,
 
2624
                                     authenticated,
 
2625
                                     complete,
 
2626
                                     value,
 
2627
                                     display_value,
 
2628
                                     more);
 
2629
        return (ret);
 
2630
}
 
2631
 
 
2632
OM_uint32 KRB5_CALLCONV
 
2633
spnego_gss_set_name_attribute(OM_uint32 *minor_status,
 
2634
                              gss_name_t name,
 
2635
                              int complete,
 
2636
                              gss_buffer_t attr,
 
2637
                              gss_buffer_t value)
 
2638
{
 
2639
        OM_uint32 ret;
 
2640
        ret = gss_set_name_attribute(minor_status,
 
2641
                                     name,
 
2642
                                     complete,
 
2643
                                     attr,
 
2644
                                     value);
 
2645
        return (ret);
 
2646
}
 
2647
 
 
2648
OM_uint32 KRB5_CALLCONV
 
2649
spnego_gss_delete_name_attribute(OM_uint32 *minor_status,
 
2650
                                 gss_name_t name,
 
2651
                                 gss_buffer_t attr)
 
2652
{
 
2653
        OM_uint32 ret;
 
2654
        ret = gss_delete_name_attribute(minor_status,
 
2655
                                        name,
 
2656
                                        attr);
 
2657
        return (ret);
 
2658
}
 
2659
 
 
2660
OM_uint32 KRB5_CALLCONV
 
2661
spnego_gss_export_name_composite(OM_uint32 *minor_status,
 
2662
                                 gss_name_t name,
 
2663
                                 gss_buffer_t exp_composite_name)
 
2664
{
 
2665
        OM_uint32 ret;
 
2666
        ret = gss_export_name_composite(minor_status,
 
2667
                                        name,
 
2668
                                        exp_composite_name);
 
2669
        return (ret);
 
2670
}
 
2671
 
 
2672
OM_uint32 KRB5_CALLCONV
 
2673
spnego_gss_map_name_to_any(OM_uint32 *minor_status,
 
2674
                           gss_name_t name,
 
2675
                           int authenticated,
 
2676
                           gss_buffer_t type_id,
 
2677
                           gss_any_t *output)
 
2678
{
 
2679
        OM_uint32 ret;
 
2680
        ret = gss_map_name_to_any(minor_status,
 
2681
                                  name,
 
2682
                                  authenticated,
 
2683
                                  type_id,
 
2684
                                  output);
 
2685
        return (ret);
 
2686
}
 
2687
 
 
2688
OM_uint32 KRB5_CALLCONV
 
2689
spnego_gss_release_any_name_mapping(OM_uint32 *minor_status,
 
2690
                                    gss_name_t name,
 
2691
                                    gss_buffer_t type_id,
 
2692
                                    gss_any_t *input)
 
2693
{
 
2694
        OM_uint32 ret;
 
2695
        ret = gss_release_any_name_mapping(minor_status,
 
2696
                                           name,
 
2697
                                           type_id,
 
2698
                                           input);
 
2699
        return (ret);
 
2700
}
 
2701
 
 
2702
OM_uint32 KRB5_CALLCONV
 
2703
spnego_gss_pseudo_random(OM_uint32 *minor_status,
 
2704
                         gss_ctx_id_t context,
 
2705
                         int prf_key,
 
2706
                         const gss_buffer_t prf_in,
 
2707
                         ssize_t desired_output_len,
 
2708
                         gss_buffer_t prf_out)
 
2709
{
 
2710
        OM_uint32 ret;
 
2711
        ret = gss_pseudo_random(minor_status,
 
2712
                                context,
 
2713
                                prf_key,
 
2714
                                prf_in,
 
2715
                                desired_output_len,
 
2716
                                prf_out);
 
2717
        return (ret);
 
2718
}
 
2719
 
 
2720
OM_uint32 KRB5_CALLCONV
 
2721
spnego_gss_set_neg_mechs(OM_uint32 *minor_status,
 
2722
                         gss_cred_id_t cred_handle,
 
2723
                         const gss_OID_set mech_list)
 
2724
{
 
2725
        OM_uint32 ret;
 
2726
        spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)cred_handle;
 
2727
 
 
2728
        /* Store mech_list in spcred for use in negotiation logic. */
 
2729
        gss_release_oid_set(minor_status, &spcred->neg_mechs);
 
2730
        ret = generic_gss_copy_oid_set(minor_status, mech_list,
 
2731
                                       &spcred->neg_mechs);
 
2732
        return (ret);
 
2733
}
 
2734
 
 
2735
#define SPNEGO_SASL_NAME        "SPNEGO"
 
2736
#define SPNEGO_SASL_NAME_LEN    (sizeof(SPNEGO_SASL_NAME) - 1)
 
2737
 
 
2738
OM_uint32 KRB5_CALLCONV
 
2739
spnego_gss_inquire_mech_for_saslname(OM_uint32 *minor_status,
 
2740
                                     const gss_buffer_t sasl_mech_name,
 
2741
                                     gss_OID *mech_type)
 
2742
{
 
2743
        if (sasl_mech_name->length == SPNEGO_SASL_NAME_LEN &&
 
2744
            memcmp(sasl_mech_name->value, SPNEGO_SASL_NAME,
 
2745
                   SPNEGO_SASL_NAME_LEN) == 0) {
 
2746
                if (mech_type != NULL)
 
2747
                        *mech_type = (gss_OID)gss_mech_spnego;
 
2748
                return (GSS_S_COMPLETE);
 
2749
        }
 
2750
 
 
2751
        return (GSS_S_BAD_MECH);
 
2752
}
 
2753
 
 
2754
OM_uint32 KRB5_CALLCONV
 
2755
spnego_gss_inquire_saslname_for_mech(OM_uint32 *minor_status,
 
2756
                                     const gss_OID desired_mech,
 
2757
                                     gss_buffer_t sasl_mech_name,
 
2758
                                     gss_buffer_t mech_name,
 
2759
                                     gss_buffer_t mech_description)
 
2760
{
 
2761
        *minor_status = 0;
 
2762
 
 
2763
        if (!g_OID_equal(desired_mech, gss_mech_spnego))
 
2764
                return (GSS_S_BAD_MECH);
 
2765
 
 
2766
        if (!g_make_string_buffer(SPNEGO_SASL_NAME, sasl_mech_name) ||
 
2767
            !g_make_string_buffer("spnego", mech_name) ||
 
2768
            !g_make_string_buffer("Simple and Protected GSS-API "
 
2769
                                  "Negotiation Mechanism", mech_description))
 
2770
                goto fail;
 
2771
 
 
2772
        return (GSS_S_COMPLETE);
 
2773
 
 
2774
fail:
 
2775
        *minor_status = ENOMEM;
 
2776
        return (GSS_S_FAILURE);
 
2777
}
 
2778
 
 
2779
OM_uint32 KRB5_CALLCONV
 
2780
spnego_gss_inquire_attrs_for_mech(OM_uint32 *minor_status,
 
2781
                                  gss_const_OID mech,
 
2782
                                  gss_OID_set *mech_attrs,
 
2783
                                  gss_OID_set *known_mech_attrs)
 
2784
{
 
2785
        OM_uint32 major, tmpMinor;
 
2786
 
 
2787
        /* known_mech_attrs is handled by mechglue */
 
2788
        *minor_status = 0;
 
2789
 
 
2790
        if (mech_attrs == NULL)
 
2791
            return (GSS_S_COMPLETE);
 
2792
 
 
2793
        major = gss_create_empty_oid_set(minor_status, mech_attrs);
 
2794
        if (GSS_ERROR(major))
 
2795
                goto cleanup;
 
2796
 
 
2797
#define MA_SUPPORTED(ma)    do {                                        \
 
2798
                major = gss_add_oid_set_member(minor_status,            \
 
2799
                                               (gss_OID)ma, mech_attrs); \
 
2800
                if (GSS_ERROR(major))                                   \
 
2801
                        goto cleanup;                                   \
 
2802
        } while (0)
 
2803
 
 
2804
        MA_SUPPORTED(GSS_C_MA_MECH_NEGO);
 
2805
        MA_SUPPORTED(GSS_C_MA_ITOK_FRAMED);
 
2806
 
 
2807
cleanup:
 
2808
        if (GSS_ERROR(major))
 
2809
                gss_release_oid_set(&tmpMinor, mech_attrs);
 
2810
 
 
2811
        return (major);
 
2812
}
 
2813
 
 
2814
OM_uint32 KRB5_CALLCONV
 
2815
spnego_gss_export_cred(OM_uint32 *minor_status,
 
2816
                       gss_cred_id_t cred_handle,
 
2817
                       gss_buffer_t token)
 
2818
{
 
2819
        spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)cred_handle;
 
2820
 
 
2821
        return (gss_export_cred(minor_status, spcred->mcred, token));
 
2822
}
 
2823
 
 
2824
OM_uint32 KRB5_CALLCONV
 
2825
spnego_gss_import_cred(OM_uint32 *minor_status,
 
2826
                       gss_buffer_t token,
 
2827
                       gss_cred_id_t *cred_handle)
 
2828
{
 
2829
        OM_uint32 ret;
 
2830
        spnego_gss_cred_id_t spcred;
 
2831
        gss_cred_id_t mcred;
 
2832
 
 
2833
        ret = gss_import_cred(minor_status, token, &mcred);
 
2834
        if (GSS_ERROR(ret))
 
2835
                return (ret);
 
2836
        spcred = malloc(sizeof(*spcred));
 
2837
        if (spcred == NULL) {
 
2838
                gss_release_cred(minor_status, &mcred);
 
2839
                *minor_status = ENOMEM;
 
2840
                return (GSS_S_FAILURE);
 
2841
        }
 
2842
        spcred->mcred = mcred;
 
2843
        spcred->neg_mechs = GSS_C_NULL_OID_SET;
 
2844
        *cred_handle = (gss_cred_id_t)spcred;
 
2845
        return (ret);
 
2846
}
 
2847
 
 
2848
OM_uint32 KRB5_CALLCONV
 
2849
spnego_gss_get_mic_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
 
2850
                       gss_qop_t qop_req, gss_iov_buffer_desc *iov,
 
2851
                       int iov_count)
 
2852
{
 
2853
    return gss_get_mic_iov(minor_status, context_handle, qop_req, iov,
 
2854
                           iov_count);
 
2855
}
 
2856
 
 
2857
OM_uint32 KRB5_CALLCONV
 
2858
spnego_gss_verify_mic_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
 
2859
                          gss_qop_t *qop_state, gss_iov_buffer_desc *iov,
 
2860
                          int iov_count)
 
2861
{
 
2862
    return gss_verify_mic_iov(minor_status, context_handle, qop_state, iov,
 
2863
                              iov_count);
 
2864
}
 
2865
 
 
2866
OM_uint32 KRB5_CALLCONV
 
2867
spnego_gss_get_mic_iov_length(OM_uint32 *minor_status,
 
2868
                              gss_ctx_id_t context_handle, gss_qop_t qop_req,
 
2869
                              gss_iov_buffer_desc *iov, int iov_count)
 
2870
{
 
2871
    return gss_get_mic_iov_length(minor_status, context_handle, qop_req, iov,
 
2872
                                  iov_count);
 
2873
}
 
2874
 
 
2875
/*
 
2876
 * We will release everything but the ctx_handle so that it
 
2877
 * can be passed back to init/accept context. This routine should
 
2878
 * not be called until after the ctx_handle memory is assigned to
 
2879
 * the supplied context handle from init/accept context.
 
2880
 */
 
2881
static void
 
2882
release_spnego_ctx(spnego_gss_ctx_id_t *ctx)
 
2883
{
 
2884
        spnego_gss_ctx_id_t context;
 
2885
        OM_uint32 minor_stat;
 
2886
        context = *ctx;
 
2887
 
 
2888
        if (context != NULL) {
 
2889
                (void) gss_release_buffer(&minor_stat,
 
2890
                                        &context->DER_mechTypes);
 
2891
 
 
2892
                (void) gss_release_oid_set(&minor_stat, &context->mech_set);
 
2893
 
 
2894
                (void) gss_release_name(&minor_stat, &context->internal_name);
 
2895
 
 
2896
                if (context->optionStr != NULL) {
 
2897
                        free(context->optionStr);
 
2898
                        context->optionStr = NULL;
 
2899
                }
 
2900
                free(context);
 
2901
                *ctx = NULL;
 
2902
        }
 
2903
}
 
2904
 
 
2905
/*
 
2906
 * Can't use gss_indicate_mechs by itself to get available mechs for
 
2907
 * SPNEGO because it will also return the SPNEGO mech and we do not
 
2908
 * want to consider SPNEGO as an available security mech for
 
2909
 * negotiation. For this reason, get_available_mechs will return
 
2910
 * all available mechs except SPNEGO.
 
2911
 *
 
2912
 * If a ptr to a creds list is given, this function will attempt
 
2913
 * to acquire creds for the creds given and trim the list of
 
2914
 * returned mechanisms to only those for which creds are valid.
 
2915
 *
 
2916
 */
 
2917
static OM_uint32
 
2918
get_available_mechs(OM_uint32 *minor_status,
 
2919
        gss_name_t name, gss_cred_usage_t usage,
 
2920
        gss_const_key_value_set_t cred_store,
 
2921
        gss_cred_id_t *creds, gss_OID_set *rmechs)
 
2922
{
 
2923
        unsigned int    i;
 
2924
        int             found = 0;
 
2925
        OM_uint32 major_status = GSS_S_COMPLETE, tmpmin;
 
2926
        gss_OID_set mechs, goodmechs;
 
2927
 
 
2928
        major_status = gss_indicate_mechs(minor_status, &mechs);
 
2929
 
 
2930
        if (major_status != GSS_S_COMPLETE) {
 
2931
                return (major_status);
 
2932
        }
 
2933
 
 
2934
        major_status = gss_create_empty_oid_set(minor_status, rmechs);
 
2935
 
 
2936
        if (major_status != GSS_S_COMPLETE) {
 
2937
                (void) gss_release_oid_set(minor_status, &mechs);
 
2938
                return (major_status);
 
2939
        }
 
2940
 
 
2941
        for (i = 0; i < mechs->count && major_status == GSS_S_COMPLETE; i++) {
 
2942
                if ((mechs->elements[i].length
 
2943
                    != spnego_mechanism.mech_type.length) ||
 
2944
                    memcmp(mechs->elements[i].elements,
 
2945
                        spnego_mechanism.mech_type.elements,
 
2946
                        spnego_mechanism.mech_type.length)) {
 
2947
 
 
2948
                        major_status = gss_add_oid_set_member(minor_status,
 
2949
                                                              &mechs->elements[i],
 
2950
                                                              rmechs);
 
2951
                        if (major_status == GSS_S_COMPLETE)
 
2952
                                found++;
 
2953
                }
 
2954
        }
 
2955
 
 
2956
        /*
 
2957
         * If the caller wanted a list of creds returned,
 
2958
         * trim the list of mechanisms down to only those
 
2959
         * for which the creds are valid.
 
2960
         */
 
2961
        if (found > 0 && major_status == GSS_S_COMPLETE && creds != NULL) {
 
2962
                major_status = gss_acquire_cred_from(minor_status, name,
 
2963
                                                     GSS_C_INDEFINITE,
 
2964
                                                     *rmechs, usage,
 
2965
                                                     cred_store, creds,
 
2966
                                                     &goodmechs, NULL);
 
2967
 
 
2968
                /*
 
2969
                 * Drop the old list in favor of the new
 
2970
                 * "trimmed" list.
 
2971
                 */
 
2972
                (void) gss_release_oid_set(&tmpmin, rmechs);
 
2973
                if (major_status == GSS_S_COMPLETE) {
 
2974
                        (void) gssint_copy_oid_set(&tmpmin,
 
2975
                                        goodmechs, rmechs);
 
2976
                        (void) gss_release_oid_set(&tmpmin, &goodmechs);
 
2977
                }
 
2978
        }
 
2979
 
 
2980
        (void) gss_release_oid_set(&tmpmin, &mechs);
 
2981
        if (found == 0 || major_status != GSS_S_COMPLETE) {
 
2982
                *minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE;
 
2983
                map_errcode(minor_status);
 
2984
                if (major_status == GSS_S_COMPLETE)
 
2985
                        major_status = GSS_S_FAILURE;
 
2986
        }
 
2987
 
 
2988
        return (major_status);
 
2989
}
 
2990
 
 
2991
/*
 
2992
 * Return a list of mechanisms we are willing to negotiate for a credential,
 
2993
 * taking into account the mech set provided with gss_set_neg_mechs if it
 
2994
 * exists.
 
2995
 */
 
2996
static OM_uint32
 
2997
get_negotiable_mechs(OM_uint32 *minor_status, spnego_gss_cred_id_t spcred,
 
2998
                     gss_cred_usage_t usage, gss_OID_set *rmechs)
 
2999
{
 
3000
        OM_uint32 ret, tmpmin;
 
3001
        gss_cred_id_t creds = GSS_C_NO_CREDENTIAL, *credptr;
 
3002
        gss_OID_set cred_mechs = GSS_C_NULL_OID_SET;
 
3003
        gss_OID_set intersect_mechs = GSS_C_NULL_OID_SET;
 
3004
        unsigned int i;
 
3005
        int present;
 
3006
 
 
3007
        if (spcred == NULL) {
 
3008
                /*
 
3009
                 * The default credentials were supplied.  Return a list of all
 
3010
                 * available mechs except SPNEGO.  When initiating, trim this
 
3011
                 * list to mechs we can acquire credentials for.
 
3012
                 */
 
3013
                credptr = (usage == GSS_C_INITIATE) ? &creds : NULL;
 
3014
                ret = get_available_mechs(minor_status, GSS_C_NO_NAME, usage,
 
3015
                                          GSS_C_NO_CRED_STORE, credptr,
 
3016
                                          rmechs);
 
3017
                gss_release_cred(&tmpmin, &creds);
 
3018
                return (ret);
 
3019
        }
 
3020
 
 
3021
        /* Get the list of mechs in the mechglue cred. */
 
3022
        ret = gss_inquire_cred(minor_status, spcred->mcred, NULL, NULL, NULL,
 
3023
                               &cred_mechs);
 
3024
        if (ret != GSS_S_COMPLETE)
 
3025
                return (ret);
 
3026
 
 
3027
        if (spcred->neg_mechs == GSS_C_NULL_OID_SET) {
 
3028
                /* gss_set_neg_mechs was never called; return cred_mechs. */
 
3029
                *rmechs = cred_mechs;
 
3030
                *minor_status = 0;
 
3031
                return (GSS_S_COMPLETE);
 
3032
        }
 
3033
 
 
3034
        /* Compute the intersection of cred_mechs and spcred->neg_mechs,
 
3035
         * preserving the order in spcred->neg_mechs. */
 
3036
        ret = gss_create_empty_oid_set(minor_status, &intersect_mechs);
 
3037
        if (ret != GSS_S_COMPLETE) {
 
3038
                gss_release_oid_set(&tmpmin, &cred_mechs);
 
3039
                return (ret);
 
3040
        }
 
3041
 
 
3042
        for (i = 0; i < spcred->neg_mechs->count; i++) {
 
3043
                gss_test_oid_set_member(&tmpmin,
 
3044
                                        &spcred->neg_mechs->elements[i],
 
3045
                                        cred_mechs, &present);
 
3046
                if (!present)
 
3047
                        continue;
 
3048
                ret = gss_add_oid_set_member(minor_status,
 
3049
                                             &spcred->neg_mechs->elements[i],
 
3050
                                             &intersect_mechs);
 
3051
                if (ret != GSS_S_COMPLETE)
 
3052
                        break;
 
3053
        }
 
3054
 
 
3055
        gss_release_oid_set(&tmpmin, &cred_mechs);
 
3056
        if (intersect_mechs->count == 0 || ret != GSS_S_COMPLETE) {
 
3057
                gss_release_oid_set(&tmpmin, &intersect_mechs);
 
3058
                *minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE;
 
3059
                map_errcode(minor_status);
 
3060
                return (GSS_S_FAILURE);
 
3061
        }
 
3062
 
 
3063
        *rmechs = intersect_mechs;
 
3064
        *minor_status = 0;
 
3065
        return (GSS_S_COMPLETE);
 
3066
}
 
3067
 
 
3068
/* following are token creation and reading routines */
 
3069
 
 
3070
/*
 
3071
 * If buff_in is not pointing to a MECH_OID, then return NULL and do not
 
3072
 * advance the buffer, otherwise, decode the mech_oid from the buffer and
 
3073
 * place in gss_OID.
 
3074
 */
 
3075
static gss_OID
 
3076
get_mech_oid(OM_uint32 *minor_status, unsigned char **buff_in, size_t length)
 
3077
{
 
3078
        OM_uint32       status;
 
3079
        gss_OID_desc    toid;
 
3080
        gss_OID         mech_out = NULL;
 
3081
        unsigned char           *start, *end;
 
3082
 
 
3083
        if (length < 1 || **buff_in != MECH_OID)
 
3084
                return (NULL);
 
3085
 
 
3086
        start = *buff_in;
 
3087
        end = start + length;
 
3088
 
 
3089
        (*buff_in)++;
 
3090
        toid.length = *(*buff_in)++;
 
3091
 
 
3092
        if ((*buff_in + toid.length) > end)
 
3093
                return (NULL);
 
3094
 
 
3095
        toid.elements = *buff_in;
 
3096
        *buff_in += toid.length;
 
3097
 
 
3098
        status = generic_gss_copy_oid(minor_status, &toid, &mech_out);
 
3099
 
 
3100
        if (status != GSS_S_COMPLETE) {
 
3101
                map_errcode(minor_status);
 
3102
                mech_out = NULL;
 
3103
        }
 
3104
 
 
3105
        return (mech_out);
 
3106
}
 
3107
 
 
3108
/*
 
3109
 * der encode the given mechanism oid into buf_out, advancing the
 
3110
 * buffer pointer.
 
3111
 */
 
3112
 
 
3113
static int
 
3114
put_mech_oid(unsigned char **buf_out, gss_OID_const mech, unsigned int buflen)
 
3115
{
 
3116
        if (buflen < mech->length + 2)
 
3117
                return (-1);
 
3118
        *(*buf_out)++ = MECH_OID;
 
3119
        *(*buf_out)++ = (unsigned char) mech->length;
 
3120
        memcpy(*buf_out, mech->elements, mech->length);
 
3121
        *buf_out += mech->length;
 
3122
        return (0);
 
3123
}
 
3124
 
 
3125
/*
 
3126
 * verify that buff_in points to an octet string, if it does not,
 
3127
 * return NULL and don't advance the pointer. If it is an octet string
 
3128
 * decode buff_in into a gss_buffer_t and return it, advancing the
 
3129
 * buffer pointer.
 
3130
 */
 
3131
static gss_buffer_t
 
3132
get_input_token(unsigned char **buff_in, unsigned int buff_length)
 
3133
{
 
3134
        gss_buffer_t input_token;
 
3135
        unsigned int len;
 
3136
 
 
3137
        if (g_get_tag_and_length(buff_in, OCTET_STRING, buff_length, &len) < 0)
 
3138
                return (NULL);
 
3139
 
 
3140
        input_token = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
 
3141
        if (input_token == NULL)
 
3142
                return (NULL);
 
3143
 
 
3144
        input_token->length = len;
 
3145
        input_token->value = gssalloc_malloc(input_token->length);
 
3146
 
 
3147
        if (input_token->value == NULL) {
 
3148
                free(input_token);
 
3149
                return (NULL);
 
3150
        }
 
3151
 
 
3152
        (void) memcpy(input_token->value, *buff_in, input_token->length);
 
3153
        *buff_in += input_token->length;
 
3154
        return (input_token);
 
3155
}
 
3156
 
 
3157
/*
 
3158
 * verify that the input token length is not 0. If it is, just return.
 
3159
 * If the token length is greater than 0, der encode as an octet string
 
3160
 * and place in buf_out, advancing buf_out.
 
3161
 */
 
3162
 
 
3163
static int
 
3164
put_input_token(unsigned char **buf_out, gss_buffer_t input_token,
 
3165
                unsigned int buflen)
 
3166
{
 
3167
        int ret;
 
3168
 
 
3169
        /* if token length is 0, we do not want to send */
 
3170
        if (input_token->length == 0)
 
3171
                return (0);
 
3172
 
 
3173
        if (input_token->length > buflen)
 
3174
                return (-1);
 
3175
 
 
3176
        *(*buf_out)++ = OCTET_STRING;
 
3177
        if ((ret = gssint_put_der_length(input_token->length, buf_out,
 
3178
                            input_token->length)))
 
3179
                return (ret);
 
3180
        TWRITE_STR(*buf_out, input_token->value, input_token->length);
 
3181
        return (0);
 
3182
}
 
3183
 
 
3184
/*
 
3185
 * verify that buff_in points to a sequence of der encoding. The mech
 
3186
 * set is the only sequence of encoded object in the token, so if it is
 
3187
 * a sequence of encoding, decode the mechset into a gss_OID_set and
 
3188
 * return it, advancing the buffer pointer.
 
3189
 */
 
3190
static gss_OID_set
 
3191
get_mech_set(OM_uint32 *minor_status, unsigned char **buff_in,
 
3192
             unsigned int buff_length)
 
3193
{
 
3194
        gss_OID_set returned_mechSet;
 
3195
        OM_uint32 major_status;
 
3196
        int length;
 
3197
        unsigned int bytes;
 
3198
        OM_uint32 set_length;
 
3199
        unsigned char           *start;
 
3200
        int i;
 
3201
 
 
3202
        if (**buff_in != SEQUENCE_OF)
 
3203
                return (NULL);
 
3204
 
 
3205
        start = *buff_in;
 
3206
        (*buff_in)++;
 
3207
 
 
3208
        length = gssint_get_der_length(buff_in, buff_length, &bytes);
 
3209
        if (length < 0 || buff_length - bytes < (unsigned int)length)
 
3210
                return NULL;
 
3211
 
 
3212
        major_status = gss_create_empty_oid_set(minor_status,
 
3213
                                                &returned_mechSet);
 
3214
        if (major_status != GSS_S_COMPLETE)
 
3215
                return (NULL);
 
3216
 
 
3217
        for (set_length = 0, i = 0; set_length < (unsigned int)length; i++) {
 
3218
                gss_OID_desc *temp = get_mech_oid(minor_status, buff_in,
 
3219
                        buff_length - (*buff_in - start));
 
3220
                if (temp == NULL)
 
3221
                        break;
 
3222
 
 
3223
                major_status = gss_add_oid_set_member(minor_status,
 
3224
                                                      temp, &returned_mechSet);
 
3225
                if (major_status == GSS_S_COMPLETE) {
 
3226
                        set_length += returned_mechSet->elements[i].length +2;
 
3227
                        if (generic_gss_release_oid(minor_status, &temp))
 
3228
                                map_errcode(minor_status);
 
3229
                }
 
3230
        }
 
3231
 
 
3232
        return (returned_mechSet);
 
3233
}
 
3234
 
 
3235
/*
 
3236
 * Encode mechSet into buf.
 
3237
 */
 
3238
static int
 
3239
put_mech_set(gss_OID_set mechSet, gss_buffer_t buf)
 
3240
{
 
3241
        unsigned char *ptr;
 
3242
        unsigned int i;
 
3243
        unsigned int tlen, ilen;
 
3244
 
 
3245
        tlen = ilen = 0;
 
3246
        for (i = 0; i < mechSet->count; i++) {
 
3247
                /*
 
3248
                 * 0x06 [DER LEN] [OID]
 
3249
                 */
 
3250
                ilen += 1 +
 
3251
                        gssint_der_length_size(mechSet->elements[i].length) +
 
3252
                        mechSet->elements[i].length;
 
3253
        }
 
3254
        /*
 
3255
         * 0x30 [DER LEN]
 
3256
         */
 
3257
        tlen = 1 + gssint_der_length_size(ilen) + ilen;
 
3258
        ptr = gssalloc_malloc(tlen);
 
3259
        if (ptr == NULL)
 
3260
                return -1;
 
3261
 
 
3262
        buf->value = ptr;
 
3263
        buf->length = tlen;
 
3264
#define REMAIN (buf->length - ((unsigned char *)buf->value - ptr))
 
3265
 
 
3266
        *ptr++ = SEQUENCE_OF;
 
3267
        if (gssint_put_der_length(ilen, &ptr, REMAIN) < 0)
 
3268
                return -1;
 
3269
        for (i = 0; i < mechSet->count; i++) {
 
3270
                if (put_mech_oid(&ptr, &mechSet->elements[i], REMAIN) < 0) {
 
3271
                        return -1;
 
3272
                }
 
3273
        }
 
3274
        return 0;
 
3275
#undef REMAIN
 
3276
}
 
3277
 
 
3278
/*
 
3279
 * Verify that buff_in is pointing to a BIT_STRING with the correct
 
3280
 * length and padding for the req_flags. If it is, decode req_flags
 
3281
 * and return them, otherwise, return NULL.
 
3282
 */
 
3283
static OM_uint32
 
3284
get_req_flags(unsigned char **buff_in, OM_uint32 bodysize,
 
3285
              OM_uint32 *req_flags)
 
3286
{
 
3287
        unsigned int len;
 
3288
 
 
3289
        if (**buff_in != (CONTEXT | 0x01))
 
3290
                return (0);
 
3291
 
 
3292
        if (g_get_tag_and_length(buff_in, (CONTEXT | 0x01),
 
3293
                                bodysize, &len) < 0)
 
3294
                return GSS_S_DEFECTIVE_TOKEN;
 
3295
 
 
3296
        if (*(*buff_in)++ != BIT_STRING)
 
3297
                return GSS_S_DEFECTIVE_TOKEN;
 
3298
 
 
3299
        if (*(*buff_in)++ != BIT_STRING_LENGTH)
 
3300
                return GSS_S_DEFECTIVE_TOKEN;
 
3301
 
 
3302
        if (*(*buff_in)++ != BIT_STRING_PADDING)
 
3303
                return GSS_S_DEFECTIVE_TOKEN;
 
3304
 
 
3305
        *req_flags = (OM_uint32) (*(*buff_in)++ >> 1);
 
3306
        return (0);
 
3307
}
 
3308
 
 
3309
static OM_uint32
 
3310
get_negTokenInit(OM_uint32 *minor_status,
 
3311
                 gss_buffer_t buf,
 
3312
                 gss_buffer_t der_mechSet,
 
3313
                 gss_OID_set *mechSet,
 
3314
                 OM_uint32 *req_flags,
 
3315
                 gss_buffer_t *mechtok,
 
3316
                 gss_buffer_t *mechListMIC)
 
3317
{
 
3318
        OM_uint32 err;
 
3319
        unsigned char *ptr, *bufstart;
 
3320
        unsigned int len;
 
3321
        gss_buffer_desc tmpbuf;
 
3322
 
 
3323
        *minor_status = 0;
 
3324
        der_mechSet->length = 0;
 
3325
        der_mechSet->value = NULL;
 
3326
        *mechSet = GSS_C_NO_OID_SET;
 
3327
        *req_flags = 0;
 
3328
        *mechtok = *mechListMIC = GSS_C_NO_BUFFER;
 
3329
 
 
3330
        ptr = bufstart = buf->value;
 
3331
        if ((buf->length - (ptr - bufstart)) > INT_MAX)
 
3332
                return GSS_S_FAILURE;
 
3333
#define REMAIN (buf->length - (ptr - bufstart))
 
3334
 
 
3335
        err = g_verify_token_header(gss_mech_spnego,
 
3336
                                    &len, &ptr, 0, REMAIN);
 
3337
        if (err) {
 
3338
                *minor_status = err;
 
3339
                map_errcode(minor_status);
 
3340
                return GSS_S_FAILURE;
 
3341
        }
 
3342
        *minor_status = g_verify_neg_token_init(&ptr, REMAIN);
 
3343
        if (*minor_status) {
 
3344
                map_errcode(minor_status);
 
3345
                return GSS_S_FAILURE;
 
3346
        }
 
3347
 
 
3348
        /* alias into input_token */
 
3349
        tmpbuf.value = ptr;
 
3350
        tmpbuf.length = REMAIN;
 
3351
        *mechSet = get_mech_set(minor_status, &ptr, REMAIN);
 
3352
        if (*mechSet == NULL)
 
3353
                return GSS_S_FAILURE;
 
3354
 
 
3355
        tmpbuf.length = ptr - (unsigned char *)tmpbuf.value;
 
3356
        der_mechSet->value = gssalloc_malloc(tmpbuf.length);
 
3357
        if (der_mechSet->value == NULL)
 
3358
                return GSS_S_FAILURE;
 
3359
        memcpy(der_mechSet->value, tmpbuf.value, tmpbuf.length);
 
3360
        der_mechSet->length = tmpbuf.length;
 
3361
 
 
3362
        err = get_req_flags(&ptr, REMAIN, req_flags);
 
3363
        if (err != GSS_S_COMPLETE) {
 
3364
                return err;
 
3365
        }
 
3366
        if (g_get_tag_and_length(&ptr, (CONTEXT | 0x02),
 
3367
                                 REMAIN, &len) >= 0) {
 
3368
                *mechtok = get_input_token(&ptr, len);
 
3369
                if (*mechtok == GSS_C_NO_BUFFER) {
 
3370
                        return GSS_S_FAILURE;
 
3371
                }
 
3372
        }
 
3373
        if (g_get_tag_and_length(&ptr, (CONTEXT | 0x03),
 
3374
                                 REMAIN, &len) >= 0) {
 
3375
                *mechListMIC = get_input_token(&ptr, len);
 
3376
                if (*mechListMIC == GSS_C_NO_BUFFER) {
 
3377
                        return GSS_S_FAILURE;
 
3378
                }
 
3379
        }
 
3380
        return GSS_S_COMPLETE;
 
3381
#undef REMAIN
 
3382
}
 
3383
 
 
3384
static OM_uint32
 
3385
get_negTokenResp(OM_uint32 *minor_status,
 
3386
                 unsigned char *buf, unsigned int buflen,
 
3387
                 OM_uint32 *negState,
 
3388
                 gss_OID *supportedMech,
 
3389
                 gss_buffer_t *responseToken,
 
3390
                 gss_buffer_t *mechListMIC)
 
3391
{
 
3392
        unsigned char *ptr, *bufstart;
 
3393
        unsigned int len;
 
3394
        int tmplen;
 
3395
        unsigned int tag, bytes;
 
3396
 
 
3397
        *negState = ACCEPT_DEFECTIVE_TOKEN;
 
3398
        *supportedMech = GSS_C_NO_OID;
 
3399
        *responseToken = *mechListMIC = GSS_C_NO_BUFFER;
 
3400
        ptr = bufstart = buf;
 
3401
#define REMAIN (buflen - (ptr - bufstart))
 
3402
 
 
3403
        if (g_get_tag_and_length(&ptr, (CONTEXT | 0x01), REMAIN, &len) < 0)
 
3404
                return GSS_S_DEFECTIVE_TOKEN;
 
3405
        if (*ptr++ == SEQUENCE) {
 
3406
                tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
 
3407
                if (tmplen < 0 || REMAIN < (unsigned int)tmplen)
 
3408
                        return GSS_S_DEFECTIVE_TOKEN;
 
3409
        }
 
3410
        if (REMAIN < 1)
 
3411
                tag = 0;
 
3412
        else
 
3413
                tag = *ptr++;
 
3414
 
 
3415
        if (tag == CONTEXT) {
 
3416
                tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
 
3417
                if (tmplen < 0 || REMAIN < (unsigned int)tmplen)
 
3418
                        return GSS_S_DEFECTIVE_TOKEN;
 
3419
 
 
3420
                if (g_get_tag_and_length(&ptr, ENUMERATED,
 
3421
                                         REMAIN, &len) < 0)
 
3422
                        return GSS_S_DEFECTIVE_TOKEN;
 
3423
 
 
3424
                if (len != ENUMERATION_LENGTH)
 
3425
                        return GSS_S_DEFECTIVE_TOKEN;
 
3426
 
 
3427
                if (REMAIN < 1)
 
3428
                        return GSS_S_DEFECTIVE_TOKEN;
 
3429
                *negState = *ptr++;
 
3430
 
 
3431
                if (REMAIN < 1)
 
3432
                        tag = 0;
 
3433
                else
 
3434
                        tag = *ptr++;
 
3435
        }
 
3436
        if (tag == (CONTEXT | 0x01)) {
 
3437
                tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
 
3438
                if (tmplen < 0 || REMAIN < (unsigned int)tmplen)
 
3439
                        return GSS_S_DEFECTIVE_TOKEN;
 
3440
 
 
3441
                *supportedMech = get_mech_oid(minor_status, &ptr, REMAIN);
 
3442
                if (*supportedMech == GSS_C_NO_OID)
 
3443
                        return GSS_S_DEFECTIVE_TOKEN;
 
3444
 
 
3445
                if (REMAIN < 1)
 
3446
                        tag = 0;
 
3447
                else
 
3448
                        tag = *ptr++;
 
3449
        }
 
3450
        if (tag == (CONTEXT | 0x02)) {
 
3451
                tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
 
3452
                if (tmplen < 0 || REMAIN < (unsigned int)tmplen)
 
3453
                        return GSS_S_DEFECTIVE_TOKEN;
 
3454
 
 
3455
                *responseToken = get_input_token(&ptr, REMAIN);
 
3456
                if (*responseToken == GSS_C_NO_BUFFER)
 
3457
                        return GSS_S_DEFECTIVE_TOKEN;
 
3458
 
 
3459
                if (REMAIN < 1)
 
3460
                        tag = 0;
 
3461
                else
 
3462
                        tag = *ptr++;
 
3463
        }
 
3464
        if (tag == (CONTEXT | 0x03)) {
 
3465
                tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
 
3466
                if (tmplen < 0 || REMAIN < (unsigned int)tmplen)
 
3467
                        return GSS_S_DEFECTIVE_TOKEN;
 
3468
 
 
3469
                *mechListMIC = get_input_token(&ptr, REMAIN);
 
3470
                if (*mechListMIC == GSS_C_NO_BUFFER)
 
3471
                        return GSS_S_DEFECTIVE_TOKEN;
 
3472
 
 
3473
                /* Handle Windows 2000 duplicate response token */
 
3474
                if (*responseToken &&
 
3475
                    ((*responseToken)->length == (*mechListMIC)->length) &&
 
3476
                    !memcmp((*responseToken)->value, (*mechListMIC)->value,
 
3477
                            (*responseToken)->length)) {
 
3478
                        OM_uint32 tmpmin;
 
3479
 
 
3480
                        gss_release_buffer(&tmpmin, *mechListMIC);
 
3481
                        free(*mechListMIC);
 
3482
                        *mechListMIC = NULL;
 
3483
                }
 
3484
        }
 
3485
        return GSS_S_COMPLETE;
 
3486
#undef REMAIN
 
3487
}
 
3488
 
 
3489
/*
 
3490
 * der encode the passed negResults as an ENUMERATED type and
 
3491
 * place it in buf_out, advancing the buffer.
 
3492
 */
 
3493
 
 
3494
static int
 
3495
put_negResult(unsigned char **buf_out, OM_uint32 negResult,
 
3496
              unsigned int buflen)
 
3497
{
 
3498
        if (buflen < 3)
 
3499
                return (-1);
 
3500
        *(*buf_out)++ = ENUMERATED;
 
3501
        *(*buf_out)++ = ENUMERATION_LENGTH;
 
3502
        *(*buf_out)++ = (unsigned char) negResult;
 
3503
        return (0);
 
3504
}
 
3505
 
 
3506
/*
 
3507
 * This routine compares the recieved mechset to the mechset that
 
3508
 * this server can support. It looks sequentially through the mechset
 
3509
 * and the first one that matches what the server can support is
 
3510
 * chosen as the negotiated mechanism. If one is found, negResult
 
3511
 * is set to ACCEPT_INCOMPLETE if it's the first mech, REQUEST_MIC if
 
3512
 * it's not the first mech, otherwise we return NULL and negResult
 
3513
 * is set to REJECT. The returned pointer is an alias into
 
3514
 * supported->elements and should not be freed.
 
3515
 *
 
3516
 * NOTE: There is currently no way to specify a preference order of
 
3517
 * mechanisms supported by the acceptor.
 
3518
 */
 
3519
static gss_OID
 
3520
negotiate_mech(gss_OID_set supported, gss_OID_set received,
 
3521
               OM_uint32 *negResult)
 
3522
{
 
3523
        size_t i, j;
 
3524
 
 
3525
        for (i = 0; i < received->count; i++) {
 
3526
                gss_OID mech_oid = &received->elements[i];
 
3527
 
 
3528
                /* Accept wrong mechanism OID from MS clients */
 
3529
                if (g_OID_equal(mech_oid, &gss_mech_krb5_wrong_oid))
 
3530
                        mech_oid = (gss_OID)&gss_mech_krb5_oid;
 
3531
 
 
3532
                for (j = 0; j < supported->count; j++) {
 
3533
                        if (g_OID_equal(mech_oid, &supported->elements[j])) {
 
3534
                                *negResult = (i == 0) ? ACCEPT_INCOMPLETE :
 
3535
                                        REQUEST_MIC;
 
3536
                                return &supported->elements[j];
 
3537
                        }
 
3538
                }
 
3539
        }
 
3540
        *negResult = REJECT;
 
3541
        return (NULL);
 
3542
}
 
3543
 
 
3544
/*
 
3545
 * the next two routines make a token buffer suitable for
 
3546
 * spnego_gss_display_status. These currently take the string
 
3547
 * in name and place it in the token. Eventually, if
 
3548
 * spnego_gss_display_status returns valid error messages,
 
3549
 * these routines will be changes to return the error string.
 
3550
 */
 
3551
static spnego_token_t
 
3552
make_spnego_token(char *name)
 
3553
{
 
3554
        return (spnego_token_t)strdup(name);
 
3555
}
 
3556
 
 
3557
static gss_buffer_desc
 
3558
make_err_msg(char *name)
 
3559
{
 
3560
        gss_buffer_desc buffer;
 
3561
 
 
3562
        if (name == NULL) {
 
3563
                buffer.length = 0;
 
3564
                buffer.value = NULL;
 
3565
        } else {
 
3566
                buffer.length = strlen(name)+1;
 
3567
                buffer.value = make_spnego_token(name);
 
3568
        }
 
3569
 
 
3570
        return (buffer);
 
3571
}
 
3572
 
 
3573
/*
 
3574
 * Create the client side spnego token passed back to gss_init_sec_context
 
3575
 * and eventually up to the application program and over to the server.
 
3576
 *
 
3577
 * Use DER rules, definite length method per RFC 2478
 
3578
 */
 
3579
static int
 
3580
make_spnego_tokenInit_msg(spnego_gss_ctx_id_t spnego_ctx,
 
3581
                          int negHintsCompat,
 
3582
                          gss_buffer_t mechListMIC, OM_uint32 req_flags,
 
3583
                          gss_buffer_t data, send_token_flag sendtoken,
 
3584
                          gss_buffer_t outbuf)
 
3585
{
 
3586
        int ret = 0;
 
3587
        unsigned int tlen, dataLen = 0;
 
3588
        unsigned int negTokenInitSize = 0;
 
3589
        unsigned int negTokenInitSeqSize = 0;
 
3590
        unsigned int negTokenInitContSize = 0;
 
3591
        unsigned int rspTokenSize = 0;
 
3592
        unsigned int mechListTokenSize = 0;
 
3593
        unsigned int micTokenSize = 0;
 
3594
        unsigned char *t;
 
3595
        unsigned char *ptr;
 
3596
 
 
3597
        if (outbuf == GSS_C_NO_BUFFER)
 
3598
                return (-1);
 
3599
 
 
3600
        outbuf->length = 0;
 
3601
        outbuf->value = NULL;
 
3602
 
 
3603
        /* calculate the data length */
 
3604
 
 
3605
        /*
 
3606
         * 0xa0 [DER LEN] [mechTypes]
 
3607
         */
 
3608
        mechListTokenSize = 1 +
 
3609
                gssint_der_length_size(spnego_ctx->DER_mechTypes.length) +
 
3610
                spnego_ctx->DER_mechTypes.length;
 
3611
        dataLen += mechListTokenSize;
 
3612
 
 
3613
        /*
 
3614
         * If a token from gss_init_sec_context exists,
 
3615
         * add the length of the token + the ASN.1 overhead
 
3616
         */
 
3617
        if (data != NULL) {
 
3618
                /*
 
3619
                 * Encoded in final output as:
 
3620
                 * 0xa2 [DER LEN] 0x04 [DER LEN] [DATA]
 
3621
                 * -----s--------|--------s2----------
 
3622
                 */
 
3623
                rspTokenSize = 1 +
 
3624
                        gssint_der_length_size(data->length) +
 
3625
                        data->length;
 
3626
                dataLen += 1 + gssint_der_length_size(rspTokenSize) +
 
3627
                        rspTokenSize;
 
3628
        }
 
3629
 
 
3630
        if (mechListMIC) {
 
3631
                /*
 
3632
                 * Encoded in final output as:
 
3633
                 * 0xa3 [DER LEN] 0x04 [DER LEN] [DATA]
 
3634
                 *      --s--     -----tlen------------
 
3635
                 */
 
3636
                micTokenSize = 1 +
 
3637
                        gssint_der_length_size(mechListMIC->length) +
 
3638
                        mechListMIC->length;
 
3639
                dataLen += 1 +
 
3640
                        gssint_der_length_size(micTokenSize) +
 
3641
                        micTokenSize;
 
3642
        }
 
3643
 
 
3644
        /*
 
3645
         * Add size of DER encoding
 
3646
         * [ SEQUENCE { MechTypeList | ReqFLags | Token | mechListMIC } ]
 
3647
         *   0x30 [DER_LEN] [data]
 
3648
         *
 
3649
         */
 
3650
        negTokenInitContSize = dataLen;
 
3651
        negTokenInitSeqSize = 1 + gssint_der_length_size(dataLen) + dataLen;
 
3652
        dataLen = negTokenInitSeqSize;
 
3653
 
 
3654
        /*
 
3655
         * negTokenInitSize indicates the bytes needed to
 
3656
         * hold the ASN.1 encoding of the entire NegTokenInit
 
3657
         * SEQUENCE.
 
3658
         * 0xa0 [DER_LEN] + data
 
3659
         *
 
3660
         */
 
3661
        negTokenInitSize = 1 +
 
3662
                gssint_der_length_size(negTokenInitSeqSize) +
 
3663
                negTokenInitSeqSize;
 
3664
 
 
3665
        tlen = g_token_size(gss_mech_spnego, negTokenInitSize);
 
3666
 
 
3667
        t = (unsigned char *) gssalloc_malloc(tlen);
 
3668
 
 
3669
        if (t == NULL) {
 
3670
                return (-1);
 
3671
        }
 
3672
 
 
3673
        ptr = t;
 
3674
 
 
3675
        /* create the message */
 
3676
        if ((ret = g_make_token_header(gss_mech_spnego, negTokenInitSize,
 
3677
                            &ptr, tlen)))
 
3678
                goto errout;
 
3679
 
 
3680
        *ptr++ = CONTEXT; /* NegotiationToken identifier */
 
3681
        if ((ret = gssint_put_der_length(negTokenInitSeqSize, &ptr, tlen)))
 
3682
                goto errout;
 
3683
 
 
3684
        *ptr++ = SEQUENCE;
 
3685
        if ((ret = gssint_put_der_length(negTokenInitContSize, &ptr,
 
3686
                                         tlen - (int)(ptr-t))))
 
3687
                goto errout;
 
3688
 
 
3689
        *ptr++ = CONTEXT | 0x00; /* MechTypeList identifier */
 
3690
        if ((ret = gssint_put_der_length(spnego_ctx->DER_mechTypes.length,
 
3691
                                         &ptr, tlen - (int)(ptr-t))))
 
3692
                goto errout;
 
3693
 
 
3694
        /* We already encoded the MechSetList */
 
3695
        (void) memcpy(ptr, spnego_ctx->DER_mechTypes.value,
 
3696
                      spnego_ctx->DER_mechTypes.length);
 
3697
 
 
3698
        ptr += spnego_ctx->DER_mechTypes.length;
 
3699
 
 
3700
        if (data != NULL) {
 
3701
                *ptr++ = CONTEXT | 0x02;
 
3702
                if ((ret = gssint_put_der_length(rspTokenSize,
 
3703
                                &ptr, tlen - (int)(ptr - t))))
 
3704
                        goto errout;
 
3705
 
 
3706
                if ((ret = put_input_token(&ptr, data,
 
3707
                        tlen - (int)(ptr - t))))
 
3708
                        goto errout;
 
3709
        }
 
3710
 
 
3711
        if (mechListMIC != GSS_C_NO_BUFFER) {
 
3712
                *ptr++ = CONTEXT | 0x03;
 
3713
                if ((ret = gssint_put_der_length(micTokenSize,
 
3714
                                &ptr, tlen - (int)(ptr - t))))
 
3715
                        goto errout;
 
3716
 
 
3717
                if (negHintsCompat) {
 
3718
                        ret = put_neg_hints(&ptr, mechListMIC,
 
3719
                                            tlen - (int)(ptr - t));
 
3720
                        if (ret)
 
3721
                                goto errout;
 
3722
                } else if ((ret = put_input_token(&ptr, mechListMIC,
 
3723
                                tlen - (int)(ptr - t))))
 
3724
                        goto errout;
 
3725
        }
 
3726
 
 
3727
errout:
 
3728
        if (ret != 0) {
 
3729
                if (t)
 
3730
                        free(t);
 
3731
                t = NULL;
 
3732
                tlen = 0;
 
3733
        }
 
3734
        outbuf->length = tlen;
 
3735
        outbuf->value = (void *) t;
 
3736
 
 
3737
        return (ret);
 
3738
}
 
3739
 
 
3740
/*
 
3741
 * create the server side spnego token passed back to
 
3742
 * gss_accept_sec_context and eventually up to the application program
 
3743
 * and over to the client.
 
3744
 */
 
3745
static int
 
3746
make_spnego_tokenTarg_msg(OM_uint32 status, gss_OID mech_wanted,
 
3747
                          gss_buffer_t data, gss_buffer_t mechListMIC,
 
3748
                          send_token_flag sendtoken,
 
3749
                          gss_buffer_t outbuf)
 
3750
{
 
3751
        unsigned int tlen = 0;
 
3752
        unsigned int ret = 0;
 
3753
        unsigned int NegTokenTargSize = 0;
 
3754
        unsigned int NegTokenSize = 0;
 
3755
        unsigned int rspTokenSize = 0;
 
3756
        unsigned int micTokenSize = 0;
 
3757
        unsigned int dataLen = 0;
 
3758
        unsigned char *t;
 
3759
        unsigned char *ptr;
 
3760
 
 
3761
        if (outbuf == GSS_C_NO_BUFFER)
 
3762
                return (GSS_S_DEFECTIVE_TOKEN);
 
3763
        if (sendtoken == INIT_TOKEN_SEND && mech_wanted == GSS_C_NO_OID)
 
3764
            return (GSS_S_DEFECTIVE_TOKEN);
 
3765
 
 
3766
        outbuf->length = 0;
 
3767
        outbuf->value = NULL;
 
3768
 
 
3769
        /*
 
3770
         * ASN.1 encoding of the negResult
 
3771
         * ENUMERATED type is 3 bytes
 
3772
         *  ENUMERATED TAG, Length, Value,
 
3773
         * Plus 2 bytes for the CONTEXT id and length.
 
3774
         */
 
3775
        dataLen = 5;
 
3776
 
 
3777
        /*
 
3778
         * calculate data length
 
3779
         *
 
3780
         * If this is the initial token, include length of
 
3781
         * mech_type and the negotiation result fields.
 
3782
         */
 
3783
        if (sendtoken == INIT_TOKEN_SEND) {
 
3784
                int mechlistTokenSize;
 
3785
                /*
 
3786
                 * 1 byte for the CONTEXT ID(0xa0),
 
3787
                 * 1 byte for the OID ID(0x06)
 
3788
                 * 1 byte for OID Length field
 
3789
                 * Plus the rest... (OID Length, OID value)
 
3790
                 */
 
3791
                mechlistTokenSize = 3 + mech_wanted->length +
 
3792
                        gssint_der_length_size(mech_wanted->length);
 
3793
 
 
3794
                dataLen += mechlistTokenSize;
 
3795
        }
 
3796
        if (data != NULL && data->length > 0) {
 
3797
                /* Length of the inner token */
 
3798
                rspTokenSize = 1 + gssint_der_length_size(data->length) +
 
3799
                        data->length;
 
3800
 
 
3801
                dataLen += rspTokenSize;
 
3802
 
 
3803
                /* Length of the outer token */
 
3804
                dataLen += 1 + gssint_der_length_size(rspTokenSize);
 
3805
        }
 
3806
        if (mechListMIC != NULL) {
 
3807
 
 
3808
                /* Length of the inner token */
 
3809
                micTokenSize = 1 + gssint_der_length_size(mechListMIC->length) +
 
3810
                        mechListMIC->length;
 
3811
 
 
3812
                dataLen += micTokenSize;
 
3813
 
 
3814
                /* Length of the outer token */
 
3815
                dataLen += 1 + gssint_der_length_size(micTokenSize);
 
3816
        }
 
3817
        /*
 
3818
         * Add size of DER encoded:
 
3819
         * NegTokenTarg [ SEQUENCE ] of
 
3820
         *    NegResult[0] ENUMERATED {
 
3821
         *      accept_completed(0),
 
3822
         *      accept_incomplete(1),
 
3823
         *      reject(2) }
 
3824
         *    supportedMech [1] MechType OPTIONAL,
 
3825
         *    responseToken [2] OCTET STRING OPTIONAL,
 
3826
         *    mechListMIC   [3] OCTET STRING OPTIONAL
 
3827
         *
 
3828
         * size = data->length + MechListMic + SupportedMech len +
 
3829
         *      Result Length + ASN.1 overhead
 
3830
         */
 
3831
        NegTokenTargSize = dataLen;
 
3832
        dataLen += 1 + gssint_der_length_size(NegTokenTargSize);
 
3833
 
 
3834
        /*
 
3835
         * NegotiationToken [ CHOICE ]{
 
3836
         *    negTokenInit  [0]  NegTokenInit,
 
3837
         *    negTokenTarg  [1]  NegTokenTarg }
 
3838
         */
 
3839
        NegTokenSize = dataLen;
 
3840
        dataLen += 1 + gssint_der_length_size(NegTokenSize);
 
3841
 
 
3842
        tlen = dataLen;
 
3843
        t = (unsigned char *) gssalloc_malloc(tlen);
 
3844
 
 
3845
        if (t == NULL) {
 
3846
                ret = GSS_S_DEFECTIVE_TOKEN;
 
3847
                goto errout;
 
3848
        }
 
3849
 
 
3850
        ptr = t;
 
3851
 
 
3852
        /*
 
3853
         * Indicate that we are sending CHOICE 1
 
3854
         * (NegTokenTarg)
 
3855
         */
 
3856
        *ptr++ = CONTEXT | 0x01;
 
3857
        if (gssint_put_der_length(NegTokenSize, &ptr, dataLen) < 0) {
 
3858
                ret = GSS_S_DEFECTIVE_TOKEN;
 
3859
                goto errout;
 
3860
        }
 
3861
        *ptr++ = SEQUENCE;
 
3862
        if (gssint_put_der_length(NegTokenTargSize, &ptr,
 
3863
                                  tlen - (int)(ptr-t)) < 0) {
 
3864
                ret = GSS_S_DEFECTIVE_TOKEN;
 
3865
                goto errout;
 
3866
        }
 
3867
 
 
3868
        /*
 
3869
         * First field of the NegTokenTarg SEQUENCE
 
3870
         * is the ENUMERATED NegResult.
 
3871
         */
 
3872
        *ptr++ = CONTEXT;
 
3873
        if (gssint_put_der_length(3, &ptr,
 
3874
                                  tlen - (int)(ptr-t)) < 0) {
 
3875
                ret = GSS_S_DEFECTIVE_TOKEN;
 
3876
                goto errout;
 
3877
        }
 
3878
        if (put_negResult(&ptr, status, tlen - (int)(ptr - t)) < 0) {
 
3879
                ret = GSS_S_DEFECTIVE_TOKEN;
 
3880
                goto errout;
 
3881
        }
 
3882
        if (sendtoken == INIT_TOKEN_SEND) {
 
3883
                /*
 
3884
                 * Next, is the Supported MechType
 
3885
                 */
 
3886
                *ptr++ = CONTEXT | 0x01;
 
3887
                if (gssint_put_der_length(mech_wanted->length + 2,
 
3888
                                          &ptr,
 
3889
                                          tlen - (int)(ptr - t)) < 0) {
 
3890
                        ret = GSS_S_DEFECTIVE_TOKEN;
 
3891
                        goto errout;
 
3892
                }
 
3893
                if (put_mech_oid(&ptr, mech_wanted,
 
3894
                                 tlen - (int)(ptr - t)) < 0) {
 
3895
                        ret = GSS_S_DEFECTIVE_TOKEN;
 
3896
                        goto errout;
 
3897
                }
 
3898
        }
 
3899
        if (data != NULL && data->length > 0) {
 
3900
                *ptr++ = CONTEXT | 0x02;
 
3901
                if (gssint_put_der_length(rspTokenSize, &ptr,
 
3902
                                          tlen - (int)(ptr - t)) < 0) {
 
3903
                        ret = GSS_S_DEFECTIVE_TOKEN;
 
3904
                        goto errout;
 
3905
                }
 
3906
                if (put_input_token(&ptr, data,
 
3907
                                    tlen - (int)(ptr - t)) < 0) {
 
3908
                        ret = GSS_S_DEFECTIVE_TOKEN;
 
3909
                        goto errout;
 
3910
                }
 
3911
        }
 
3912
        if (mechListMIC != NULL) {
 
3913
                *ptr++ = CONTEXT | 0x03;
 
3914
                if (gssint_put_der_length(micTokenSize, &ptr,
 
3915
                                          tlen - (int)(ptr - t)) < 0) {
 
3916
                        ret = GSS_S_DEFECTIVE_TOKEN;
 
3917
                        goto errout;
 
3918
                }
 
3919
                if (put_input_token(&ptr, mechListMIC,
 
3920
                                    tlen - (int)(ptr - t)) < 0) {
 
3921
                        ret = GSS_S_DEFECTIVE_TOKEN;
 
3922
                        goto errout;
 
3923
                }
 
3924
        }
 
3925
        ret = GSS_S_COMPLETE;
 
3926
errout:
 
3927
        if (ret != GSS_S_COMPLETE) {
 
3928
                if (t)
 
3929
                        free(t);
 
3930
        } else {
 
3931
                outbuf->length = ptr - t;
 
3932
                outbuf->value = (void *) t;
 
3933
        }
 
3934
 
 
3935
        return (ret);
 
3936
}
 
3937
 
 
3938
/* determine size of token */
 
3939
static int
 
3940
g_token_size(gss_OID_const mech, unsigned int body_size)
 
3941
{
 
3942
        int hdrsize;
 
3943
 
 
3944
        /*
 
3945
         * Initialize the header size to the
 
3946
         * MECH_OID byte + the bytes needed to indicate the
 
3947
         * length of the OID + the OID itself.
 
3948
         *
 
3949
         * 0x06 [MECHLENFIELD] MECHDATA
 
3950
         */
 
3951
        hdrsize = 1 + gssint_der_length_size(mech->length) + mech->length;
 
3952
 
 
3953
        /*
 
3954
         * Now add the bytes needed for the initial header
 
3955
         * token bytes:
 
3956
         * 0x60 + [DER_LEN] + HDRSIZE
 
3957
         */
 
3958
        hdrsize += 1 + gssint_der_length_size(body_size + hdrsize);
 
3959
 
 
3960
        return (hdrsize + body_size);
 
3961
}
 
3962
 
 
3963
/*
 
3964
 * generate token header.
 
3965
 *
 
3966
 * Use DER Definite Length method per RFC2478
 
3967
 * Use of indefinite length encoding will not be compatible
 
3968
 * with Microsoft or others that actually follow the spec.
 
3969
 */
 
3970
static int
 
3971
g_make_token_header(gss_OID_const mech,
 
3972
                    unsigned int body_size,
 
3973
                    unsigned char **buf,
 
3974
                    unsigned int totallen)
 
3975
{
 
3976
        int ret = 0;
 
3977
        unsigned int hdrsize;
 
3978
        unsigned char *p = *buf;
 
3979
 
 
3980
        hdrsize = 1 + gssint_der_length_size(mech->length) + mech->length;
 
3981
 
 
3982
        *(*buf)++ = HEADER_ID;
 
3983
        if ((ret = gssint_put_der_length(hdrsize + body_size, buf, totallen)))
 
3984
                return (ret);
 
3985
 
 
3986
        *(*buf)++ = MECH_OID;
 
3987
        if ((ret = gssint_put_der_length(mech->length, buf,
 
3988
                            totallen - (int)(p - *buf))))
 
3989
                return (ret);
 
3990
        TWRITE_STR(*buf, mech->elements, mech->length);
 
3991
        return (0);
 
3992
}
 
3993
 
 
3994
/*
 
3995
 * NOTE: This checks that the length returned by
 
3996
 * gssint_get_der_length() is not greater than the number of octets
 
3997
 * remaining, even though gssint_get_der_length() already checks, in
 
3998
 * theory.
 
3999
 */
 
4000
static int
 
4001
g_get_tag_and_length(unsigned char **buf, int tag,
 
4002
                     unsigned int buflen, unsigned int *outlen)
 
4003
{
 
4004
        unsigned char *ptr = *buf;
 
4005
        int ret = -1; /* pessimists, assume failure ! */
 
4006
        unsigned int encoded_len;
 
4007
        int tmplen = 0;
 
4008
 
 
4009
        *outlen = 0;
 
4010
        if (buflen > 1 && *ptr == tag) {
 
4011
                ptr++;
 
4012
                tmplen = gssint_get_der_length(&ptr, buflen - 1,
 
4013
                                                &encoded_len);
 
4014
                if (tmplen < 0) {
 
4015
                        ret = -1;
 
4016
                } else if ((unsigned int)tmplen > buflen - (ptr - *buf)) {
 
4017
                        ret = -1;
 
4018
                } else
 
4019
                        ret = 0;
 
4020
        }
 
4021
        *outlen = tmplen;
 
4022
        *buf = ptr;
 
4023
        return (ret);
 
4024
}
 
4025
 
 
4026
static int
 
4027
g_verify_neg_token_init(unsigned char **buf_in, unsigned int cur_size)
 
4028
{
 
4029
        unsigned char *buf = *buf_in;
 
4030
        unsigned char *endptr = buf + cur_size;
 
4031
        int seqsize;
 
4032
        int ret = 0;
 
4033
        unsigned int bytes;
 
4034
 
 
4035
        /*
 
4036
         * Verify this is a NegotiationToken type token
 
4037
         * - check for a0(context specific identifier)
 
4038
         * - get length and verify that enoughd ata exists
 
4039
         */
 
4040
        if (g_get_tag_and_length(&buf, CONTEXT, cur_size, &bytes) < 0)
 
4041
                return (G_BAD_TOK_HEADER);
 
4042
 
 
4043
        cur_size = bytes; /* should indicate bytes remaining */
 
4044
 
 
4045
        /*
 
4046
         * Verify the next piece, it should identify this as
 
4047
         * a strucure of type NegTokenInit.
 
4048
         */
 
4049
        if (*buf++ == SEQUENCE) {
 
4050
                if ((seqsize = gssint_get_der_length(&buf, cur_size, &bytes)) < 0)
 
4051
                        return (G_BAD_TOK_HEADER);
 
4052
                /*
 
4053
                 * Make sure we have the entire buffer as described
 
4054
                 */
 
4055
                if (seqsize > endptr - buf)
 
4056
                        return (G_BAD_TOK_HEADER);
 
4057
        } else {
 
4058
                return (G_BAD_TOK_HEADER);
 
4059
        }
 
4060
 
 
4061
        cur_size = seqsize; /* should indicate bytes remaining */
 
4062
 
 
4063
        /*
 
4064
         * Verify that the first blob is a sequence of mechTypes
 
4065
         */
 
4066
        if (*buf++ == CONTEXT) {
 
4067
                if ((seqsize = gssint_get_der_length(&buf, cur_size, &bytes)) < 0)
 
4068
                        return (G_BAD_TOK_HEADER);
 
4069
                /*
 
4070
                 * Make sure we have the entire buffer as described
 
4071
                 */
 
4072
                if (seqsize > endptr - buf)
 
4073
                        return (G_BAD_TOK_HEADER);
 
4074
        } else {
 
4075
                return (G_BAD_TOK_HEADER);
 
4076
        }
 
4077
 
 
4078
        /*
 
4079
         * At this point, *buf should be at the beginning of the
 
4080
         * DER encoded list of mech types that are to be negotiated.
 
4081
         */
 
4082
        *buf_in = buf;
 
4083
 
 
4084
        return (ret);
 
4085
 
 
4086
}
 
4087
 
 
4088
/* verify token header. */
 
4089
static int
 
4090
g_verify_token_header(gss_OID_const mech,
 
4091
                    unsigned int *body_size,
 
4092
                    unsigned char **buf_in,
 
4093
                    int tok_type,
 
4094
                    unsigned int toksize)
 
4095
{
 
4096
        unsigned char *buf = *buf_in;
 
4097
        int seqsize;
 
4098
        gss_OID_desc toid;
 
4099
        int ret = 0;
 
4100
        unsigned int bytes;
 
4101
 
 
4102
        if (toksize-- < 1)
 
4103
                return (G_BAD_TOK_HEADER);
 
4104
 
 
4105
        if (*buf++ != HEADER_ID)
 
4106
                return (G_BAD_TOK_HEADER);
 
4107
 
 
4108
        if ((seqsize = gssint_get_der_length(&buf, toksize, &bytes)) < 0)
 
4109
                return (G_BAD_TOK_HEADER);
 
4110
 
 
4111
        if ((seqsize + bytes) != toksize)
 
4112
                return (G_BAD_TOK_HEADER);
 
4113
 
 
4114
        if (toksize-- < 1)
 
4115
                return (G_BAD_TOK_HEADER);
 
4116
 
 
4117
 
 
4118
        if (*buf++ != MECH_OID)
 
4119
                return (G_BAD_TOK_HEADER);
 
4120
 
 
4121
        if (toksize-- < 1)
 
4122
                return (G_BAD_TOK_HEADER);
 
4123
 
 
4124
        toid.length = *buf++;
 
4125
 
 
4126
        if (toksize < toid.length)
 
4127
                return (G_BAD_TOK_HEADER);
 
4128
        else
 
4129
                toksize -= toid.length;
 
4130
 
 
4131
        toid.elements = buf;
 
4132
        buf += toid.length;
 
4133
 
 
4134
        if (!g_OID_equal(&toid, mech))
 
4135
                ret = G_WRONG_MECH;
 
4136
 
 
4137
        /*
 
4138
         * G_WRONG_MECH is not returned immediately because it's more important
 
4139
         * to return G_BAD_TOK_HEADER if the token header is in fact bad
 
4140
         */
 
4141
        if (toksize < 2)
 
4142
                return (G_BAD_TOK_HEADER);
 
4143
        else
 
4144
                toksize -= 2;
 
4145
 
 
4146
        if (!ret) {
 
4147
                *buf_in = buf;
 
4148
                *body_size = toksize;
 
4149
        }
 
4150
 
 
4151
        return (ret);
 
4152
}
 
4153
 
 
4154
/*
 
4155
 * Return non-zero if the oid is one of the kerberos mech oids,
 
4156
 * otherwise return zero.
 
4157
 *
 
4158
 * N.B. There are 3 oids that represent the kerberos mech:
 
4159
 * RFC-specified GSS_MECH_KRB5_OID,
 
4160
 * Old pre-RFC   GSS_MECH_KRB5_OLD_OID,
 
4161
 * Incorrect MS  GSS_MECH_KRB5_WRONG_OID
 
4162
 */
 
4163
 
 
4164
static int
 
4165
is_kerb_mech(gss_OID oid)
 
4166
{
 
4167
        int answer = 0;
 
4168
        OM_uint32 minor;
 
4169
        extern const gss_OID_set_desc * const gss_mech_set_krb5_both;
 
4170
 
 
4171
        (void) gss_test_oid_set_member(&minor,
 
4172
                oid, (gss_OID_set)gss_mech_set_krb5_both, &answer);
 
4173
 
 
4174
        return (answer);
 
4175
}