2
* Copyright (C) 2006,2008 by the Massachusetts Institute of Technology.
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.
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.
25
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
26
* Use is subject to license terms.
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.
34
* Copyright (c) 2006-2008, Novell, Inc.
35
* All rights reserved.
37
* Redistribution and use in source and binary forms, with or without
38
* modification, are permitted provided that the following conditions are met:
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.
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.
60
/* #pragma ident "@(#)spnego_mech.c 1.7 04/09/28 SMI" */
69
#include "gssapiP_spnego.h"
70
#include <gssapi_err_generic.h>
72
#ifndef MAXHOSTNAMELEN
73
#define MAXHOSTNAMELEN 64
77
#undef g_verify_token_header
78
#undef g_make_token_header
80
#define HARD_ERROR(v) ((v) != GSS_S_COMPLETE && (v) != GSS_S_CONTINUE_NEEDED)
81
typedef const gss_OID_desc *gss_OID_const;
83
/* der routines defined in libgss */
84
extern unsigned int gssint_der_length_size(unsigned int);
85
extern int gssint_get_der_length(unsigned char **, unsigned int,
87
extern int gssint_put_der_length(unsigned int, unsigned char **, unsigned int);
90
/* private routines for spnego_mechanism */
91
static spnego_token_t make_spnego_token(char *);
92
static gss_buffer_desc make_err_msg(char *);
93
static int g_token_size(gss_OID_const, unsigned int);
94
static int g_make_token_header(gss_OID_const, unsigned int,
95
unsigned char **, unsigned int);
96
static int g_verify_token_header(gss_OID_const, unsigned int *,
99
static int g_verify_neg_token_init(unsigned char **, unsigned int);
100
static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);
101
static gss_buffer_t get_input_token(unsigned char **, unsigned int);
102
static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);
103
static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);
104
static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t,
105
gss_const_key_value_set_t,
106
gss_cred_id_t *, gss_OID_set *);
107
static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_cred_id_t,
108
gss_cred_usage_t, gss_OID_set *);
109
static void release_spnego_ctx(spnego_gss_ctx_id_t *);
110
static void check_spnego_options(spnego_gss_ctx_id_t);
111
static spnego_gss_ctx_id_t create_spnego_ctx(void);
112
static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);
113
static int put_input_token(unsigned char **, gss_buffer_t, unsigned int);
114
static int put_mech_oid(unsigned char **, gss_OID_const, unsigned int);
115
static int put_negResult(unsigned char **, OM_uint32, unsigned int);
118
process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
119
gss_buffer_t *, OM_uint32 *, send_token_flag *);
121
handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
122
gss_buffer_t *, OM_uint32 *, send_token_flag *);
125
init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, gss_ctx_id_t *,
128
init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
129
gss_buffer_t *, gss_buffer_t *,
130
OM_uint32 *, send_token_flag *);
132
init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
133
gss_buffer_t *, gss_buffer_t *,
134
OM_uint32 *, send_token_flag *);
136
init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
137
gss_OID, gss_buffer_t *, gss_buffer_t *,
138
OM_uint32 *, send_token_flag *);
140
init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
141
gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
142
gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
143
OM_uint32 *, send_token_flag *);
146
acc_ctx_new(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
147
spnego_gss_cred_id_t, gss_buffer_t *,
148
gss_buffer_t *, OM_uint32 *, send_token_flag *);
150
acc_ctx_cont(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
151
gss_buffer_t *, gss_buffer_t *,
152
OM_uint32 *, send_token_flag *);
154
acc_ctx_vfy_oid(OM_uint32 *, spnego_gss_ctx_id_t, gss_OID,
155
OM_uint32 *, send_token_flag *);
157
acc_ctx_call_acc(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
158
gss_buffer_t, gss_OID *, gss_buffer_t,
159
OM_uint32 *, OM_uint32 *, gss_cred_id_t *,
160
OM_uint32 *, send_token_flag *);
163
negotiate_mech(gss_OID_set, gss_OID_set, OM_uint32 *);
165
g_get_tag_and_length(unsigned char **, int, unsigned int, unsigned int *);
168
make_spnego_tokenInit_msg(spnego_gss_ctx_id_t,
171
OM_uint32, gss_buffer_t, send_token_flag,
174
make_spnego_tokenTarg_msg(OM_uint32, gss_OID, gss_buffer_t,
175
gss_buffer_t, send_token_flag,
179
get_negTokenInit(OM_uint32 *, gss_buffer_t, gss_buffer_t,
180
gss_OID_set *, OM_uint32 *, gss_buffer_t *,
183
get_negTokenResp(OM_uint32 *, unsigned char *, unsigned int,
184
OM_uint32 *, gss_OID *, gss_buffer_t *, gss_buffer_t *);
187
is_kerb_mech(gss_OID oid);
189
/* SPNEGO oid structure */
190
static const gss_OID_desc spnego_oids[] = {
191
{SPNEGO_OID_LENGTH, SPNEGO_OID},
194
const gss_OID_desc * const gss_mech_spnego = spnego_oids+0;
195
static const gss_OID_set_desc spnego_oidsets[] = {
196
{1, (gss_OID) spnego_oids+0},
198
const gss_OID_set_desc * const gss_mech_set_spnego = spnego_oidsets+0;
200
static int make_NegHints(OM_uint32 *, spnego_gss_cred_id_t, gss_buffer_t *);
201
static int put_neg_hints(unsigned char **, gss_buffer_t, unsigned int);
203
acc_ctx_hints(OM_uint32 *, gss_ctx_id_t *, spnego_gss_cred_id_t,
204
gss_buffer_t *, OM_uint32 *, send_token_flag *);
207
* The Mech OID for SPNEGO:
208
* { iso(1) org(3) dod(6) internet(1) security(5)
209
* mechanism(5) spnego(2) }
211
static struct gss_config spnego_mechanism =
213
{SPNEGO_OID_LENGTH, SPNEGO_OID},
215
spnego_gss_acquire_cred,
216
spnego_gss_release_cred,
217
spnego_gss_init_sec_context,
219
spnego_gss_accept_sec_context,
222
#endif /* LEAN_CLIENT */
223
NULL, /* gss_process_context_token */
224
spnego_gss_delete_sec_context, /* gss_delete_sec_context */
225
spnego_gss_context_time, /* gss_context_time */
226
spnego_gss_get_mic, /* gss_get_mic */
227
spnego_gss_verify_mic, /* gss_verify_mic */
228
spnego_gss_wrap, /* gss_wrap */
229
spnego_gss_unwrap, /* gss_unwrap */
230
spnego_gss_display_status,
231
NULL, /* gss_indicate_mechs */
232
spnego_gss_compare_name,
233
spnego_gss_display_name,
234
spnego_gss_import_name,
235
spnego_gss_release_name,
236
spnego_gss_inquire_cred, /* gss_inquire_cred */
237
NULL, /* gss_add_cred */
239
spnego_gss_export_sec_context, /* gss_export_sec_context */
240
spnego_gss_import_sec_context, /* gss_import_sec_context */
242
NULL, /* gss_export_sec_context */
243
NULL, /* gss_import_sec_context */
244
#endif /* LEAN_CLIENT */
245
NULL, /* gss_inquire_cred_by_mech */
246
spnego_gss_inquire_names_for_mech,
247
spnego_gss_inquire_context, /* gss_inquire_context */
248
NULL, /* gss_internal_release_oid */
249
spnego_gss_wrap_size_limit, /* gss_wrap_size_limit */
250
NULL, /* gssd_pname_to_uid */
251
NULL, /* gss_userok */
252
NULL, /* gss_export_name */
253
spnego_gss_duplicate_name, /* gss_duplicate_name */
254
NULL, /* gss_store_cred */
255
spnego_gss_inquire_sec_context_by_oid, /* gss_inquire_sec_context_by_oid */
256
spnego_gss_inquire_cred_by_oid, /* gss_inquire_cred_by_oid */
257
spnego_gss_set_sec_context_option, /* gss_set_sec_context_option */
258
spnego_gss_set_cred_option, /* gssspi_set_cred_option */
259
NULL, /* gssspi_mech_invoke */
260
spnego_gss_wrap_aead,
261
spnego_gss_unwrap_aead,
263
spnego_gss_unwrap_iov,
264
spnego_gss_wrap_iov_length,
265
spnego_gss_complete_auth_token,
266
spnego_gss_acquire_cred_impersonate_name,
267
NULL, /* gss_add_cred_impersonate_name */
268
spnego_gss_display_name_ext,
269
spnego_gss_inquire_name,
270
spnego_gss_get_name_attribute,
271
spnego_gss_set_name_attribute,
272
spnego_gss_delete_name_attribute,
273
spnego_gss_export_name_composite,
274
spnego_gss_map_name_to_any,
275
spnego_gss_release_any_name_mapping,
276
spnego_gss_pseudo_random,
277
spnego_gss_set_neg_mechs,
278
spnego_gss_inquire_saslname_for_mech,
279
spnego_gss_inquire_mech_for_saslname,
280
spnego_gss_inquire_attrs_for_mech,
281
spnego_gss_acquire_cred_from,
282
NULL, /* gss_store_cred_into */
283
spnego_gss_acquire_cred_with_password,
284
spnego_gss_export_cred,
285
spnego_gss_import_cred,
286
NULL, /* gssspi_import_sec_context_by_mech */
287
NULL, /* gssspi_import_name_by_mech */
288
NULL, /* gssspi_import_cred_by_mech */
289
spnego_gss_get_mic_iov,
290
spnego_gss_verify_mic_iov,
291
spnego_gss_get_mic_iov_length
294
#ifdef _GSS_STATIC_LINK
297
static int gss_spnegomechglue_init(void)
299
struct gss_mech_config mech_spnego;
301
memset(&mech_spnego, 0, sizeof(mech_spnego));
302
mech_spnego.mech = &spnego_mechanism;
303
mech_spnego.mechNameStr = "spnego";
304
mech_spnego.mech_type = GSS_C_NO_OID;
306
return gssint_register_mechinfo(&mech_spnego);
309
gss_mechanism KRB5_CALLCONV
310
gss_mech_initialize(void)
312
return (&spnego_mechanism);
315
MAKE_INIT_FUNCTION(gss_krb5int_lib_init);
316
MAKE_FINI_FUNCTION(gss_krb5int_lib_fini);
317
int gss_krb5int_lib_init(void);
318
#endif /* _GSS_STATIC_LINK */
320
int gss_spnegoint_lib_init(void)
322
#ifdef _GSS_STATIC_LINK
323
return gss_spnegomechglue_init();
329
void gss_spnegoint_lib_fini(void)
334
OM_uint32 KRB5_CALLCONV
335
spnego_gss_acquire_cred(OM_uint32 *minor_status,
336
gss_name_t desired_name,
338
gss_OID_set desired_mechs,
339
gss_cred_usage_t cred_usage,
340
gss_cred_id_t *output_cred_handle,
341
gss_OID_set *actual_mechs,
344
return spnego_gss_acquire_cred_from(minor_status, desired_name, time_req,
345
desired_mechs, cred_usage, NULL,
346
output_cred_handle, actual_mechs,
351
OM_uint32 KRB5_CALLCONV
352
spnego_gss_acquire_cred_from(OM_uint32 *minor_status,
353
const gss_name_t desired_name,
355
const gss_OID_set desired_mechs,
356
gss_cred_usage_t cred_usage,
357
gss_const_key_value_set_t cred_store,
358
gss_cred_id_t *output_cred_handle,
359
gss_OID_set *actual_mechs,
362
OM_uint32 status, tmpmin;
364
gss_cred_id_t mcred = NULL;
365
spnego_gss_cred_id_t spcred = NULL;
366
dsyslog("Entering spnego_gss_acquire_cred\n");
369
*actual_mechs = NULL;
374
/* We will obtain a mechglue credential and wrap it in a
375
* spnego_gss_cred_id_rec structure. Allocate the wrapper. */
376
spcred = malloc(sizeof(spnego_gss_cred_id_rec));
377
if (spcred == NULL) {
378
*minor_status = ENOMEM;
379
return (GSS_S_FAILURE);
381
spcred->neg_mechs = GSS_C_NULL_OID_SET;
384
* Always use get_available_mechs to collect a list of
385
* mechs for which creds are available.
387
status = get_available_mechs(minor_status, desired_name,
388
cred_usage, cred_store, &mcred,
391
if (actual_mechs && amechs != GSS_C_NULL_OID_SET) {
392
(void) gssint_copy_oid_set(&tmpmin, amechs, actual_mechs);
394
(void) gss_release_oid_set(&tmpmin, &amechs);
396
if (status == GSS_S_COMPLETE) {
397
spcred->mcred = mcred;
398
*output_cred_handle = (gss_cred_id_t)spcred;
401
*output_cred_handle = GSS_C_NO_CREDENTIAL;
404
dsyslog("Leaving spnego_gss_acquire_cred\n");
409
OM_uint32 KRB5_CALLCONV
410
spnego_gss_release_cred(OM_uint32 *minor_status,
411
gss_cred_id_t *cred_handle)
413
spnego_gss_cred_id_t spcred = NULL;
415
dsyslog("Entering spnego_gss_release_cred\n");
417
if (minor_status == NULL || cred_handle == NULL)
418
return (GSS_S_CALL_INACCESSIBLE_WRITE);
422
if (*cred_handle == GSS_C_NO_CREDENTIAL)
423
return (GSS_S_COMPLETE);
425
spcred = (spnego_gss_cred_id_t)*cred_handle;
426
*cred_handle = GSS_C_NO_CREDENTIAL;
427
gss_release_oid_set(minor_status, &spcred->neg_mechs);
428
gss_release_cred(minor_status, &spcred->mcred);
431
dsyslog("Leaving spnego_gss_release_cred\n");
432
return (GSS_S_COMPLETE);
436
check_spnego_options(spnego_gss_ctx_id_t spnego_ctx)
438
spnego_ctx->optionStr = gssint_get_modOptions(
439
(const gss_OID)&spnego_oids[0]);
442
static spnego_gss_ctx_id_t
443
create_spnego_ctx(void)
445
spnego_gss_ctx_id_t spnego_ctx = NULL;
446
spnego_ctx = (spnego_gss_ctx_id_t)
447
malloc(sizeof (spnego_gss_ctx_id_rec));
449
if (spnego_ctx == NULL) {
453
spnego_ctx->magic_num = SPNEGO_MAGIC_ID;
454
spnego_ctx->ctx_handle = GSS_C_NO_CONTEXT;
455
spnego_ctx->mech_set = NULL;
456
spnego_ctx->internal_mech = NULL;
457
spnego_ctx->optionStr = NULL;
458
spnego_ctx->DER_mechTypes.length = 0;
459
spnego_ctx->DER_mechTypes.value = NULL;
460
spnego_ctx->default_cred = GSS_C_NO_CREDENTIAL;
461
spnego_ctx->mic_reqd = 0;
462
spnego_ctx->mic_sent = 0;
463
spnego_ctx->mic_rcvd = 0;
464
spnego_ctx->mech_complete = 0;
465
spnego_ctx->nego_done = 0;
466
spnego_ctx->internal_name = GSS_C_NO_NAME;
467
spnego_ctx->actual_mech = GSS_C_NO_OID;
469
check_spnego_options(spnego_ctx);
475
* Both initiator and acceptor call here to verify and/or create mechListMIC,
476
* and to consistency-check the MIC state. handle_mic is invoked only if the
477
* negotiated mech has completed and supports MICs.
480
handle_mic(OM_uint32 *minor_status, gss_buffer_t mic_in,
481
int send_mechtok, spnego_gss_ctx_id_t sc,
482
gss_buffer_t *mic_out,
483
OM_uint32 *negState, send_token_flag *tokflag)
488
*mic_out = GSS_C_NO_BUFFER;
489
if (mic_in != GSS_C_NO_BUFFER) {
491
/* Reject MIC if we've already received a MIC. */
493
*tokflag = ERROR_TOKEN_SEND;
494
return GSS_S_DEFECTIVE_TOKEN;
496
} else if (sc->mic_reqd && !send_mechtok) {
498
* If the peer sends the final mechanism token, it
499
* must send the MIC with that token if the
500
* negotiation requires MICs.
503
*tokflag = ERROR_TOKEN_SEND;
504
return GSS_S_DEFECTIVE_TOKEN;
506
ret = process_mic(minor_status, mic_in, sc, mic_out,
508
if (ret != GSS_S_COMPLETE) {
512
assert(sc->mic_sent || sc->mic_rcvd);
514
if (sc->mic_sent && sc->mic_rcvd) {
515
ret = GSS_S_COMPLETE;
516
*negState = ACCEPT_COMPLETE;
517
if (*mic_out == GSS_C_NO_BUFFER) {
519
* We sent a MIC on the previous pass; we
520
* shouldn't be sending a mechanism token.
522
assert(!send_mechtok);
523
*tokflag = NO_TOKEN_SEND;
525
*tokflag = CONT_TOKEN_SEND;
527
} else if (sc->mic_reqd) {
528
*negState = ACCEPT_INCOMPLETE;
529
ret = GSS_S_CONTINUE_NEEDED;
530
} else if (*negState == ACCEPT_COMPLETE) {
531
ret = GSS_S_COMPLETE;
533
ret = GSS_S_CONTINUE_NEEDED;
539
* Perform the actual verification and/or generation of mechListMIC.
542
process_mic(OM_uint32 *minor_status, gss_buffer_t mic_in,
543
spnego_gss_ctx_id_t sc, gss_buffer_t *mic_out,
544
OM_uint32 *negState, send_token_flag *tokflag)
546
OM_uint32 ret, tmpmin;
548
gss_buffer_desc tmpmic = GSS_C_EMPTY_BUFFER;
551
if (mic_in != GSS_C_NO_BUFFER) {
552
ret = gss_verify_mic(minor_status, sc->ctx_handle,
555
if (ret != GSS_S_COMPLETE) {
557
*tokflag = ERROR_TOKEN_SEND;
560
/* If we got a MIC, we must send a MIC. */
564
if (sc->mic_reqd && !sc->mic_sent) {
565
ret = gss_get_mic(minor_status, sc->ctx_handle,
569
if (ret != GSS_S_COMPLETE) {
570
gss_release_buffer(&tmpmin, &tmpmic);
571
*tokflag = NO_TOKEN_SEND;
574
*mic_out = malloc(sizeof(gss_buffer_desc));
575
if (*mic_out == GSS_C_NO_BUFFER) {
576
gss_release_buffer(&tmpmin, &tmpmic);
577
*tokflag = NO_TOKEN_SEND;
578
return GSS_S_FAILURE;
583
return GSS_S_COMPLETE;
587
* Initial call to spnego_gss_init_sec_context().
590
init_ctx_new(OM_uint32 *minor_status,
591
spnego_gss_cred_id_t spcred,
593
send_token_flag *tokflag)
596
spnego_gss_ctx_id_t sc = NULL;
598
sc = create_spnego_ctx();
600
return GSS_S_FAILURE;
602
/* determine negotiation mech set */
603
ret = get_negotiable_mechs(minor_status, spcred, GSS_C_INITIATE,
605
if (ret != GSS_S_COMPLETE)
608
/* Set an initial internal mech to make the first context token. */
609
sc->internal_mech = &sc->mech_set->elements[0];
611
if (put_mech_set(sc->mech_set, &sc->DER_mechTypes) < 0) {
616
* The actual context is not yet determined, set the output
617
* context handle to refer to the spnego context itself.
619
sc->ctx_handle = GSS_C_NO_CONTEXT;
620
*ctx = (gss_ctx_id_t)sc;
622
*tokflag = INIT_TOKEN_SEND;
623
ret = GSS_S_CONTINUE_NEEDED;
626
release_spnego_ctx(&sc);
631
* Called by second and later calls to spnego_gss_init_sec_context()
632
* to decode reply and update state.
635
init_ctx_cont(OM_uint32 *minor_status, gss_ctx_id_t *ctx, gss_buffer_t buf,
636
gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
637
OM_uint32 *negState, send_token_flag *tokflag)
639
OM_uint32 ret, tmpmin, acc_negState;
641
spnego_gss_ctx_id_t sc;
642
gss_OID supportedMech = GSS_C_NO_OID;
644
sc = (spnego_gss_ctx_id_t)*ctx;
646
*tokflag = ERROR_TOKEN_SEND;
649
ret = get_negTokenResp(minor_status, ptr, buf->length,
650
&acc_negState, &supportedMech,
651
responseToken, mechListMIC);
652
if (ret != GSS_S_COMPLETE)
654
if (acc_negState == ACCEPT_DEFECTIVE_TOKEN &&
655
supportedMech == GSS_C_NO_OID &&
656
*responseToken == GSS_C_NO_BUFFER &&
657
*mechListMIC == GSS_C_NO_BUFFER) {
658
/* Reject "empty" token. */
659
ret = GSS_S_DEFECTIVE_TOKEN;
661
if (acc_negState == REJECT) {
662
*minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
663
map_errcode(minor_status);
664
*tokflag = NO_TOKEN_SEND;
669
* nego_done is false for the first call to init_ctx_cont()
671
if (!sc->nego_done) {
672
ret = init_ctx_nego(minor_status, sc,
674
supportedMech, responseToken,
677
} else if ((!sc->mech_complete && *responseToken == GSS_C_NO_BUFFER) ||
678
(sc->mech_complete && *responseToken != GSS_C_NO_BUFFER)) {
679
/* Missing or spurious token from acceptor. */
680
ret = GSS_S_DEFECTIVE_TOKEN;
681
} else if (!sc->mech_complete ||
683
(sc->ctx_flags & GSS_C_INTEG_FLAG))) {
684
/* Not obviously done; we may decide we're done later in
685
* init_ctx_call_init or handle_mic. */
686
*negState = ACCEPT_INCOMPLETE;
687
*tokflag = CONT_TOKEN_SEND;
688
ret = GSS_S_CONTINUE_NEEDED;
690
/* mech finished on last pass and no MIC required, so done. */
691
*negState = ACCEPT_COMPLETE;
692
*tokflag = NO_TOKEN_SEND;
693
ret = GSS_S_COMPLETE;
696
if (supportedMech != GSS_C_NO_OID)
697
generic_gss_release_oid(&tmpmin, &supportedMech);
702
* Consistency checking and mechanism negotiation handling for second
703
* call of spnego_gss_init_sec_context(). Call init_ctx_reselect() to
704
* update internal state if acceptor has counter-proposed.
707
init_ctx_nego(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
708
OM_uint32 acc_negState, gss_OID supportedMech,
709
gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
710
OM_uint32 *negState, send_token_flag *tokflag)
715
*tokflag = ERROR_TOKEN_SEND;
716
ret = GSS_S_DEFECTIVE_TOKEN;
718
* Both supportedMech and negState must be present in first
721
if (supportedMech == GSS_C_NO_OID) {
722
*minor_status = ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR;
723
map_errcode(minor_status);
724
return GSS_S_DEFECTIVE_TOKEN;
726
if (acc_negState == ACCEPT_DEFECTIVE_TOKEN) {
727
*minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
728
map_errcode(minor_status);
729
return GSS_S_DEFECTIVE_TOKEN;
733
* If the mechanism we sent is not the mechanism returned from
734
* the server, we need to handle the server's counter
735
* proposal. There is a bug in SAMBA servers that always send
736
* the old Kerberos mech OID, even though we sent the new one.
737
* So we will treat all the Kerberos mech OIDS as the same.
739
if (!(is_kerb_mech(supportedMech) &&
740
is_kerb_mech(sc->internal_mech)) &&
741
!g_OID_equal(supportedMech, sc->internal_mech)) {
742
ret = init_ctx_reselect(minor_status, sc,
743
acc_negState, supportedMech,
744
responseToken, mechListMIC,
747
} else if (*responseToken == GSS_C_NO_BUFFER) {
748
if (sc->mech_complete) {
750
* Mech completed on first call to its
751
* init_sec_context(). Acceptor sends no mech
754
*negState = ACCEPT_COMPLETE;
755
*tokflag = NO_TOKEN_SEND;
756
ret = GSS_S_COMPLETE;
759
* Reject missing mech token when optimistic
762
*minor_status = ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR;
763
map_errcode(minor_status);
764
ret = GSS_S_DEFECTIVE_TOKEN;
766
} else if (sc->mech_complete) {
767
/* Reject spurious mech token. */
768
ret = GSS_S_DEFECTIVE_TOKEN;
770
*negState = ACCEPT_INCOMPLETE;
771
*tokflag = CONT_TOKEN_SEND;
772
ret = GSS_S_CONTINUE_NEEDED;
779
* Handle acceptor's counter-proposal of an alternative mechanism.
782
init_ctx_reselect(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
783
OM_uint32 acc_negState, gss_OID supportedMech,
784
gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
785
OM_uint32 *negState, send_token_flag *tokflag)
790
gss_delete_sec_context(&tmpmin, &sc->ctx_handle,
793
/* Find supportedMech in sc->mech_set. */
794
for (i = 0; i < sc->mech_set->count; i++) {
795
if (g_OID_equal(supportedMech, &sc->mech_set->elements[i]))
798
if (i == sc->mech_set->count)
799
return GSS_S_DEFECTIVE_TOKEN;
800
sc->internal_mech = &sc->mech_set->elements[i];
803
* Windows 2003 and earlier don't correctly send a
804
* negState of request-mic when counter-proposing a
805
* mechanism. They probably don't handle mechListMICs
808
if (acc_negState != REQUEST_MIC)
809
return GSS_S_DEFECTIVE_TOKEN;
811
sc->mech_complete = 0;
813
*negState = REQUEST_MIC;
814
*tokflag = CONT_TOKEN_SEND;
815
return GSS_S_CONTINUE_NEEDED;
819
* Wrap call to mechanism gss_init_sec_context() and update state
823
init_ctx_call_init(OM_uint32 *minor_status,
824
spnego_gss_ctx_id_t sc,
825
spnego_gss_cred_id_t spcred,
826
gss_name_t target_name,
829
gss_buffer_t mechtok_in,
830
gss_OID *actual_mech,
831
gss_buffer_t mechtok_out,
832
OM_uint32 *ret_flags,
835
send_token_flag *send_token)
837
OM_uint32 ret, tmpret, tmpmin;
840
mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
841
ret = gss_init_sec_context(minor_status,
846
(req_flags | GSS_C_INTEG_FLAG),
848
GSS_C_NO_CHANNEL_BINDINGS,
854
if (ret == GSS_S_COMPLETE) {
855
sc->mech_complete = 1;
856
if (ret_flags != NULL)
857
*ret_flags = sc->ctx_flags;
859
* Microsoft SPNEGO implementations expect an even number of
860
* token exchanges. So if we're sending a final token, ask for
861
* a zero-length token back from the server. Also ask for a
862
* token back if this is the first token or if a MIC exchange
865
if (*send_token == CONT_TOKEN_SEND &&
866
mechtok_out->length == 0 &&
868
!(sc->ctx_flags & GSS_C_INTEG_FLAG))) {
869
/* The exchange is complete. */
870
*negState = ACCEPT_COMPLETE;
871
ret = GSS_S_COMPLETE;
872
*send_token = NO_TOKEN_SEND;
874
/* Ask for one more hop. */
875
*negState = ACCEPT_INCOMPLETE;
876
ret = GSS_S_CONTINUE_NEEDED;
881
if (ret == GSS_S_CONTINUE_NEEDED)
884
if (*send_token != INIT_TOKEN_SEND) {
885
*send_token = ERROR_TOKEN_SEND;
891
* Since this is the first token, we can fall back to later mechanisms
892
* in the list. Since the mechanism list is expected to be short, we
893
* can do this with recursion. If all mechanisms produce errors, the
894
* caller should get the error from the first mech in the list.
896
memmove(sc->mech_set->elements, sc->mech_set->elements + 1,
897
--sc->mech_set->count * sizeof(*sc->mech_set->elements));
898
if (sc->mech_set->count == 0)
900
gss_release_buffer(&tmpmin, &sc->DER_mechTypes);
901
if (put_mech_set(sc->mech_set, &sc->DER_mechTypes) < 0)
903
tmpret = init_ctx_call_init(&tmpmin, sc, spcred, target_name,
904
req_flags, time_req, mechtok_in,
905
actual_mech, mechtok_out, ret_flags,
906
time_rec, negState, send_token);
907
if (HARD_ERROR(tmpret))
909
*minor_status = tmpmin;
913
/* Don't output token on error from first call. */
914
*send_token = NO_TOKEN_SEND;
920
OM_uint32 KRB5_CALLCONV
921
spnego_gss_init_sec_context(
922
OM_uint32 *minor_status,
923
gss_cred_id_t claimant_cred_handle,
924
gss_ctx_id_t *context_handle,
925
gss_name_t target_name,
929
gss_channel_bindings_t input_chan_bindings,
930
gss_buffer_t input_token,
931
gss_OID *actual_mech,
932
gss_buffer_t output_token,
933
OM_uint32 *ret_flags,
936
send_token_flag send_token = NO_TOKEN_SEND;
937
OM_uint32 tmpmin, ret, negState;
938
gss_buffer_t mechtok_in, mechListMIC_in, mechListMIC_out;
939
gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER;
940
spnego_gss_cred_id_t spcred = NULL;
941
spnego_gss_ctx_id_t spnego_ctx = NULL;
943
dsyslog("Entering init_sec_context\n");
945
mechtok_in = mechListMIC_out = mechListMIC_in = GSS_C_NO_BUFFER;
949
* This function works in three steps:
951
* 1. Perform mechanism negotiation.
952
* 2. Invoke the negotiated or optimistic mech's gss_init_sec_context
953
* function and examine the results.
954
* 3. Process or generate MICs if necessary.
956
* The three steps share responsibility for determining when the
957
* exchange is complete. If the selected mech completed in a previous
958
* call and no MIC exchange is expected, then step 1 will decide. If
959
* the selected mech completes in this call and no MIC exchange is
960
* expected, then step 2 will decide. If a MIC exchange is expected,
961
* then step 3 will decide. If an error occurs in any step, the
962
* exchange will be aborted, possibly with an error token.
964
* negState determines the state of the negotiation, and is
965
* communicated to the acceptor if a continuing token is sent.
966
* send_token is used to indicate what type of token, if any, should be
970
/* Validate arguments. */
971
if (minor_status != NULL)
973
if (output_token != GSS_C_NO_BUFFER) {
974
output_token->length = 0;
975
output_token->value = NULL;
977
if (minor_status == NULL ||
978
output_token == GSS_C_NO_BUFFER ||
979
context_handle == NULL)
980
return GSS_S_CALL_INACCESSIBLE_WRITE;
982
if (actual_mech != NULL)
983
*actual_mech = GSS_C_NO_OID;
985
/* Step 1: perform mechanism negotiation. */
986
spcred = (spnego_gss_cred_id_t)claimant_cred_handle;
987
if (*context_handle == GSS_C_NO_CONTEXT) {
988
ret = init_ctx_new(minor_status, spcred,
989
context_handle, &send_token);
990
if (ret != GSS_S_CONTINUE_NEEDED) {
994
ret = init_ctx_cont(minor_status, context_handle,
995
input_token, &mechtok_in,
996
&mechListMIC_in, &negState, &send_token);
997
if (HARD_ERROR(ret)) {
1002
/* Step 2: invoke the selected or optimistic mechanism's
1003
* gss_init_sec_context function, if it didn't complete previously. */
1004
spnego_ctx = (spnego_gss_ctx_id_t)*context_handle;
1005
if (!spnego_ctx->mech_complete) {
1006
ret = init_ctx_call_init(
1007
minor_status, spnego_ctx, spcred,
1008
target_name, req_flags,
1009
time_req, mechtok_in,
1010
actual_mech, &mechtok_out,
1011
ret_flags, time_rec,
1012
&negState, &send_token);
1015
/* Step 3: process or generate the MIC, if the negotiated mech is
1016
* complete and supports MICs. */
1017
if (!HARD_ERROR(ret) && spnego_ctx->mech_complete &&
1018
(spnego_ctx->ctx_flags & GSS_C_INTEG_FLAG)) {
1020
ret = handle_mic(minor_status,
1022
(mechtok_out.length != 0),
1023
spnego_ctx, &mechListMIC_out,
1024
&negState, &send_token);
1027
if (send_token == INIT_TOKEN_SEND) {
1028
if (make_spnego_tokenInit_msg(spnego_ctx,
1032
&mechtok_out, send_token,
1033
output_token) < 0) {
1034
ret = GSS_S_FAILURE;
1036
} else if (send_token != NO_TOKEN_SEND) {
1037
if (make_spnego_tokenTarg_msg(negState, GSS_C_NO_OID,
1038
&mechtok_out, mechListMIC_out,
1040
output_token) < 0) {
1041
ret = GSS_S_FAILURE;
1044
gss_release_buffer(&tmpmin, &mechtok_out);
1045
if (ret == GSS_S_COMPLETE) {
1047
* Now, switch the output context to refer to the
1048
* negotiated mechanism's context.
1050
*context_handle = (gss_ctx_id_t)spnego_ctx->ctx_handle;
1051
if (actual_mech != NULL)
1052
*actual_mech = spnego_ctx->actual_mech;
1053
if (ret_flags != NULL)
1054
*ret_flags = spnego_ctx->ctx_flags;
1055
release_spnego_ctx(&spnego_ctx);
1056
} else if (ret != GSS_S_CONTINUE_NEEDED) {
1057
if (spnego_ctx != NULL) {
1058
gss_delete_sec_context(&tmpmin,
1059
&spnego_ctx->ctx_handle,
1061
release_spnego_ctx(&spnego_ctx);
1063
*context_handle = GSS_C_NO_CONTEXT;
1065
if (mechtok_in != GSS_C_NO_BUFFER) {
1066
gss_release_buffer(&tmpmin, mechtok_in);
1069
if (mechListMIC_in != GSS_C_NO_BUFFER) {
1070
gss_release_buffer(&tmpmin, mechListMIC_in);
1071
free(mechListMIC_in);
1073
if (mechListMIC_out != GSS_C_NO_BUFFER) {
1074
gss_release_buffer(&tmpmin, mechListMIC_out);
1075
free(mechListMIC_out);
1078
} /* init_sec_context */
1080
/* We don't want to import KRB5 headers here */
1081
static const gss_OID_desc gss_mech_krb5_oid =
1082
{ 9, "\052\206\110\206\367\022\001\002\002" };
1083
static const gss_OID_desc gss_mech_krb5_wrong_oid =
1084
{ 9, "\052\206\110\202\367\022\001\002\002" };
1087
* verify that the input token length is not 0. If it is, just return.
1088
* If the token length is greater than 0, der encode as a sequence
1089
* and place in buf_out, advancing buf_out.
1093
put_neg_hints(unsigned char **buf_out, gss_buffer_t input_token,
1094
unsigned int buflen)
1098
/* if token length is 0, we do not want to send */
1099
if (input_token->length == 0)
1102
if (input_token->length > buflen)
1105
*(*buf_out)++ = SEQUENCE;
1106
if ((ret = gssint_put_der_length(input_token->length, buf_out,
1107
input_token->length)))
1109
TWRITE_STR(*buf_out, input_token->value, input_token->length);
1114
* NegHints ::= SEQUENCE {
1115
* hintName [0] GeneralString OPTIONAL,
1116
* hintAddress [1] OCTET STRING OPTIONAL
1120
#define HOST_PREFIX "host@"
1121
#define HOST_PREFIX_LEN (sizeof(HOST_PREFIX) - 1)
1124
make_NegHints(OM_uint32 *minor_status,
1125
spnego_gss_cred_id_t spcred, gss_buffer_t *outbuf)
1127
gss_buffer_desc hintNameBuf;
1128
gss_name_t hintName = GSS_C_NO_NAME;
1129
gss_name_t hintKerberosName;
1130
gss_OID hintNameType;
1131
OM_uint32 major_status;
1133
unsigned int tlen = 0;
1134
unsigned int hintNameSize = 0;
1138
*outbuf = GSS_C_NO_BUFFER;
1140
if (spcred != NULL) {
1141
major_status = gss_inquire_cred(minor_status,
1147
if (major_status != GSS_S_COMPLETE)
1148
return (major_status);
1151
if (hintName == GSS_C_NO_NAME) {
1152
krb5_error_code code;
1153
krb5int_access kaccess;
1154
char hostname[HOST_PREFIX_LEN + MAXHOSTNAMELEN + 1] = HOST_PREFIX;
1156
code = krb5int_accessor(&kaccess, KRB5INT_ACCESS_VERSION);
1158
*minor_status = code;
1159
return (GSS_S_FAILURE);
1162
/* this breaks mutual authentication but Samba relies on it */
1163
code = (*kaccess.clean_hostname)(NULL, NULL,
1164
&hostname[HOST_PREFIX_LEN],
1167
*minor_status = code;
1168
return (GSS_S_FAILURE);
1171
hintNameBuf.value = hostname;
1172
hintNameBuf.length = strlen(hostname);
1174
major_status = gss_import_name(minor_status,
1176
GSS_C_NT_HOSTBASED_SERVICE,
1178
if (major_status != GSS_S_COMPLETE) {
1179
return (major_status);
1183
hintNameBuf.value = NULL;
1184
hintNameBuf.length = 0;
1186
major_status = gss_canonicalize_name(minor_status,
1188
(gss_OID)&gss_mech_krb5_oid,
1190
if (major_status != GSS_S_COMPLETE) {
1191
gss_release_name(&minor, &hintName);
1192
return (major_status);
1194
gss_release_name(&minor, &hintName);
1196
major_status = gss_display_name(minor_status,
1200
if (major_status != GSS_S_COMPLETE) {
1201
gss_release_name(&minor, &hintName);
1202
return (major_status);
1204
gss_release_name(&minor, &hintKerberosName);
1207
* Now encode the name hint into a NegHints ASN.1 type
1209
major_status = GSS_S_FAILURE;
1211
/* Length of DER encoded GeneralString */
1212
tlen = 1 + gssint_der_length_size(hintNameBuf.length) +
1214
hintNameSize = tlen;
1216
/* Length of DER encoded hintName */
1217
tlen += 1 + gssint_der_length_size(hintNameSize);
1219
t = gssalloc_malloc(tlen);
1221
*minor_status = ENOMEM;
1227
*ptr++ = CONTEXT | 0x00; /* hintName identifier */
1228
if (gssint_put_der_length(hintNameSize,
1229
&ptr, tlen - (int)(ptr-t)))
1232
*ptr++ = GENERAL_STRING;
1233
if (gssint_put_der_length(hintNameBuf.length,
1234
&ptr, tlen - (int)(ptr-t)))
1237
memcpy(ptr, hintNameBuf.value, hintNameBuf.length);
1238
ptr += hintNameBuf.length;
1240
*outbuf = (gss_buffer_t)malloc(sizeof(gss_buffer_desc));
1241
if (*outbuf == NULL) {
1242
*minor_status = ENOMEM;
1245
(*outbuf)->value = (void *)t;
1246
(*outbuf)->length = ptr - t;
1248
t = NULL; /* don't free */
1251
major_status = GSS_S_COMPLETE;
1258
gss_release_buffer(&minor, &hintNameBuf);
1260
return (major_status);
1264
* Support the Microsoft NegHints extension to SPNEGO for compatibility with
1265
* some versions of Samba. See:
1266
* http://msdn.microsoft.com/en-us/library/cc247039(PROT.10).aspx
1269
acc_ctx_hints(OM_uint32 *minor_status,
1271
spnego_gss_cred_id_t spcred,
1272
gss_buffer_t *mechListMIC,
1273
OM_uint32 *negState,
1274
send_token_flag *return_token)
1276
OM_uint32 tmpmin, ret;
1277
gss_OID_set supported_mechSet;
1278
spnego_gss_ctx_id_t sc = NULL;
1280
*mechListMIC = GSS_C_NO_BUFFER;
1281
supported_mechSet = GSS_C_NO_OID_SET;
1282
*return_token = NO_TOKEN_SEND;
1286
/* A hint request must be the first token received. */
1287
if (*ctx != GSS_C_NO_CONTEXT)
1288
return GSS_S_DEFECTIVE_TOKEN;
1290
ret = get_negotiable_mechs(minor_status, spcred, GSS_C_ACCEPT,
1291
&supported_mechSet);
1292
if (ret != GSS_S_COMPLETE)
1295
ret = make_NegHints(minor_status, spcred, mechListMIC);
1296
if (ret != GSS_S_COMPLETE)
1299
sc = create_spnego_ctx();
1301
ret = GSS_S_FAILURE;
1304
if (put_mech_set(supported_mechSet, &sc->DER_mechTypes) < 0) {
1305
ret = GSS_S_FAILURE;
1308
sc->internal_mech = GSS_C_NO_OID;
1310
*negState = ACCEPT_INCOMPLETE;
1311
*return_token = INIT_TOKEN_SEND;
1313
*ctx = (gss_ctx_id_t)sc;
1315
ret = GSS_S_COMPLETE;
1318
release_spnego_ctx(&sc);
1319
gss_release_oid_set(&tmpmin, &supported_mechSet);
1325
* Set negState to REJECT if the token is defective, else
1326
* ACCEPT_INCOMPLETE or REQUEST_MIC, depending on whether initiator's
1327
* preferred mechanism is supported.
1330
acc_ctx_new(OM_uint32 *minor_status,
1333
spnego_gss_cred_id_t spcred,
1334
gss_buffer_t *mechToken,
1335
gss_buffer_t *mechListMIC,
1336
OM_uint32 *negState,
1337
send_token_flag *return_token)
1339
OM_uint32 tmpmin, ret, req_flags;
1340
gss_OID_set supported_mechSet, mechTypes;
1341
gss_buffer_desc der_mechTypes;
1342
gss_OID mech_wanted;
1343
spnego_gss_ctx_id_t sc = NULL;
1345
ret = GSS_S_DEFECTIVE_TOKEN;
1346
der_mechTypes.length = 0;
1347
der_mechTypes.value = NULL;
1348
*mechToken = *mechListMIC = GSS_C_NO_BUFFER;
1349
supported_mechSet = mechTypes = GSS_C_NO_OID_SET;
1350
*return_token = ERROR_TOKEN_SEND;
1354
ret = get_negTokenInit(minor_status, buf, &der_mechTypes,
1355
&mechTypes, &req_flags,
1356
mechToken, mechListMIC);
1357
if (ret != GSS_S_COMPLETE) {
1360
ret = get_negotiable_mechs(minor_status, spcred, GSS_C_ACCEPT,
1361
&supported_mechSet);
1362
if (ret != GSS_S_COMPLETE) {
1363
*return_token = NO_TOKEN_SEND;
1367
* Select the best match between the list of mechs
1368
* that the initiator requested and the list that
1369
* the acceptor will support.
1371
mech_wanted = negotiate_mech(supported_mechSet, mechTypes, negState);
1372
if (*negState == REJECT) {
1373
ret = GSS_S_BAD_MECH;
1376
sc = (spnego_gss_ctx_id_t)*ctx;
1378
gss_release_buffer(&tmpmin, &sc->DER_mechTypes);
1379
assert(mech_wanted != GSS_C_NO_OID);
1381
sc = create_spnego_ctx();
1383
ret = GSS_S_FAILURE;
1384
*return_token = NO_TOKEN_SEND;
1387
sc->mech_set = supported_mechSet;
1388
supported_mechSet = GSS_C_NO_OID_SET;
1389
sc->internal_mech = mech_wanted;
1390
sc->DER_mechTypes = der_mechTypes;
1391
der_mechTypes.length = 0;
1392
der_mechTypes.value = NULL;
1394
if (*negState == REQUEST_MIC)
1397
*return_token = INIT_TOKEN_SEND;
1399
*ctx = (gss_ctx_id_t)sc;
1400
ret = GSS_S_COMPLETE;
1402
gss_release_oid_set(&tmpmin, &mechTypes);
1403
gss_release_oid_set(&tmpmin, &supported_mechSet);
1404
if (der_mechTypes.length != 0)
1405
gss_release_buffer(&tmpmin, &der_mechTypes);
1411
acc_ctx_cont(OM_uint32 *minstat,
1414
gss_buffer_t *responseToken,
1415
gss_buffer_t *mechListMIC,
1416
OM_uint32 *negState,
1417
send_token_flag *return_token)
1419
OM_uint32 ret, tmpmin;
1420
gss_OID supportedMech;
1421
spnego_gss_ctx_id_t sc;
1423
unsigned char *ptr, *bufstart;
1425
sc = (spnego_gss_ctx_id_t)*ctx;
1426
ret = GSS_S_DEFECTIVE_TOKEN;
1429
supportedMech = GSS_C_NO_OID;
1430
*return_token = ERROR_TOKEN_SEND;
1431
*responseToken = *mechListMIC = GSS_C_NO_BUFFER;
1433
ptr = bufstart = buf->value;
1434
#define REMAIN (buf->length - (ptr - bufstart))
1435
if (REMAIN > INT_MAX)
1436
return GSS_S_DEFECTIVE_TOKEN;
1439
* Attempt to work with old Sun SPNEGO.
1441
if (*ptr == HEADER_ID) {
1442
ret = g_verify_token_header(gss_mech_spnego,
1443
&len, &ptr, 0, REMAIN);
1446
return GSS_S_DEFECTIVE_TOKEN;
1449
if (*ptr != (CONTEXT | 0x01)) {
1450
return GSS_S_DEFECTIVE_TOKEN;
1452
ret = get_negTokenResp(minstat, ptr, REMAIN,
1453
negState, &supportedMech,
1454
responseToken, mechListMIC);
1455
if (ret != GSS_S_COMPLETE)
1458
if (*responseToken == GSS_C_NO_BUFFER &&
1459
*mechListMIC == GSS_C_NO_BUFFER) {
1461
ret = GSS_S_DEFECTIVE_TOKEN;
1464
if (supportedMech != GSS_C_NO_OID) {
1465
ret = GSS_S_DEFECTIVE_TOKEN;
1469
*negState = ACCEPT_INCOMPLETE;
1470
*return_token = CONT_TOKEN_SEND;
1472
if (supportedMech != GSS_C_NO_OID) {
1473
generic_gss_release_oid(&tmpmin, &supportedMech);
1480
* Verify that mech OID is either exactly the same as the negotiated
1481
* mech OID, or is a mech OID supported by the negotiated mech. MS
1482
* implementations can list a most preferred mech using an incorrect
1483
* krb5 OID while emitting a krb5 initiator mech token having the
1484
* correct krb5 mech OID.
1487
acc_ctx_vfy_oid(OM_uint32 *minor_status,
1488
spnego_gss_ctx_id_t sc, gss_OID mechoid,
1489
OM_uint32 *negState, send_token_flag *tokflag)
1491
OM_uint32 ret, tmpmin;
1492
gss_mechanism mech = NULL;
1493
gss_OID_set mech_set = GSS_C_NO_OID_SET;
1496
if (g_OID_equal(sc->internal_mech, mechoid))
1497
return GSS_S_COMPLETE;
1499
mech = gssint_get_mechanism(sc->internal_mech);
1500
if (mech == NULL || mech->gss_indicate_mechs == NULL) {
1501
*minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
1502
map_errcode(minor_status);
1504
*tokflag = ERROR_TOKEN_SEND;
1505
return GSS_S_BAD_MECH;
1507
ret = mech->gss_indicate_mechs(minor_status, &mech_set);
1508
if (ret != GSS_S_COMPLETE) {
1509
*tokflag = NO_TOKEN_SEND;
1510
map_error(minor_status, mech);
1513
ret = gss_test_oid_set_member(minor_status, mechoid,
1514
mech_set, &present);
1515
if (ret != GSS_S_COMPLETE)
1518
*minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
1519
map_errcode(minor_status);
1521
*tokflag = ERROR_TOKEN_SEND;
1522
ret = GSS_S_BAD_MECH;
1525
gss_release_oid_set(&tmpmin, &mech_set);
1530
* Wrap call to gss_accept_sec_context() and update state
1534
acc_ctx_call_acc(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
1535
spnego_gss_cred_id_t spcred, gss_buffer_t mechtok_in,
1536
gss_OID *mech_type, gss_buffer_t mechtok_out,
1537
OM_uint32 *ret_flags, OM_uint32 *time_rec,
1538
gss_cred_id_t *delegated_cred_handle,
1539
OM_uint32 *negState, send_token_flag *tokflag)
1542
gss_OID_desc mechoid;
1543
gss_cred_id_t mcred;
1545
if (sc->ctx_handle == GSS_C_NO_CONTEXT) {
1547
* mechoid is an alias; don't free it.
1549
ret = gssint_get_mech_type(&mechoid, mechtok_in);
1550
if (ret != GSS_S_COMPLETE) {
1551
*tokflag = NO_TOKEN_SEND;
1554
ret = acc_ctx_vfy_oid(minor_status, sc, &mechoid,
1556
if (ret != GSS_S_COMPLETE)
1560
mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
1561
ret = gss_accept_sec_context(minor_status,
1565
GSS_C_NO_CHANNEL_BINDINGS,
1571
delegated_cred_handle);
1572
if (ret == GSS_S_COMPLETE) {
1575
* Force MIC to be not required even if we previously
1578
char *envstr = getenv("MS_FORCE_NO_MIC");
1580
if (envstr != NULL && strcmp(envstr, "1") == 0 &&
1581
!(sc->ctx_flags & GSS_C_MUTUAL_FLAG) &&
1587
sc->mech_complete = 1;
1588
if (ret_flags != NULL)
1589
*ret_flags = sc->ctx_flags;
1591
if (!sc->mic_reqd ||
1592
!(sc->ctx_flags & GSS_C_INTEG_FLAG)) {
1593
/* No MIC exchange required, so we're done. */
1594
*negState = ACCEPT_COMPLETE;
1595
ret = GSS_S_COMPLETE;
1597
/* handle_mic will decide if we're done. */
1598
ret = GSS_S_CONTINUE_NEEDED;
1600
} else if (ret != GSS_S_CONTINUE_NEEDED) {
1602
*tokflag = ERROR_TOKEN_SEND;
1608
OM_uint32 KRB5_CALLCONV
1609
spnego_gss_accept_sec_context(
1610
OM_uint32 *minor_status,
1611
gss_ctx_id_t *context_handle,
1612
gss_cred_id_t verifier_cred_handle,
1613
gss_buffer_t input_token,
1614
gss_channel_bindings_t input_chan_bindings,
1615
gss_name_t *src_name,
1617
gss_buffer_t output_token,
1618
OM_uint32 *ret_flags,
1619
OM_uint32 *time_rec,
1620
gss_cred_id_t *delegated_cred_handle)
1622
OM_uint32 ret, tmpmin, negState;
1623
send_token_flag return_token;
1624
gss_buffer_t mechtok_in, mic_in, mic_out;
1625
gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER;
1626
spnego_gss_ctx_id_t sc = NULL;
1627
spnego_gss_cred_id_t spcred = NULL;
1628
int sendTokenInit = 0, tmpret;
1630
mechtok_in = mic_in = mic_out = GSS_C_NO_BUFFER;
1633
* This function works in three steps:
1635
* 1. Perform mechanism negotiation.
1636
* 2. Invoke the negotiated mech's gss_accept_sec_context function
1637
* and examine the results.
1638
* 3. Process or generate MICs if necessary.
1640
* Step one determines whether the negotiation requires a MIC exchange,
1641
* while steps two and three share responsibility for determining when
1642
* the exchange is complete. If the selected mech completes in this
1643
* call and no MIC exchange is expected, then step 2 will decide. If a
1644
* MIC exchange is expected, then step 3 will decide. If an error
1645
* occurs in any step, the exchange will be aborted, possibly with an
1648
* negState determines the state of the negotiation, and is
1649
* communicated to the acceptor if a continuing token is sent.
1650
* return_token is used to indicate what type of token, if any, should
1654
/* Validate arguments. */
1655
if (minor_status != NULL)
1657
if (output_token != GSS_C_NO_BUFFER) {
1658
output_token->length = 0;
1659
output_token->value = NULL;
1662
if (minor_status == NULL ||
1663
output_token == GSS_C_NO_BUFFER ||
1664
context_handle == NULL)
1665
return GSS_S_CALL_INACCESSIBLE_WRITE;
1667
if (input_token == GSS_C_NO_BUFFER)
1668
return GSS_S_CALL_INACCESSIBLE_READ;
1670
/* Step 1: Perform mechanism negotiation. */
1671
sc = (spnego_gss_ctx_id_t)*context_handle;
1672
spcred = (spnego_gss_cred_id_t)verifier_cred_handle;
1673
if (sc == NULL || sc->internal_mech == GSS_C_NO_OID) {
1674
/* Process an initial token or request for NegHints. */
1675
if (src_name != NULL)
1676
*src_name = GSS_C_NO_NAME;
1677
if (mech_type != NULL)
1678
*mech_type = GSS_C_NO_OID;
1679
if (time_rec != NULL)
1681
if (ret_flags != NULL)
1683
if (delegated_cred_handle != NULL)
1684
*delegated_cred_handle = GSS_C_NO_CREDENTIAL;
1685
if (input_token->length == 0) {
1686
ret = acc_ctx_hints(minor_status,
1687
context_handle, spcred,
1691
if (ret != GSS_S_COMPLETE)
1694
ret = GSS_S_CONTINUE_NEEDED;
1696
/* Can set negState to REQUEST_MIC */
1697
ret = acc_ctx_new(minor_status, input_token,
1698
context_handle, spcred,
1699
&mechtok_in, &mic_in,
1700
&negState, &return_token);
1701
if (ret != GSS_S_COMPLETE)
1703
ret = GSS_S_CONTINUE_NEEDED;
1706
/* Process a response token. Can set negState to
1707
* ACCEPT_INCOMPLETE. */
1708
ret = acc_ctx_cont(minor_status, input_token,
1709
context_handle, &mechtok_in,
1710
&mic_in, &negState, &return_token);
1711
if (ret != GSS_S_COMPLETE)
1713
ret = GSS_S_CONTINUE_NEEDED;
1716
/* Step 2: invoke the negotiated mechanism's gss_accept_sec_context
1718
sc = (spnego_gss_ctx_id_t)*context_handle;
1720
* Handle mechtok_in and mic_in only if they are
1721
* present in input_token. If neither is present, whether
1722
* this is an error depends on whether this is the first
1723
* round-trip. RET is set to a default value according to
1724
* whether it is the first round-trip.
1726
if (negState != REQUEST_MIC && mechtok_in != GSS_C_NO_BUFFER) {
1727
ret = acc_ctx_call_acc(minor_status, sc, spcred,
1728
mechtok_in, mech_type, &mechtok_out,
1729
ret_flags, time_rec,
1730
delegated_cred_handle,
1731
&negState, &return_token);
1734
/* Step 3: process or generate the MIC, if the negotiated mech is
1735
* complete and supports MICs. */
1736
if (!HARD_ERROR(ret) && sc->mech_complete &&
1737
(sc->ctx_flags & GSS_C_INTEG_FLAG)) {
1739
ret = handle_mic(minor_status, mic_in,
1740
(mechtok_out.length != 0),
1742
&negState, &return_token);
1745
if (return_token == INIT_TOKEN_SEND && sendTokenInit) {
1747
tmpret = make_spnego_tokenInit_msg(sc, 1, mic_out, 0,
1749
return_token, output_token);
1751
ret = GSS_S_FAILURE;
1752
} else if (return_token != NO_TOKEN_SEND &&
1753
return_token != CHECK_MIC) {
1754
tmpret = make_spnego_tokenTarg_msg(negState,
1755
sc ? sc->internal_mech :
1757
&mechtok_out, mic_out,
1761
ret = GSS_S_FAILURE;
1763
if (ret == GSS_S_COMPLETE) {
1764
*context_handle = (gss_ctx_id_t)sc->ctx_handle;
1765
if (sc->internal_name != GSS_C_NO_NAME &&
1767
*src_name = sc->internal_name;
1768
sc->internal_name = GSS_C_NO_NAME;
1770
release_spnego_ctx(&sc);
1771
} else if (ret != GSS_S_CONTINUE_NEEDED) {
1773
gss_delete_sec_context(&tmpmin, &sc->ctx_handle,
1775
release_spnego_ctx(&sc);
1777
*context_handle = GSS_C_NO_CONTEXT;
1779
gss_release_buffer(&tmpmin, &mechtok_out);
1780
if (mechtok_in != GSS_C_NO_BUFFER) {
1781
gss_release_buffer(&tmpmin, mechtok_in);
1784
if (mic_in != GSS_C_NO_BUFFER) {
1785
gss_release_buffer(&tmpmin, mic_in);
1788
if (mic_out != GSS_C_NO_BUFFER) {
1789
gss_release_buffer(&tmpmin, mic_out);
1794
#endif /* LEAN_CLIENT */
1798
OM_uint32 KRB5_CALLCONV
1799
spnego_gss_display_status(
1800
OM_uint32 *minor_status,
1801
OM_uint32 status_value,
1804
OM_uint32 *message_context,
1805
gss_buffer_t status_string)
1807
dsyslog("Entering display_status\n");
1809
*message_context = 0;
1810
switch (status_value) {
1811
case ERR_SPNEGO_NO_MECHS_AVAILABLE:
1813
*status_string = make_err_msg(_("SPNEGO cannot find "
1814
"mechanisms to negotiate"));
1816
case ERR_SPNEGO_NO_CREDS_ACQUIRED:
1818
*status_string = make_err_msg(_("SPNEGO failed to acquire "
1821
case ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR:
1823
*status_string = make_err_msg(_("SPNEGO acceptor did not "
1824
"select a mechanism"));
1826
case ERR_SPNEGO_NEGOTIATION_FAILED:
1828
*status_string = make_err_msg(_("SPNEGO failed to negotiate a "
1831
case ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR:
1833
*status_string = make_err_msg(_("SPNEGO acceptor did not "
1834
"return a valid token"));
1837
status_string->length = 0;
1838
status_string->value = "";
1842
dsyslog("Leaving display_status\n");
1843
return (GSS_S_COMPLETE);
1848
OM_uint32 KRB5_CALLCONV
1849
spnego_gss_import_name(
1850
OM_uint32 *minor_status,
1851
gss_buffer_t input_name_buffer,
1852
gss_OID input_name_type,
1853
gss_name_t *output_name)
1857
dsyslog("Entering import_name\n");
1859
status = gss_import_name(minor_status, input_name_buffer,
1860
input_name_type, output_name);
1862
dsyslog("Leaving import_name\n");
1867
OM_uint32 KRB5_CALLCONV
1868
spnego_gss_release_name(
1869
OM_uint32 *minor_status,
1870
gss_name_t *input_name)
1874
dsyslog("Entering release_name\n");
1876
status = gss_release_name(minor_status, input_name);
1878
dsyslog("Leaving release_name\n");
1883
OM_uint32 KRB5_CALLCONV
1884
spnego_gss_duplicate_name(
1885
OM_uint32 *minor_status,
1886
const gss_name_t input_name,
1887
gss_name_t *output_name)
1891
dsyslog("Entering duplicate_name\n");
1893
status = gss_duplicate_name(minor_status, input_name, output_name);
1895
dsyslog("Leaving duplicate_name\n");
1899
OM_uint32 KRB5_CALLCONV
1900
spnego_gss_inquire_cred(
1901
OM_uint32 *minor_status,
1902
gss_cred_id_t cred_handle,
1904
OM_uint32 *lifetime,
1906
gss_OID_set *mechanisms)
1909
spnego_gss_cred_id_t spcred = NULL;
1910
gss_cred_id_t creds = GSS_C_NO_CREDENTIAL;
1911
OM_uint32 tmp_minor_status;
1912
OM_uint32 initiator_lifetime, acceptor_lifetime;
1914
dsyslog("Entering inquire_cred\n");
1917
* To avoid infinite recursion, if GSS_C_NO_CREDENTIAL is
1918
* supplied we call gss_inquire_cred_by_mech() on the
1919
* first non-SPNEGO mechanism.
1921
spcred = (spnego_gss_cred_id_t)cred_handle;
1922
if (spcred == NULL) {
1923
status = get_available_mechs(minor_status,
1926
GSS_C_NO_CRED_STORE,
1929
if (status != GSS_S_COMPLETE) {
1930
dsyslog("Leaving inquire_cred\n");
1934
if ((*mechanisms)->count == 0) {
1935
gss_release_cred(&tmp_minor_status, &creds);
1936
gss_release_oid_set(&tmp_minor_status, mechanisms);
1937
dsyslog("Leaving inquire_cred\n");
1938
return (GSS_S_DEFECTIVE_CREDENTIAL);
1941
assert((*mechanisms)->elements != NULL);
1943
status = gss_inquire_cred_by_mech(minor_status,
1945
&(*mechanisms)->elements[0],
1947
&initiator_lifetime,
1950
if (status != GSS_S_COMPLETE) {
1951
gss_release_cred(&tmp_minor_status, &creds);
1952
dsyslog("Leaving inquire_cred\n");
1956
if (lifetime != NULL)
1957
*lifetime = (*cred_usage == GSS_C_ACCEPT) ?
1958
acceptor_lifetime : initiator_lifetime;
1960
gss_release_cred(&tmp_minor_status, &creds);
1962
status = gss_inquire_cred(minor_status, spcred->mcred,
1964
cred_usage, mechanisms);
1967
dsyslog("Leaving inquire_cred\n");
1973
OM_uint32 KRB5_CALLCONV
1974
spnego_gss_compare_name(
1975
OM_uint32 *minor_status,
1976
const gss_name_t name1,
1977
const gss_name_t name2,
1980
OM_uint32 status = GSS_S_COMPLETE;
1981
dsyslog("Entering compare_name\n");
1983
status = gss_compare_name(minor_status, name1, name2, name_equal);
1985
dsyslog("Leaving compare_name\n");
1991
OM_uint32 KRB5_CALLCONV
1992
spnego_gss_display_name(
1993
OM_uint32 *minor_status,
1994
gss_name_t input_name,
1995
gss_buffer_t output_name_buffer,
1996
gss_OID *output_name_type)
1998
OM_uint32 status = GSS_S_COMPLETE;
1999
dsyslog("Entering display_name\n");
2001
status = gss_display_name(minor_status, input_name,
2002
output_name_buffer, output_name_type);
2004
dsyslog("Leaving display_name\n");
2010
OM_uint32 KRB5_CALLCONV
2011
spnego_gss_inquire_names_for_mech(
2012
OM_uint32 *minor_status,
2014
gss_OID_set *name_types)
2016
OM_uint32 major, minor;
2018
dsyslog("Entering inquire_names_for_mech\n");
2020
* We only know how to handle our own mechanism.
2022
if ((mechanism != GSS_C_NULL_OID) &&
2023
!g_OID_equal(gss_mech_spnego, mechanism)) {
2025
return (GSS_S_FAILURE);
2028
major = gss_create_empty_oid_set(minor_status, name_types);
2029
if (major == GSS_S_COMPLETE) {
2030
/* Now add our members. */
2031
if (((major = gss_add_oid_set_member(minor_status,
2032
(gss_OID) GSS_C_NT_USER_NAME,
2033
name_types)) == GSS_S_COMPLETE) &&
2034
((major = gss_add_oid_set_member(minor_status,
2035
(gss_OID) GSS_C_NT_MACHINE_UID_NAME,
2036
name_types)) == GSS_S_COMPLETE) &&
2037
((major = gss_add_oid_set_member(minor_status,
2038
(gss_OID) GSS_C_NT_STRING_UID_NAME,
2039
name_types)) == GSS_S_COMPLETE)) {
2040
major = gss_add_oid_set_member(minor_status,
2041
(gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
2045
if (major != GSS_S_COMPLETE)
2046
(void) gss_release_oid_set(&minor, name_types);
2049
dsyslog("Leaving inquire_names_for_mech\n");
2053
OM_uint32 KRB5_CALLCONV
2055
OM_uint32 *minor_status,
2056
gss_ctx_id_t context_handle,
2057
gss_buffer_t input_message_buffer,
2058
gss_buffer_t output_message_buffer,
2060
gss_qop_t *qop_state)
2063
ret = gss_unwrap(minor_status,
2065
input_message_buffer,
2066
output_message_buffer,
2073
OM_uint32 KRB5_CALLCONV
2075
OM_uint32 *minor_status,
2076
gss_ctx_id_t context_handle,
2079
gss_buffer_t input_message_buffer,
2081
gss_buffer_t output_message_buffer)
2084
ret = gss_wrap(minor_status,
2088
input_message_buffer,
2090
output_message_buffer);
2095
OM_uint32 KRB5_CALLCONV
2096
spnego_gss_process_context_token(
2097
OM_uint32 *minor_status,
2098
const gss_ctx_id_t context_handle,
2099
const gss_buffer_t token_buffer)
2102
ret = gss_process_context_token(minor_status,
2109
OM_uint32 KRB5_CALLCONV
2110
spnego_gss_delete_sec_context(
2111
OM_uint32 *minor_status,
2112
gss_ctx_id_t *context_handle,
2113
gss_buffer_t output_token)
2115
OM_uint32 ret = GSS_S_COMPLETE;
2116
spnego_gss_ctx_id_t *ctx =
2117
(spnego_gss_ctx_id_t *)context_handle;
2121
if (context_handle == NULL)
2122
return (GSS_S_FAILURE);
2125
return (GSS_S_COMPLETE);
2128
* If this is still an SPNEGO mech, release it locally.
2130
if ((*ctx)->magic_num == SPNEGO_MAGIC_ID) {
2131
(void) gss_delete_sec_context(minor_status,
2132
&(*ctx)->ctx_handle,
2134
(void) release_spnego_ctx(ctx);
2136
ret = gss_delete_sec_context(minor_status,
2144
OM_uint32 KRB5_CALLCONV
2145
spnego_gss_context_time(
2146
OM_uint32 *minor_status,
2147
const gss_ctx_id_t context_handle,
2148
OM_uint32 *time_rec)
2151
ret = gss_context_time(minor_status,
2157
OM_uint32 KRB5_CALLCONV
2158
spnego_gss_export_sec_context(
2159
OM_uint32 *minor_status,
2160
gss_ctx_id_t *context_handle,
2161
gss_buffer_t interprocess_token)
2164
ret = gss_export_sec_context(minor_status,
2166
interprocess_token);
2170
OM_uint32 KRB5_CALLCONV
2171
spnego_gss_import_sec_context(
2172
OM_uint32 *minor_status,
2173
const gss_buffer_t interprocess_token,
2174
gss_ctx_id_t *context_handle)
2177
ret = gss_import_sec_context(minor_status,
2182
#endif /* LEAN_CLIENT */
2184
OM_uint32 KRB5_CALLCONV
2185
spnego_gss_inquire_context(
2186
OM_uint32 *minor_status,
2187
const gss_ctx_id_t context_handle,
2188
gss_name_t *src_name,
2189
gss_name_t *targ_name,
2190
OM_uint32 *lifetime_rec,
2192
OM_uint32 *ctx_flags,
2193
int *locally_initiated,
2196
OM_uint32 ret = GSS_S_COMPLETE;
2198
ret = gss_inquire_context(minor_status,
2211
OM_uint32 KRB5_CALLCONV
2212
spnego_gss_wrap_size_limit(
2213
OM_uint32 *minor_status,
2214
const gss_ctx_id_t context_handle,
2217
OM_uint32 req_output_size,
2218
OM_uint32 *max_input_size)
2221
ret = gss_wrap_size_limit(minor_status,
2230
OM_uint32 KRB5_CALLCONV
2232
OM_uint32 *minor_status,
2233
const gss_ctx_id_t context_handle,
2235
const gss_buffer_t message_buffer,
2236
gss_buffer_t message_token)
2239
ret = gss_get_mic(minor_status,
2247
OM_uint32 KRB5_CALLCONV
2248
spnego_gss_verify_mic(
2249
OM_uint32 *minor_status,
2250
const gss_ctx_id_t context_handle,
2251
const gss_buffer_t msg_buffer,
2252
const gss_buffer_t token_buffer,
2253
gss_qop_t *qop_state)
2256
ret = gss_verify_mic(minor_status,
2264
OM_uint32 KRB5_CALLCONV
2265
spnego_gss_inquire_sec_context_by_oid(
2266
OM_uint32 *minor_status,
2267
const gss_ctx_id_t context_handle,
2268
const gss_OID desired_object,
2269
gss_buffer_set_t *data_set)
2272
ret = gss_inquire_sec_context_by_oid(minor_status,
2279
OM_uint32 KRB5_CALLCONV
2280
spnego_gss_inquire_cred_by_oid(
2281
OM_uint32 *minor_status,
2282
const gss_cred_id_t cred_handle,
2283
const gss_OID desired_object,
2284
gss_buffer_set_t *data_set)
2287
spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)cred_handle;
2288
gss_cred_id_t mcred;
2289
mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
2290
ret = gss_inquire_cred_by_oid(minor_status,
2297
OM_uint32 KRB5_CALLCONV
2298
spnego_gss_set_cred_option(
2299
OM_uint32 *minor_status,
2300
gss_cred_id_t *cred_handle,
2301
const gss_OID desired_object,
2302
const gss_buffer_t value)
2305
OM_uint32 tmp_minor_status;
2306
spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)*cred_handle;
2307
gss_cred_id_t mcred;
2309
mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
2310
ret = gss_set_cred_option(minor_status,
2314
if (ret == GSS_S_COMPLETE && spcred == NULL) {
2316
* If the mechanism allocated a new credential handle, then
2317
* we need to wrap it up in an SPNEGO credential handle.
2320
spcred = malloc(sizeof(spnego_gss_cred_id_rec));
2321
if (spcred == NULL) {
2322
gss_release_cred(&tmp_minor_status, &mcred);
2323
*minor_status = ENOMEM;
2324
return (GSS_S_FAILURE);
2326
spcred->mcred = mcred;
2327
spcred->neg_mechs = GSS_C_NULL_OID_SET;
2328
*cred_handle = (gss_cred_id_t)spcred;
2334
OM_uint32 KRB5_CALLCONV
2335
spnego_gss_set_sec_context_option(
2336
OM_uint32 *minor_status,
2337
gss_ctx_id_t *context_handle,
2338
const gss_OID desired_object,
2339
const gss_buffer_t value)
2342
ret = gss_set_sec_context_option(minor_status,
2349
OM_uint32 KRB5_CALLCONV
2350
spnego_gss_wrap_aead(OM_uint32 *minor_status,
2351
gss_ctx_id_t context_handle,
2354
gss_buffer_t input_assoc_buffer,
2355
gss_buffer_t input_payload_buffer,
2357
gss_buffer_t output_message_buffer)
2360
ret = gss_wrap_aead(minor_status,
2365
input_payload_buffer,
2367
output_message_buffer);
2372
OM_uint32 KRB5_CALLCONV
2373
spnego_gss_unwrap_aead(OM_uint32 *minor_status,
2374
gss_ctx_id_t context_handle,
2375
gss_buffer_t input_message_buffer,
2376
gss_buffer_t input_assoc_buffer,
2377
gss_buffer_t output_payload_buffer,
2379
gss_qop_t *qop_state)
2382
ret = gss_unwrap_aead(minor_status,
2384
input_message_buffer,
2386
output_payload_buffer,
2392
OM_uint32 KRB5_CALLCONV
2393
spnego_gss_wrap_iov(OM_uint32 *minor_status,
2394
gss_ctx_id_t context_handle,
2398
gss_iov_buffer_desc *iov,
2402
ret = gss_wrap_iov(minor_status,
2412
OM_uint32 KRB5_CALLCONV
2413
spnego_gss_unwrap_iov(OM_uint32 *minor_status,
2414
gss_ctx_id_t context_handle,
2416
gss_qop_t *qop_state,
2417
gss_iov_buffer_desc *iov,
2421
ret = gss_unwrap_iov(minor_status,
2430
OM_uint32 KRB5_CALLCONV
2431
spnego_gss_wrap_iov_length(OM_uint32 *minor_status,
2432
gss_ctx_id_t context_handle,
2436
gss_iov_buffer_desc *iov,
2440
ret = gss_wrap_iov_length(minor_status,
2451
OM_uint32 KRB5_CALLCONV
2452
spnego_gss_complete_auth_token(
2453
OM_uint32 *minor_status,
2454
const gss_ctx_id_t context_handle,
2455
gss_buffer_t input_message_buffer)
2458
ret = gss_complete_auth_token(minor_status,
2460
input_message_buffer);
2464
OM_uint32 KRB5_CALLCONV
2465
spnego_gss_acquire_cred_impersonate_name(OM_uint32 *minor_status,
2466
const gss_cred_id_t impersonator_cred_handle,
2467
const gss_name_t desired_name,
2469
gss_OID_set desired_mechs,
2470
gss_cred_usage_t cred_usage,
2471
gss_cred_id_t *output_cred_handle,
2472
gss_OID_set *actual_mechs,
2473
OM_uint32 *time_rec)
2476
gss_OID_set amechs = GSS_C_NULL_OID_SET;
2477
spnego_gss_cred_id_t imp_spcred = NULL, out_spcred = NULL;
2478
gss_cred_id_t imp_mcred, out_mcred;
2480
dsyslog("Entering spnego_gss_acquire_cred_impersonate_name\n");
2483
*actual_mechs = NULL;
2488
imp_spcred = (spnego_gss_cred_id_t)impersonator_cred_handle;
2489
imp_mcred = imp_spcred ? imp_spcred->mcred : GSS_C_NO_CREDENTIAL;
2490
if (desired_mechs == GSS_C_NO_OID_SET) {
2491
status = gss_inquire_cred(minor_status, imp_mcred, NULL, NULL,
2493
if (status != GSS_S_COMPLETE)
2496
desired_mechs = amechs;
2499
status = gss_acquire_cred_impersonate_name(minor_status, imp_mcred,
2500
desired_name, time_req,
2501
desired_mechs, cred_usage,
2502
&out_mcred, actual_mechs,
2505
if (amechs != GSS_C_NULL_OID_SET)
2506
(void) gss_release_oid_set(minor_status, &amechs);
2508
out_spcred = malloc(sizeof(spnego_gss_cred_id_rec));
2509
if (out_spcred == NULL) {
2510
gss_release_cred(minor_status, &out_mcred);
2511
*minor_status = ENOMEM;
2512
return (GSS_S_FAILURE);
2514
out_spcred->mcred = out_mcred;
2515
out_spcred->neg_mechs = GSS_C_NULL_OID_SET;
2516
*output_cred_handle = (gss_cred_id_t)out_spcred;
2518
dsyslog("Leaving spnego_gss_acquire_cred_impersonate_name\n");
2522
OM_uint32 KRB5_CALLCONV
2523
spnego_gss_acquire_cred_with_password(OM_uint32 *minor_status,
2524
const gss_name_t desired_name,
2525
const gss_buffer_t password,
2527
const gss_OID_set desired_mechs,
2528
gss_cred_usage_t cred_usage,
2529
gss_cred_id_t *output_cred_handle,
2530
gss_OID_set *actual_mechs,
2531
OM_uint32 *time_rec)
2533
OM_uint32 status, tmpmin;
2534
gss_OID_set amechs = GSS_C_NULL_OID_SET;
2535
gss_cred_id_t mcred = NULL;
2536
spnego_gss_cred_id_t spcred = NULL;
2538
dsyslog("Entering spnego_gss_acquire_cred_with_password\n");
2541
*actual_mechs = NULL;
2546
status = get_available_mechs(minor_status, desired_name,
2547
cred_usage, GSS_C_NO_CRED_STORE,
2549
if (status != GSS_S_COMPLETE)
2552
status = gss_acquire_cred_with_password(minor_status, desired_name,
2553
password, time_req, amechs,
2555
actual_mechs, time_rec);
2556
if (status != GSS_S_COMPLETE)
2559
spcred = malloc(sizeof(spnego_gss_cred_id_rec));
2560
if (spcred == NULL) {
2561
*minor_status = ENOMEM;
2562
status = GSS_S_FAILURE;
2565
spcred->neg_mechs = GSS_C_NULL_OID_SET;
2566
spcred->mcred = mcred;
2567
mcred = GSS_C_NO_CREDENTIAL;
2568
*output_cred_handle = (gss_cred_id_t)spcred;
2572
(void) gss_release_oid_set(&tmpmin, &amechs);
2573
(void) gss_release_cred(&tmpmin, &mcred);
2575
dsyslog("Leaving spnego_gss_acquire_cred_with_password\n");
2579
OM_uint32 KRB5_CALLCONV
2580
spnego_gss_display_name_ext(OM_uint32 *minor_status,
2582
gss_OID display_as_name_type,
2583
gss_buffer_t display_name)
2586
ret = gss_display_name_ext(minor_status,
2588
display_as_name_type,
2594
OM_uint32 KRB5_CALLCONV
2595
spnego_gss_inquire_name(OM_uint32 *minor_status,
2599
gss_buffer_set_t *attrs)
2602
ret = gss_inquire_name(minor_status,
2610
OM_uint32 KRB5_CALLCONV
2611
spnego_gss_get_name_attribute(OM_uint32 *minor_status,
2617
gss_buffer_t display_value,
2621
ret = gss_get_name_attribute(minor_status,
2632
OM_uint32 KRB5_CALLCONV
2633
spnego_gss_set_name_attribute(OM_uint32 *minor_status,
2640
ret = gss_set_name_attribute(minor_status,
2648
OM_uint32 KRB5_CALLCONV
2649
spnego_gss_delete_name_attribute(OM_uint32 *minor_status,
2654
ret = gss_delete_name_attribute(minor_status,
2660
OM_uint32 KRB5_CALLCONV
2661
spnego_gss_export_name_composite(OM_uint32 *minor_status,
2663
gss_buffer_t exp_composite_name)
2666
ret = gss_export_name_composite(minor_status,
2668
exp_composite_name);
2672
OM_uint32 KRB5_CALLCONV
2673
spnego_gss_map_name_to_any(OM_uint32 *minor_status,
2676
gss_buffer_t type_id,
2680
ret = gss_map_name_to_any(minor_status,
2688
OM_uint32 KRB5_CALLCONV
2689
spnego_gss_release_any_name_mapping(OM_uint32 *minor_status,
2691
gss_buffer_t type_id,
2695
ret = gss_release_any_name_mapping(minor_status,
2702
OM_uint32 KRB5_CALLCONV
2703
spnego_gss_pseudo_random(OM_uint32 *minor_status,
2704
gss_ctx_id_t context,
2706
const gss_buffer_t prf_in,
2707
ssize_t desired_output_len,
2708
gss_buffer_t prf_out)
2711
ret = gss_pseudo_random(minor_status,
2720
OM_uint32 KRB5_CALLCONV
2721
spnego_gss_set_neg_mechs(OM_uint32 *minor_status,
2722
gss_cred_id_t cred_handle,
2723
const gss_OID_set mech_list)
2726
spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)cred_handle;
2728
/* Store mech_list in spcred for use in negotiation logic. */
2729
gss_release_oid_set(minor_status, &spcred->neg_mechs);
2730
ret = generic_gss_copy_oid_set(minor_status, mech_list,
2731
&spcred->neg_mechs);
2735
#define SPNEGO_SASL_NAME "SPNEGO"
2736
#define SPNEGO_SASL_NAME_LEN (sizeof(SPNEGO_SASL_NAME) - 1)
2738
OM_uint32 KRB5_CALLCONV
2739
spnego_gss_inquire_mech_for_saslname(OM_uint32 *minor_status,
2740
const gss_buffer_t sasl_mech_name,
2743
if (sasl_mech_name->length == SPNEGO_SASL_NAME_LEN &&
2744
memcmp(sasl_mech_name->value, SPNEGO_SASL_NAME,
2745
SPNEGO_SASL_NAME_LEN) == 0) {
2746
if (mech_type != NULL)
2747
*mech_type = (gss_OID)gss_mech_spnego;
2748
return (GSS_S_COMPLETE);
2751
return (GSS_S_BAD_MECH);
2754
OM_uint32 KRB5_CALLCONV
2755
spnego_gss_inquire_saslname_for_mech(OM_uint32 *minor_status,
2756
const gss_OID desired_mech,
2757
gss_buffer_t sasl_mech_name,
2758
gss_buffer_t mech_name,
2759
gss_buffer_t mech_description)
2763
if (!g_OID_equal(desired_mech, gss_mech_spnego))
2764
return (GSS_S_BAD_MECH);
2766
if (!g_make_string_buffer(SPNEGO_SASL_NAME, sasl_mech_name) ||
2767
!g_make_string_buffer("spnego", mech_name) ||
2768
!g_make_string_buffer("Simple and Protected GSS-API "
2769
"Negotiation Mechanism", mech_description))
2772
return (GSS_S_COMPLETE);
2775
*minor_status = ENOMEM;
2776
return (GSS_S_FAILURE);
2779
OM_uint32 KRB5_CALLCONV
2780
spnego_gss_inquire_attrs_for_mech(OM_uint32 *minor_status,
2782
gss_OID_set *mech_attrs,
2783
gss_OID_set *known_mech_attrs)
2785
OM_uint32 major, tmpMinor;
2787
/* known_mech_attrs is handled by mechglue */
2790
if (mech_attrs == NULL)
2791
return (GSS_S_COMPLETE);
2793
major = gss_create_empty_oid_set(minor_status, mech_attrs);
2794
if (GSS_ERROR(major))
2797
#define MA_SUPPORTED(ma) do { \
2798
major = gss_add_oid_set_member(minor_status, \
2799
(gss_OID)ma, mech_attrs); \
2800
if (GSS_ERROR(major)) \
2804
MA_SUPPORTED(GSS_C_MA_MECH_NEGO);
2805
MA_SUPPORTED(GSS_C_MA_ITOK_FRAMED);
2808
if (GSS_ERROR(major))
2809
gss_release_oid_set(&tmpMinor, mech_attrs);
2814
OM_uint32 KRB5_CALLCONV
2815
spnego_gss_export_cred(OM_uint32 *minor_status,
2816
gss_cred_id_t cred_handle,
2819
spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)cred_handle;
2821
return (gss_export_cred(minor_status, spcred->mcred, token));
2824
OM_uint32 KRB5_CALLCONV
2825
spnego_gss_import_cred(OM_uint32 *minor_status,
2827
gss_cred_id_t *cred_handle)
2830
spnego_gss_cred_id_t spcred;
2831
gss_cred_id_t mcred;
2833
ret = gss_import_cred(minor_status, token, &mcred);
2836
spcred = malloc(sizeof(*spcred));
2837
if (spcred == NULL) {
2838
gss_release_cred(minor_status, &mcred);
2839
*minor_status = ENOMEM;
2840
return (GSS_S_FAILURE);
2842
spcred->mcred = mcred;
2843
spcred->neg_mechs = GSS_C_NULL_OID_SET;
2844
*cred_handle = (gss_cred_id_t)spcred;
2848
OM_uint32 KRB5_CALLCONV
2849
spnego_gss_get_mic_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
2850
gss_qop_t qop_req, gss_iov_buffer_desc *iov,
2853
return gss_get_mic_iov(minor_status, context_handle, qop_req, iov,
2857
OM_uint32 KRB5_CALLCONV
2858
spnego_gss_verify_mic_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
2859
gss_qop_t *qop_state, gss_iov_buffer_desc *iov,
2862
return gss_verify_mic_iov(minor_status, context_handle, qop_state, iov,
2866
OM_uint32 KRB5_CALLCONV
2867
spnego_gss_get_mic_iov_length(OM_uint32 *minor_status,
2868
gss_ctx_id_t context_handle, gss_qop_t qop_req,
2869
gss_iov_buffer_desc *iov, int iov_count)
2871
return gss_get_mic_iov_length(minor_status, context_handle, qop_req, iov,
2876
* We will release everything but the ctx_handle so that it
2877
* can be passed back to init/accept context. This routine should
2878
* not be called until after the ctx_handle memory is assigned to
2879
* the supplied context handle from init/accept context.
2882
release_spnego_ctx(spnego_gss_ctx_id_t *ctx)
2884
spnego_gss_ctx_id_t context;
2885
OM_uint32 minor_stat;
2888
if (context != NULL) {
2889
(void) gss_release_buffer(&minor_stat,
2890
&context->DER_mechTypes);
2892
(void) gss_release_oid_set(&minor_stat, &context->mech_set);
2894
(void) gss_release_name(&minor_stat, &context->internal_name);
2896
if (context->optionStr != NULL) {
2897
free(context->optionStr);
2898
context->optionStr = NULL;
2906
* Can't use gss_indicate_mechs by itself to get available mechs for
2907
* SPNEGO because it will also return the SPNEGO mech and we do not
2908
* want to consider SPNEGO as an available security mech for
2909
* negotiation. For this reason, get_available_mechs will return
2910
* all available mechs except SPNEGO.
2912
* If a ptr to a creds list is given, this function will attempt
2913
* to acquire creds for the creds given and trim the list of
2914
* returned mechanisms to only those for which creds are valid.
2918
get_available_mechs(OM_uint32 *minor_status,
2919
gss_name_t name, gss_cred_usage_t usage,
2920
gss_const_key_value_set_t cred_store,
2921
gss_cred_id_t *creds, gss_OID_set *rmechs)
2925
OM_uint32 major_status = GSS_S_COMPLETE, tmpmin;
2926
gss_OID_set mechs, goodmechs;
2928
major_status = gss_indicate_mechs(minor_status, &mechs);
2930
if (major_status != GSS_S_COMPLETE) {
2931
return (major_status);
2934
major_status = gss_create_empty_oid_set(minor_status, rmechs);
2936
if (major_status != GSS_S_COMPLETE) {
2937
(void) gss_release_oid_set(minor_status, &mechs);
2938
return (major_status);
2941
for (i = 0; i < mechs->count && major_status == GSS_S_COMPLETE; i++) {
2942
if ((mechs->elements[i].length
2943
!= spnego_mechanism.mech_type.length) ||
2944
memcmp(mechs->elements[i].elements,
2945
spnego_mechanism.mech_type.elements,
2946
spnego_mechanism.mech_type.length)) {
2948
major_status = gss_add_oid_set_member(minor_status,
2949
&mechs->elements[i],
2951
if (major_status == GSS_S_COMPLETE)
2957
* If the caller wanted a list of creds returned,
2958
* trim the list of mechanisms down to only those
2959
* for which the creds are valid.
2961
if (found > 0 && major_status == GSS_S_COMPLETE && creds != NULL) {
2962
major_status = gss_acquire_cred_from(minor_status, name,
2969
* Drop the old list in favor of the new
2972
(void) gss_release_oid_set(&tmpmin, rmechs);
2973
if (major_status == GSS_S_COMPLETE) {
2974
(void) gssint_copy_oid_set(&tmpmin,
2976
(void) gss_release_oid_set(&tmpmin, &goodmechs);
2980
(void) gss_release_oid_set(&tmpmin, &mechs);
2981
if (found == 0 || major_status != GSS_S_COMPLETE) {
2982
*minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE;
2983
map_errcode(minor_status);
2984
if (major_status == GSS_S_COMPLETE)
2985
major_status = GSS_S_FAILURE;
2988
return (major_status);
2992
* Return a list of mechanisms we are willing to negotiate for a credential,
2993
* taking into account the mech set provided with gss_set_neg_mechs if it
2997
get_negotiable_mechs(OM_uint32 *minor_status, spnego_gss_cred_id_t spcred,
2998
gss_cred_usage_t usage, gss_OID_set *rmechs)
3000
OM_uint32 ret, tmpmin;
3001
gss_cred_id_t creds = GSS_C_NO_CREDENTIAL, *credptr;
3002
gss_OID_set cred_mechs = GSS_C_NULL_OID_SET;
3003
gss_OID_set intersect_mechs = GSS_C_NULL_OID_SET;
3007
if (spcred == NULL) {
3009
* The default credentials were supplied. Return a list of all
3010
* available mechs except SPNEGO. When initiating, trim this
3011
* list to mechs we can acquire credentials for.
3013
credptr = (usage == GSS_C_INITIATE) ? &creds : NULL;
3014
ret = get_available_mechs(minor_status, GSS_C_NO_NAME, usage,
3015
GSS_C_NO_CRED_STORE, credptr,
3017
gss_release_cred(&tmpmin, &creds);
3021
/* Get the list of mechs in the mechglue cred. */
3022
ret = gss_inquire_cred(minor_status, spcred->mcred, NULL, NULL, NULL,
3024
if (ret != GSS_S_COMPLETE)
3027
if (spcred->neg_mechs == GSS_C_NULL_OID_SET) {
3028
/* gss_set_neg_mechs was never called; return cred_mechs. */
3029
*rmechs = cred_mechs;
3031
return (GSS_S_COMPLETE);
3034
/* Compute the intersection of cred_mechs and spcred->neg_mechs,
3035
* preserving the order in spcred->neg_mechs. */
3036
ret = gss_create_empty_oid_set(minor_status, &intersect_mechs);
3037
if (ret != GSS_S_COMPLETE) {
3038
gss_release_oid_set(&tmpmin, &cred_mechs);
3042
for (i = 0; i < spcred->neg_mechs->count; i++) {
3043
gss_test_oid_set_member(&tmpmin,
3044
&spcred->neg_mechs->elements[i],
3045
cred_mechs, &present);
3048
ret = gss_add_oid_set_member(minor_status,
3049
&spcred->neg_mechs->elements[i],
3051
if (ret != GSS_S_COMPLETE)
3055
gss_release_oid_set(&tmpmin, &cred_mechs);
3056
if (intersect_mechs->count == 0 || ret != GSS_S_COMPLETE) {
3057
gss_release_oid_set(&tmpmin, &intersect_mechs);
3058
*minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE;
3059
map_errcode(minor_status);
3060
return (GSS_S_FAILURE);
3063
*rmechs = intersect_mechs;
3065
return (GSS_S_COMPLETE);
3068
/* following are token creation and reading routines */
3071
* If buff_in is not pointing to a MECH_OID, then return NULL and do not
3072
* advance the buffer, otherwise, decode the mech_oid from the buffer and
3076
get_mech_oid(OM_uint32 *minor_status, unsigned char **buff_in, size_t length)
3080
gss_OID mech_out = NULL;
3081
unsigned char *start, *end;
3083
if (length < 1 || **buff_in != MECH_OID)
3087
end = start + length;
3090
toid.length = *(*buff_in)++;
3092
if ((*buff_in + toid.length) > end)
3095
toid.elements = *buff_in;
3096
*buff_in += toid.length;
3098
status = generic_gss_copy_oid(minor_status, &toid, &mech_out);
3100
if (status != GSS_S_COMPLETE) {
3101
map_errcode(minor_status);
3109
* der encode the given mechanism oid into buf_out, advancing the
3114
put_mech_oid(unsigned char **buf_out, gss_OID_const mech, unsigned int buflen)
3116
if (buflen < mech->length + 2)
3118
*(*buf_out)++ = MECH_OID;
3119
*(*buf_out)++ = (unsigned char) mech->length;
3120
memcpy(*buf_out, mech->elements, mech->length);
3121
*buf_out += mech->length;
3126
* verify that buff_in points to an octet string, if it does not,
3127
* return NULL and don't advance the pointer. If it is an octet string
3128
* decode buff_in into a gss_buffer_t and return it, advancing the
3132
get_input_token(unsigned char **buff_in, unsigned int buff_length)
3134
gss_buffer_t input_token;
3137
if (g_get_tag_and_length(buff_in, OCTET_STRING, buff_length, &len) < 0)
3140
input_token = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
3141
if (input_token == NULL)
3144
input_token->length = len;
3145
input_token->value = gssalloc_malloc(input_token->length);
3147
if (input_token->value == NULL) {
3152
(void) memcpy(input_token->value, *buff_in, input_token->length);
3153
*buff_in += input_token->length;
3154
return (input_token);
3158
* verify that the input token length is not 0. If it is, just return.
3159
* If the token length is greater than 0, der encode as an octet string
3160
* and place in buf_out, advancing buf_out.
3164
put_input_token(unsigned char **buf_out, gss_buffer_t input_token,
3165
unsigned int buflen)
3169
/* if token length is 0, we do not want to send */
3170
if (input_token->length == 0)
3173
if (input_token->length > buflen)
3176
*(*buf_out)++ = OCTET_STRING;
3177
if ((ret = gssint_put_der_length(input_token->length, buf_out,
3178
input_token->length)))
3180
TWRITE_STR(*buf_out, input_token->value, input_token->length);
3185
* verify that buff_in points to a sequence of der encoding. The mech
3186
* set is the only sequence of encoded object in the token, so if it is
3187
* a sequence of encoding, decode the mechset into a gss_OID_set and
3188
* return it, advancing the buffer pointer.
3191
get_mech_set(OM_uint32 *minor_status, unsigned char **buff_in,
3192
unsigned int buff_length)
3194
gss_OID_set returned_mechSet;
3195
OM_uint32 major_status;
3198
OM_uint32 set_length;
3199
unsigned char *start;
3202
if (**buff_in != SEQUENCE_OF)
3208
length = gssint_get_der_length(buff_in, buff_length, &bytes);
3209
if (length < 0 || buff_length - bytes < (unsigned int)length)
3212
major_status = gss_create_empty_oid_set(minor_status,
3214
if (major_status != GSS_S_COMPLETE)
3217
for (set_length = 0, i = 0; set_length < (unsigned int)length; i++) {
3218
gss_OID_desc *temp = get_mech_oid(minor_status, buff_in,
3219
buff_length - (*buff_in - start));
3223
major_status = gss_add_oid_set_member(minor_status,
3224
temp, &returned_mechSet);
3225
if (major_status == GSS_S_COMPLETE) {
3226
set_length += returned_mechSet->elements[i].length +2;
3227
if (generic_gss_release_oid(minor_status, &temp))
3228
map_errcode(minor_status);
3232
return (returned_mechSet);
3236
* Encode mechSet into buf.
3239
put_mech_set(gss_OID_set mechSet, gss_buffer_t buf)
3243
unsigned int tlen, ilen;
3246
for (i = 0; i < mechSet->count; i++) {
3248
* 0x06 [DER LEN] [OID]
3251
gssint_der_length_size(mechSet->elements[i].length) +
3252
mechSet->elements[i].length;
3257
tlen = 1 + gssint_der_length_size(ilen) + ilen;
3258
ptr = gssalloc_malloc(tlen);
3264
#define REMAIN (buf->length - ((unsigned char *)buf->value - ptr))
3266
*ptr++ = SEQUENCE_OF;
3267
if (gssint_put_der_length(ilen, &ptr, REMAIN) < 0)
3269
for (i = 0; i < mechSet->count; i++) {
3270
if (put_mech_oid(&ptr, &mechSet->elements[i], REMAIN) < 0) {
3279
* Verify that buff_in is pointing to a BIT_STRING with the correct
3280
* length and padding for the req_flags. If it is, decode req_flags
3281
* and return them, otherwise, return NULL.
3284
get_req_flags(unsigned char **buff_in, OM_uint32 bodysize,
3285
OM_uint32 *req_flags)
3289
if (**buff_in != (CONTEXT | 0x01))
3292
if (g_get_tag_and_length(buff_in, (CONTEXT | 0x01),
3293
bodysize, &len) < 0)
3294
return GSS_S_DEFECTIVE_TOKEN;
3296
if (*(*buff_in)++ != BIT_STRING)
3297
return GSS_S_DEFECTIVE_TOKEN;
3299
if (*(*buff_in)++ != BIT_STRING_LENGTH)
3300
return GSS_S_DEFECTIVE_TOKEN;
3302
if (*(*buff_in)++ != BIT_STRING_PADDING)
3303
return GSS_S_DEFECTIVE_TOKEN;
3305
*req_flags = (OM_uint32) (*(*buff_in)++ >> 1);
3310
get_negTokenInit(OM_uint32 *minor_status,
3312
gss_buffer_t der_mechSet,
3313
gss_OID_set *mechSet,
3314
OM_uint32 *req_flags,
3315
gss_buffer_t *mechtok,
3316
gss_buffer_t *mechListMIC)
3319
unsigned char *ptr, *bufstart;
3321
gss_buffer_desc tmpbuf;
3324
der_mechSet->length = 0;
3325
der_mechSet->value = NULL;
3326
*mechSet = GSS_C_NO_OID_SET;
3328
*mechtok = *mechListMIC = GSS_C_NO_BUFFER;
3330
ptr = bufstart = buf->value;
3331
if ((buf->length - (ptr - bufstart)) > INT_MAX)
3332
return GSS_S_FAILURE;
3333
#define REMAIN (buf->length - (ptr - bufstart))
3335
err = g_verify_token_header(gss_mech_spnego,
3336
&len, &ptr, 0, REMAIN);
3338
*minor_status = err;
3339
map_errcode(minor_status);
3340
return GSS_S_FAILURE;
3342
*minor_status = g_verify_neg_token_init(&ptr, REMAIN);
3343
if (*minor_status) {
3344
map_errcode(minor_status);
3345
return GSS_S_FAILURE;
3348
/* alias into input_token */
3350
tmpbuf.length = REMAIN;
3351
*mechSet = get_mech_set(minor_status, &ptr, REMAIN);
3352
if (*mechSet == NULL)
3353
return GSS_S_FAILURE;
3355
tmpbuf.length = ptr - (unsigned char *)tmpbuf.value;
3356
der_mechSet->value = gssalloc_malloc(tmpbuf.length);
3357
if (der_mechSet->value == NULL)
3358
return GSS_S_FAILURE;
3359
memcpy(der_mechSet->value, tmpbuf.value, tmpbuf.length);
3360
der_mechSet->length = tmpbuf.length;
3362
err = get_req_flags(&ptr, REMAIN, req_flags);
3363
if (err != GSS_S_COMPLETE) {
3366
if (g_get_tag_and_length(&ptr, (CONTEXT | 0x02),
3367
REMAIN, &len) >= 0) {
3368
*mechtok = get_input_token(&ptr, len);
3369
if (*mechtok == GSS_C_NO_BUFFER) {
3370
return GSS_S_FAILURE;
3373
if (g_get_tag_and_length(&ptr, (CONTEXT | 0x03),
3374
REMAIN, &len) >= 0) {
3375
*mechListMIC = get_input_token(&ptr, len);
3376
if (*mechListMIC == GSS_C_NO_BUFFER) {
3377
return GSS_S_FAILURE;
3380
return GSS_S_COMPLETE;
3385
get_negTokenResp(OM_uint32 *minor_status,
3386
unsigned char *buf, unsigned int buflen,
3387
OM_uint32 *negState,
3388
gss_OID *supportedMech,
3389
gss_buffer_t *responseToken,
3390
gss_buffer_t *mechListMIC)
3392
unsigned char *ptr, *bufstart;
3395
unsigned int tag, bytes;
3397
*negState = ACCEPT_DEFECTIVE_TOKEN;
3398
*supportedMech = GSS_C_NO_OID;
3399
*responseToken = *mechListMIC = GSS_C_NO_BUFFER;
3400
ptr = bufstart = buf;
3401
#define REMAIN (buflen - (ptr - bufstart))
3403
if (g_get_tag_and_length(&ptr, (CONTEXT | 0x01), REMAIN, &len) < 0)
3404
return GSS_S_DEFECTIVE_TOKEN;
3405
if (*ptr++ == SEQUENCE) {
3406
tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3407
if (tmplen < 0 || REMAIN < (unsigned int)tmplen)
3408
return GSS_S_DEFECTIVE_TOKEN;
3415
if (tag == CONTEXT) {
3416
tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3417
if (tmplen < 0 || REMAIN < (unsigned int)tmplen)
3418
return GSS_S_DEFECTIVE_TOKEN;
3420
if (g_get_tag_and_length(&ptr, ENUMERATED,
3422
return GSS_S_DEFECTIVE_TOKEN;
3424
if (len != ENUMERATION_LENGTH)
3425
return GSS_S_DEFECTIVE_TOKEN;
3428
return GSS_S_DEFECTIVE_TOKEN;
3436
if (tag == (CONTEXT | 0x01)) {
3437
tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3438
if (tmplen < 0 || REMAIN < (unsigned int)tmplen)
3439
return GSS_S_DEFECTIVE_TOKEN;
3441
*supportedMech = get_mech_oid(minor_status, &ptr, REMAIN);
3442
if (*supportedMech == GSS_C_NO_OID)
3443
return GSS_S_DEFECTIVE_TOKEN;
3450
if (tag == (CONTEXT | 0x02)) {
3451
tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3452
if (tmplen < 0 || REMAIN < (unsigned int)tmplen)
3453
return GSS_S_DEFECTIVE_TOKEN;
3455
*responseToken = get_input_token(&ptr, REMAIN);
3456
if (*responseToken == GSS_C_NO_BUFFER)
3457
return GSS_S_DEFECTIVE_TOKEN;
3464
if (tag == (CONTEXT | 0x03)) {
3465
tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3466
if (tmplen < 0 || REMAIN < (unsigned int)tmplen)
3467
return GSS_S_DEFECTIVE_TOKEN;
3469
*mechListMIC = get_input_token(&ptr, REMAIN);
3470
if (*mechListMIC == GSS_C_NO_BUFFER)
3471
return GSS_S_DEFECTIVE_TOKEN;
3473
/* Handle Windows 2000 duplicate response token */
3474
if (*responseToken &&
3475
((*responseToken)->length == (*mechListMIC)->length) &&
3476
!memcmp((*responseToken)->value, (*mechListMIC)->value,
3477
(*responseToken)->length)) {
3480
gss_release_buffer(&tmpmin, *mechListMIC);
3482
*mechListMIC = NULL;
3485
return GSS_S_COMPLETE;
3490
* der encode the passed negResults as an ENUMERATED type and
3491
* place it in buf_out, advancing the buffer.
3495
put_negResult(unsigned char **buf_out, OM_uint32 negResult,
3496
unsigned int buflen)
3500
*(*buf_out)++ = ENUMERATED;
3501
*(*buf_out)++ = ENUMERATION_LENGTH;
3502
*(*buf_out)++ = (unsigned char) negResult;
3507
* This routine compares the recieved mechset to the mechset that
3508
* this server can support. It looks sequentially through the mechset
3509
* and the first one that matches what the server can support is
3510
* chosen as the negotiated mechanism. If one is found, negResult
3511
* is set to ACCEPT_INCOMPLETE if it's the first mech, REQUEST_MIC if
3512
* it's not the first mech, otherwise we return NULL and negResult
3513
* is set to REJECT. The returned pointer is an alias into
3514
* supported->elements and should not be freed.
3516
* NOTE: There is currently no way to specify a preference order of
3517
* mechanisms supported by the acceptor.
3520
negotiate_mech(gss_OID_set supported, gss_OID_set received,
3521
OM_uint32 *negResult)
3525
for (i = 0; i < received->count; i++) {
3526
gss_OID mech_oid = &received->elements[i];
3528
/* Accept wrong mechanism OID from MS clients */
3529
if (g_OID_equal(mech_oid, &gss_mech_krb5_wrong_oid))
3530
mech_oid = (gss_OID)&gss_mech_krb5_oid;
3532
for (j = 0; j < supported->count; j++) {
3533
if (g_OID_equal(mech_oid, &supported->elements[j])) {
3534
*negResult = (i == 0) ? ACCEPT_INCOMPLETE :
3536
return &supported->elements[j];
3540
*negResult = REJECT;
3545
* the next two routines make a token buffer suitable for
3546
* spnego_gss_display_status. These currently take the string
3547
* in name and place it in the token. Eventually, if
3548
* spnego_gss_display_status returns valid error messages,
3549
* these routines will be changes to return the error string.
3551
static spnego_token_t
3552
make_spnego_token(char *name)
3554
return (spnego_token_t)strdup(name);
3557
static gss_buffer_desc
3558
make_err_msg(char *name)
3560
gss_buffer_desc buffer;
3564
buffer.value = NULL;
3566
buffer.length = strlen(name)+1;
3567
buffer.value = make_spnego_token(name);
3574
* Create the client side spnego token passed back to gss_init_sec_context
3575
* and eventually up to the application program and over to the server.
3577
* Use DER rules, definite length method per RFC 2478
3580
make_spnego_tokenInit_msg(spnego_gss_ctx_id_t spnego_ctx,
3582
gss_buffer_t mechListMIC, OM_uint32 req_flags,
3583
gss_buffer_t data, send_token_flag sendtoken,
3584
gss_buffer_t outbuf)
3587
unsigned int tlen, dataLen = 0;
3588
unsigned int negTokenInitSize = 0;
3589
unsigned int negTokenInitSeqSize = 0;
3590
unsigned int negTokenInitContSize = 0;
3591
unsigned int rspTokenSize = 0;
3592
unsigned int mechListTokenSize = 0;
3593
unsigned int micTokenSize = 0;
3597
if (outbuf == GSS_C_NO_BUFFER)
3601
outbuf->value = NULL;
3603
/* calculate the data length */
3606
* 0xa0 [DER LEN] [mechTypes]
3608
mechListTokenSize = 1 +
3609
gssint_der_length_size(spnego_ctx->DER_mechTypes.length) +
3610
spnego_ctx->DER_mechTypes.length;
3611
dataLen += mechListTokenSize;
3614
* If a token from gss_init_sec_context exists,
3615
* add the length of the token + the ASN.1 overhead
3619
* Encoded in final output as:
3620
* 0xa2 [DER LEN] 0x04 [DER LEN] [DATA]
3621
* -----s--------|--------s2----------
3624
gssint_der_length_size(data->length) +
3626
dataLen += 1 + gssint_der_length_size(rspTokenSize) +
3632
* Encoded in final output as:
3633
* 0xa3 [DER LEN] 0x04 [DER LEN] [DATA]
3634
* --s-- -----tlen------------
3637
gssint_der_length_size(mechListMIC->length) +
3638
mechListMIC->length;
3640
gssint_der_length_size(micTokenSize) +
3645
* Add size of DER encoding
3646
* [ SEQUENCE { MechTypeList | ReqFLags | Token | mechListMIC } ]
3647
* 0x30 [DER_LEN] [data]
3650
negTokenInitContSize = dataLen;
3651
negTokenInitSeqSize = 1 + gssint_der_length_size(dataLen) + dataLen;
3652
dataLen = negTokenInitSeqSize;
3655
* negTokenInitSize indicates the bytes needed to
3656
* hold the ASN.1 encoding of the entire NegTokenInit
3658
* 0xa0 [DER_LEN] + data
3661
negTokenInitSize = 1 +
3662
gssint_der_length_size(negTokenInitSeqSize) +
3663
negTokenInitSeqSize;
3665
tlen = g_token_size(gss_mech_spnego, negTokenInitSize);
3667
t = (unsigned char *) gssalloc_malloc(tlen);
3675
/* create the message */
3676
if ((ret = g_make_token_header(gss_mech_spnego, negTokenInitSize,
3680
*ptr++ = CONTEXT; /* NegotiationToken identifier */
3681
if ((ret = gssint_put_der_length(negTokenInitSeqSize, &ptr, tlen)))
3685
if ((ret = gssint_put_der_length(negTokenInitContSize, &ptr,
3686
tlen - (int)(ptr-t))))
3689
*ptr++ = CONTEXT | 0x00; /* MechTypeList identifier */
3690
if ((ret = gssint_put_der_length(spnego_ctx->DER_mechTypes.length,
3691
&ptr, tlen - (int)(ptr-t))))
3694
/* We already encoded the MechSetList */
3695
(void) memcpy(ptr, spnego_ctx->DER_mechTypes.value,
3696
spnego_ctx->DER_mechTypes.length);
3698
ptr += spnego_ctx->DER_mechTypes.length;
3701
*ptr++ = CONTEXT | 0x02;
3702
if ((ret = gssint_put_der_length(rspTokenSize,
3703
&ptr, tlen - (int)(ptr - t))))
3706
if ((ret = put_input_token(&ptr, data,
3707
tlen - (int)(ptr - t))))
3711
if (mechListMIC != GSS_C_NO_BUFFER) {
3712
*ptr++ = CONTEXT | 0x03;
3713
if ((ret = gssint_put_der_length(micTokenSize,
3714
&ptr, tlen - (int)(ptr - t))))
3717
if (negHintsCompat) {
3718
ret = put_neg_hints(&ptr, mechListMIC,
3719
tlen - (int)(ptr - t));
3722
} else if ((ret = put_input_token(&ptr, mechListMIC,
3723
tlen - (int)(ptr - t))))
3734
outbuf->length = tlen;
3735
outbuf->value = (void *) t;
3741
* create the server side spnego token passed back to
3742
* gss_accept_sec_context and eventually up to the application program
3743
* and over to the client.
3746
make_spnego_tokenTarg_msg(OM_uint32 status, gss_OID mech_wanted,
3747
gss_buffer_t data, gss_buffer_t mechListMIC,
3748
send_token_flag sendtoken,
3749
gss_buffer_t outbuf)
3751
unsigned int tlen = 0;
3752
unsigned int ret = 0;
3753
unsigned int NegTokenTargSize = 0;
3754
unsigned int NegTokenSize = 0;
3755
unsigned int rspTokenSize = 0;
3756
unsigned int micTokenSize = 0;
3757
unsigned int dataLen = 0;
3761
if (outbuf == GSS_C_NO_BUFFER)
3762
return (GSS_S_DEFECTIVE_TOKEN);
3763
if (sendtoken == INIT_TOKEN_SEND && mech_wanted == GSS_C_NO_OID)
3764
return (GSS_S_DEFECTIVE_TOKEN);
3767
outbuf->value = NULL;
3770
* ASN.1 encoding of the negResult
3771
* ENUMERATED type is 3 bytes
3772
* ENUMERATED TAG, Length, Value,
3773
* Plus 2 bytes for the CONTEXT id and length.
3778
* calculate data length
3780
* If this is the initial token, include length of
3781
* mech_type and the negotiation result fields.
3783
if (sendtoken == INIT_TOKEN_SEND) {
3784
int mechlistTokenSize;
3786
* 1 byte for the CONTEXT ID(0xa0),
3787
* 1 byte for the OID ID(0x06)
3788
* 1 byte for OID Length field
3789
* Plus the rest... (OID Length, OID value)
3791
mechlistTokenSize = 3 + mech_wanted->length +
3792
gssint_der_length_size(mech_wanted->length);
3794
dataLen += mechlistTokenSize;
3796
if (data != NULL && data->length > 0) {
3797
/* Length of the inner token */
3798
rspTokenSize = 1 + gssint_der_length_size(data->length) +
3801
dataLen += rspTokenSize;
3803
/* Length of the outer token */
3804
dataLen += 1 + gssint_der_length_size(rspTokenSize);
3806
if (mechListMIC != NULL) {
3808
/* Length of the inner token */
3809
micTokenSize = 1 + gssint_der_length_size(mechListMIC->length) +
3810
mechListMIC->length;
3812
dataLen += micTokenSize;
3814
/* Length of the outer token */
3815
dataLen += 1 + gssint_der_length_size(micTokenSize);
3818
* Add size of DER encoded:
3819
* NegTokenTarg [ SEQUENCE ] of
3820
* NegResult[0] ENUMERATED {
3821
* accept_completed(0),
3822
* accept_incomplete(1),
3824
* supportedMech [1] MechType OPTIONAL,
3825
* responseToken [2] OCTET STRING OPTIONAL,
3826
* mechListMIC [3] OCTET STRING OPTIONAL
3828
* size = data->length + MechListMic + SupportedMech len +
3829
* Result Length + ASN.1 overhead
3831
NegTokenTargSize = dataLen;
3832
dataLen += 1 + gssint_der_length_size(NegTokenTargSize);
3835
* NegotiationToken [ CHOICE ]{
3836
* negTokenInit [0] NegTokenInit,
3837
* negTokenTarg [1] NegTokenTarg }
3839
NegTokenSize = dataLen;
3840
dataLen += 1 + gssint_der_length_size(NegTokenSize);
3843
t = (unsigned char *) gssalloc_malloc(tlen);
3846
ret = GSS_S_DEFECTIVE_TOKEN;
3853
* Indicate that we are sending CHOICE 1
3856
*ptr++ = CONTEXT | 0x01;
3857
if (gssint_put_der_length(NegTokenSize, &ptr, dataLen) < 0) {
3858
ret = GSS_S_DEFECTIVE_TOKEN;
3862
if (gssint_put_der_length(NegTokenTargSize, &ptr,
3863
tlen - (int)(ptr-t)) < 0) {
3864
ret = GSS_S_DEFECTIVE_TOKEN;
3869
* First field of the NegTokenTarg SEQUENCE
3870
* is the ENUMERATED NegResult.
3873
if (gssint_put_der_length(3, &ptr,
3874
tlen - (int)(ptr-t)) < 0) {
3875
ret = GSS_S_DEFECTIVE_TOKEN;
3878
if (put_negResult(&ptr, status, tlen - (int)(ptr - t)) < 0) {
3879
ret = GSS_S_DEFECTIVE_TOKEN;
3882
if (sendtoken == INIT_TOKEN_SEND) {
3884
* Next, is the Supported MechType
3886
*ptr++ = CONTEXT | 0x01;
3887
if (gssint_put_der_length(mech_wanted->length + 2,
3889
tlen - (int)(ptr - t)) < 0) {
3890
ret = GSS_S_DEFECTIVE_TOKEN;
3893
if (put_mech_oid(&ptr, mech_wanted,
3894
tlen - (int)(ptr - t)) < 0) {
3895
ret = GSS_S_DEFECTIVE_TOKEN;
3899
if (data != NULL && data->length > 0) {
3900
*ptr++ = CONTEXT | 0x02;
3901
if (gssint_put_der_length(rspTokenSize, &ptr,
3902
tlen - (int)(ptr - t)) < 0) {
3903
ret = GSS_S_DEFECTIVE_TOKEN;
3906
if (put_input_token(&ptr, data,
3907
tlen - (int)(ptr - t)) < 0) {
3908
ret = GSS_S_DEFECTIVE_TOKEN;
3912
if (mechListMIC != NULL) {
3913
*ptr++ = CONTEXT | 0x03;
3914
if (gssint_put_der_length(micTokenSize, &ptr,
3915
tlen - (int)(ptr - t)) < 0) {
3916
ret = GSS_S_DEFECTIVE_TOKEN;
3919
if (put_input_token(&ptr, mechListMIC,
3920
tlen - (int)(ptr - t)) < 0) {
3921
ret = GSS_S_DEFECTIVE_TOKEN;
3925
ret = GSS_S_COMPLETE;
3927
if (ret != GSS_S_COMPLETE) {
3931
outbuf->length = ptr - t;
3932
outbuf->value = (void *) t;
3938
/* determine size of token */
3940
g_token_size(gss_OID_const mech, unsigned int body_size)
3945
* Initialize the header size to the
3946
* MECH_OID byte + the bytes needed to indicate the
3947
* length of the OID + the OID itself.
3949
* 0x06 [MECHLENFIELD] MECHDATA
3951
hdrsize = 1 + gssint_der_length_size(mech->length) + mech->length;
3954
* Now add the bytes needed for the initial header
3956
* 0x60 + [DER_LEN] + HDRSIZE
3958
hdrsize += 1 + gssint_der_length_size(body_size + hdrsize);
3960
return (hdrsize + body_size);
3964
* generate token header.
3966
* Use DER Definite Length method per RFC2478
3967
* Use of indefinite length encoding will not be compatible
3968
* with Microsoft or others that actually follow the spec.
3971
g_make_token_header(gss_OID_const mech,
3972
unsigned int body_size,
3973
unsigned char **buf,
3974
unsigned int totallen)
3977
unsigned int hdrsize;
3978
unsigned char *p = *buf;
3980
hdrsize = 1 + gssint_der_length_size(mech->length) + mech->length;
3982
*(*buf)++ = HEADER_ID;
3983
if ((ret = gssint_put_der_length(hdrsize + body_size, buf, totallen)))
3986
*(*buf)++ = MECH_OID;
3987
if ((ret = gssint_put_der_length(mech->length, buf,
3988
totallen - (int)(p - *buf))))
3990
TWRITE_STR(*buf, mech->elements, mech->length);
3995
* NOTE: This checks that the length returned by
3996
* gssint_get_der_length() is not greater than the number of octets
3997
* remaining, even though gssint_get_der_length() already checks, in
4001
g_get_tag_and_length(unsigned char **buf, int tag,
4002
unsigned int buflen, unsigned int *outlen)
4004
unsigned char *ptr = *buf;
4005
int ret = -1; /* pessimists, assume failure ! */
4006
unsigned int encoded_len;
4010
if (buflen > 1 && *ptr == tag) {
4012
tmplen = gssint_get_der_length(&ptr, buflen - 1,
4016
} else if ((unsigned int)tmplen > buflen - (ptr - *buf)) {
4027
g_verify_neg_token_init(unsigned char **buf_in, unsigned int cur_size)
4029
unsigned char *buf = *buf_in;
4030
unsigned char *endptr = buf + cur_size;
4036
* Verify this is a NegotiationToken type token
4037
* - check for a0(context specific identifier)
4038
* - get length and verify that enoughd ata exists
4040
if (g_get_tag_and_length(&buf, CONTEXT, cur_size, &bytes) < 0)
4041
return (G_BAD_TOK_HEADER);
4043
cur_size = bytes; /* should indicate bytes remaining */
4046
* Verify the next piece, it should identify this as
4047
* a strucure of type NegTokenInit.
4049
if (*buf++ == SEQUENCE) {
4050
if ((seqsize = gssint_get_der_length(&buf, cur_size, &bytes)) < 0)
4051
return (G_BAD_TOK_HEADER);
4053
* Make sure we have the entire buffer as described
4055
if (seqsize > endptr - buf)
4056
return (G_BAD_TOK_HEADER);
4058
return (G_BAD_TOK_HEADER);
4061
cur_size = seqsize; /* should indicate bytes remaining */
4064
* Verify that the first blob is a sequence of mechTypes
4066
if (*buf++ == CONTEXT) {
4067
if ((seqsize = gssint_get_der_length(&buf, cur_size, &bytes)) < 0)
4068
return (G_BAD_TOK_HEADER);
4070
* Make sure we have the entire buffer as described
4072
if (seqsize > endptr - buf)
4073
return (G_BAD_TOK_HEADER);
4075
return (G_BAD_TOK_HEADER);
4079
* At this point, *buf should be at the beginning of the
4080
* DER encoded list of mech types that are to be negotiated.
4088
/* verify token header. */
4090
g_verify_token_header(gss_OID_const mech,
4091
unsigned int *body_size,
4092
unsigned char **buf_in,
4094
unsigned int toksize)
4096
unsigned char *buf = *buf_in;
4103
return (G_BAD_TOK_HEADER);
4105
if (*buf++ != HEADER_ID)
4106
return (G_BAD_TOK_HEADER);
4108
if ((seqsize = gssint_get_der_length(&buf, toksize, &bytes)) < 0)
4109
return (G_BAD_TOK_HEADER);
4111
if ((seqsize + bytes) != toksize)
4112
return (G_BAD_TOK_HEADER);
4115
return (G_BAD_TOK_HEADER);
4118
if (*buf++ != MECH_OID)
4119
return (G_BAD_TOK_HEADER);
4122
return (G_BAD_TOK_HEADER);
4124
toid.length = *buf++;
4126
if (toksize < toid.length)
4127
return (G_BAD_TOK_HEADER);
4129
toksize -= toid.length;
4131
toid.elements = buf;
4134
if (!g_OID_equal(&toid, mech))
4138
* G_WRONG_MECH is not returned immediately because it's more important
4139
* to return G_BAD_TOK_HEADER if the token header is in fact bad
4142
return (G_BAD_TOK_HEADER);
4148
*body_size = toksize;
4155
* Return non-zero if the oid is one of the kerberos mech oids,
4156
* otherwise return zero.
4158
* N.B. There are 3 oids that represent the kerberos mech:
4159
* RFC-specified GSS_MECH_KRB5_OID,
4160
* Old pre-RFC GSS_MECH_KRB5_OLD_OID,
4161
* Incorrect MS GSS_MECH_KRB5_WRONG_OID
4165
is_kerb_mech(gss_OID oid)
4169
extern const gss_OID_set_desc * const gss_mech_set_krb5_both;
4171
(void) gss_test_oid_set_member(&minor,
4172
oid, (gss_OID_set)gss_mech_set_krb5_both, &answer);