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