Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/krb5/src/lib/gssapi/spnego/negoex_ctx.c
39563 views
1
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
/*
3
* Copyright (C) 2011-2018 PADL Software Pty Ltd.
4
* All rights reserved.
5
*
6
* Redistribution and use in source and binary forms, with or without
7
* modification, are permitted provided that the following conditions
8
* are met:
9
*
10
* * Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
12
*
13
* * Redistributions in binary form must reproduce the above copyright
14
* notice, this list of conditions and the following disclaimer in
15
* the documentation and/or other materials provided with the
16
* distribution.
17
*
18
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
23
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
29
* OF THE POSSIBILITY OF SUCH DAMAGE.
30
*/
31
32
#include "k5-platform.h"
33
#include "gssapiP_spnego.h"
34
#include <generic/gssapiP_generic.h>
35
36
/*
37
* The initial context token emitted by the initiator is a INITIATOR_NEGO
38
* message followed by zero or more INITIATOR_META_DATA tokens, and zero
39
* or one AP_REQUEST tokens.
40
*
41
* Upon receiving this, the acceptor computes the list of mutually supported
42
* authentication mechanisms and performs the metadata exchange. The output
43
* token is ACCEPTOR_NEGO followed by zero or more ACCEPTOR_META_DATA tokens,
44
* and zero or one CHALLENGE tokens.
45
*
46
* Once the metadata exchange is complete and a mechanism is selected, the
47
* selected mechanism's context token exchange continues with AP_REQUEST and
48
* CHALLENGE messages.
49
*
50
* Once the context token exchange is complete, VERIFY messages are sent to
51
* authenticate the entire exchange.
52
*/
53
54
static void
55
zero_and_release_buffer_set(gss_buffer_set_t *pbuffers)
56
{
57
OM_uint32 tmpmin;
58
gss_buffer_set_t buffers = *pbuffers;
59
uint32_t i;
60
61
if (buffers != GSS_C_NO_BUFFER_SET) {
62
for (i = 0; i < buffers->count; i++)
63
zap(buffers->elements[i].value, buffers->elements[i].length);
64
65
gss_release_buffer_set(&tmpmin, &buffers);
66
}
67
68
*pbuffers = GSS_C_NO_BUFFER_SET;
69
}
70
71
static OM_uint32
72
buffer_set_to_key(OM_uint32 *minor, gss_buffer_set_t buffers,
73
krb5_keyblock *key)
74
{
75
krb5_error_code ret;
76
77
/* Returned keys must be in two buffers, with the key contents in the first
78
* and the enctype as a 32-bit little-endian integer in the second. */
79
if (buffers->count != 2 || buffers->elements[1].length != 4) {
80
*minor = ERR_NEGOEX_NO_VERIFY_KEY;
81
return GSS_S_FAILURE;
82
}
83
84
krb5_free_keyblock_contents(NULL, key);
85
86
key->contents = k5memdup(buffers->elements[0].value,
87
buffers->elements[0].length, &ret);
88
if (key->contents == NULL) {
89
*minor = ret;
90
return GSS_S_FAILURE;
91
}
92
key->length = buffers->elements[0].length;
93
key->enctype = load_32_le(buffers->elements[1].value);
94
95
return GSS_S_COMPLETE;
96
}
97
98
static OM_uint32
99
get_session_keys(OM_uint32 *minor, struct negoex_auth_mech *mech)
100
{
101
OM_uint32 major, tmpmin;
102
gss_buffer_set_t buffers = GSS_C_NO_BUFFER_SET;
103
104
major = gss_inquire_sec_context_by_oid(&tmpmin, mech->mech_context,
105
GSS_C_INQ_NEGOEX_KEY, &buffers);
106
if (major == GSS_S_COMPLETE) {
107
major = buffer_set_to_key(minor, buffers, &mech->key);
108
zero_and_release_buffer_set(&buffers);
109
if (major != GSS_S_COMPLETE)
110
return major;
111
}
112
113
major = gss_inquire_sec_context_by_oid(&tmpmin, mech->mech_context,
114
GSS_C_INQ_NEGOEX_VERIFY_KEY,
115
&buffers);
116
if (major == GSS_S_COMPLETE) {
117
major = buffer_set_to_key(minor, buffers, &mech->verify_key);
118
zero_and_release_buffer_set(&buffers);
119
if (major != GSS_S_COMPLETE)
120
return major;
121
}
122
123
return GSS_S_COMPLETE;
124
}
125
126
static OM_uint32
127
emit_initiator_nego(OM_uint32 *minor, spnego_gss_ctx_id_t ctx)
128
{
129
OM_uint32 major;
130
uint8_t random[32];
131
132
major = negoex_random(minor, ctx, random, 32);
133
if (major != GSS_S_COMPLETE)
134
return major;
135
136
negoex_add_nego_message(ctx, INITIATOR_NEGO, random);
137
return GSS_S_COMPLETE;
138
}
139
140
static OM_uint32
141
process_initiator_nego(OM_uint32 *minor, spnego_gss_ctx_id_t ctx,
142
struct negoex_message *messages, size_t nmessages)
143
{
144
struct nego_message *msg;
145
146
assert(!ctx->initiate && ctx->negoex_step == 1);
147
148
msg = negoex_locate_nego_message(messages, nmessages, INITIATOR_NEGO);
149
if (msg == NULL) {
150
*minor = ERR_NEGOEX_MISSING_NEGO_MESSAGE;
151
return GSS_S_DEFECTIVE_TOKEN;
152
}
153
154
negoex_restrict_auth_schemes(ctx, msg->schemes, msg->nschemes);
155
return GSS_S_COMPLETE;
156
}
157
158
static OM_uint32
159
emit_acceptor_nego(OM_uint32 *minor, spnego_gss_ctx_id_t ctx)
160
{
161
OM_uint32 major;
162
uint8_t random[32];
163
164
major = negoex_random(minor, ctx, random, 32);
165
if (major != GSS_S_COMPLETE)
166
return major;
167
168
negoex_add_nego_message(ctx, ACCEPTOR_NEGO, random);
169
return GSS_S_COMPLETE;
170
}
171
172
static OM_uint32
173
process_acceptor_nego(OM_uint32 *minor, spnego_gss_ctx_id_t ctx,
174
struct negoex_message *messages, size_t nmessages)
175
{
176
struct nego_message *msg;
177
178
msg = negoex_locate_nego_message(messages, nmessages, ACCEPTOR_NEGO);
179
if (msg == NULL) {
180
*minor = ERR_NEGOEX_MISSING_NEGO_MESSAGE;
181
return GSS_S_DEFECTIVE_TOKEN;
182
}
183
184
/* Reorder and prune our mech list to match the acceptor's list (or a
185
* subset of it). */
186
negoex_common_auth_schemes(ctx, msg->schemes, msg->nschemes);
187
188
return GSS_S_COMPLETE;
189
}
190
191
static void
192
query_meta_data(spnego_gss_ctx_id_t ctx, gss_cred_id_t cred,
193
gss_name_t target, OM_uint32 req_flags)
194
{
195
OM_uint32 major, minor;
196
struct negoex_auth_mech *p, *next;
197
198
K5_TAILQ_FOREACH_SAFE(p, &ctx->negoex_mechs, links, next) {
199
major = gssspi_query_meta_data(&minor, p->oid, cred, &p->mech_context,
200
target, req_flags, &p->metadata);
201
/* GSS_Query_meta_data failure removes mechanism from list. */
202
if (major != GSS_S_COMPLETE)
203
negoex_delete_auth_mech(ctx, p);
204
}
205
}
206
207
static void
208
exchange_meta_data(spnego_gss_ctx_id_t ctx, gss_cred_id_t cred,
209
gss_name_t target, OM_uint32 req_flags,
210
struct negoex_message *messages, size_t nmessages)
211
{
212
OM_uint32 major, minor;
213
struct negoex_auth_mech *mech;
214
enum message_type type;
215
struct exchange_message *msg;
216
uint32_t i;
217
218
type = ctx->initiate ? ACCEPTOR_META_DATA : INITIATOR_META_DATA;
219
220
for (i = 0; i < nmessages; i++) {
221
if (messages[i].type != type)
222
continue;
223
msg = &messages[i].u.e;
224
225
mech = negoex_locate_auth_scheme(ctx, msg->scheme);
226
if (mech == NULL)
227
continue;
228
229
major = gssspi_exchange_meta_data(&minor, mech->oid, cred,
230
&mech->mech_context, target,
231
req_flags, &msg->token);
232
/* GSS_Exchange_meta_data failure removes mechanism from list. */
233
if (major != GSS_S_COMPLETE)
234
negoex_delete_auth_mech(ctx, mech);
235
}
236
}
237
238
/*
239
* In the initiator, if we are processing the acceptor's first reply, discard
240
* the optimistic context if the acceptor ignored the optimistic token. If the
241
* acceptor continued the optimistic mech, discard all other mechs.
242
*/
243
static void
244
check_optimistic_result(spnego_gss_ctx_id_t ctx,
245
struct negoex_message *messages, size_t nmessages)
246
{
247
struct negoex_auth_mech *mech;
248
OM_uint32 tmpmin;
249
250
assert(ctx->initiate && ctx->negoex_step == 2);
251
252
/* Do nothing if we didn't make an optimistic context. */
253
mech = K5_TAILQ_FIRST(&ctx->negoex_mechs);
254
if (mech == NULL || mech->mech_context == GSS_C_NO_CONTEXT)
255
return;
256
257
/* If the acceptor used the optimistic token, it will send an acceptor
258
* token or a checksum (or both) in its first reply. */
259
if (negoex_locate_exchange_message(messages, nmessages,
260
CHALLENGE) != NULL ||
261
negoex_locate_verify_message(messages, nmessages) != NULL) {
262
/* The acceptor continued the optimistic mech, and metadata exchange
263
* didn't remove it. Commit to this mechanism. */
264
negoex_select_auth_mech(ctx, mech);
265
} else {
266
/* The acceptor ignored the optimistic token. Restart the mech. */
267
(void)gss_delete_sec_context(&tmpmin, &mech->mech_context, NULL);
268
krb5_free_keyblock_contents(NULL, &mech->key);
269
krb5_free_keyblock_contents(NULL, &mech->verify_key);
270
mech->complete = mech->sent_checksum = FALSE;
271
}
272
}
273
274
/* Perform an initiator step of the underlying mechanism exchange. */
275
static OM_uint32
276
mech_init(OM_uint32 *minor, spnego_gss_ctx_id_t ctx, gss_cred_id_t cred,
277
gss_name_t target, OM_uint32 req_flags, OM_uint32 time_req,
278
struct negoex_message *messages, size_t nmessages,
279
gss_channel_bindings_t bindings, gss_buffer_t output_token,
280
OM_uint32 *time_rec)
281
{
282
OM_uint32 major, first_major = 0, first_minor = 0;
283
struct negoex_auth_mech *mech = NULL;
284
gss_buffer_t input_token = GSS_C_NO_BUFFER;
285
struct exchange_message *msg;
286
int first_mech;
287
288
output_token->value = NULL;
289
output_token->length = 0;
290
291
/* Allow disabling of optimistic token for testing. */
292
if (ctx->negoex_step == 1 &&
293
secure_getenv("NEGOEX_NO_OPTIMISTIC_TOKEN") != NULL)
294
return GSS_S_COMPLETE;
295
296
if (K5_TAILQ_EMPTY(&ctx->negoex_mechs)) {
297
*minor = ERR_NEGOEX_NO_AVAILABLE_MECHS;
298
return GSS_S_FAILURE;
299
}
300
301
/*
302
* Get the input token. The challenge could be for the optimistic mech,
303
* which we might have discarded in metadata exchange, so ignore the
304
* challenge if it doesn't match the first auth mech.
305
*/
306
mech = K5_TAILQ_FIRST(&ctx->negoex_mechs);
307
msg = negoex_locate_exchange_message(messages, nmessages, CHALLENGE);
308
if (msg != NULL && GUID_EQ(msg->scheme, mech->scheme))
309
input_token = &msg->token;
310
311
if (mech->complete)
312
return GSS_S_COMPLETE;
313
314
first_mech = TRUE;
315
316
while (!K5_TAILQ_EMPTY(&ctx->negoex_mechs)) {
317
mech = K5_TAILQ_FIRST(&ctx->negoex_mechs);
318
319
major = gss_init_sec_context(minor, cred, &mech->mech_context, target,
320
mech->oid, req_flags, time_req, bindings,
321
input_token, &ctx->actual_mech,
322
output_token, &ctx->ctx_flags, time_rec);
323
324
if (major == GSS_S_COMPLETE)
325
mech->complete = 1;
326
327
if (!GSS_ERROR(major))
328
return get_session_keys(minor, mech);
329
330
/* Remember the error we got from the first mech. */
331
if (first_mech) {
332
first_major = major;
333
first_minor = *minor;
334
}
335
336
/* If we still have multiple mechs to try, move on to the next one. */
337
negoex_delete_auth_mech(ctx, mech);
338
first_mech = FALSE;
339
input_token = GSS_C_NO_BUFFER;
340
}
341
342
if (K5_TAILQ_EMPTY(&ctx->negoex_mechs)) {
343
major = first_major;
344
*minor = first_minor;
345
}
346
347
return major;
348
}
349
350
/* Perform an acceptor step of the underlying mechanism exchange. */
351
static OM_uint32
352
mech_accept(OM_uint32 *minor, spnego_gss_ctx_id_t ctx,
353
gss_cred_id_t cred, struct negoex_message *messages,
354
size_t nmessages, gss_channel_bindings_t bindings,
355
gss_buffer_t output_token, OM_uint32 *time_rec)
356
{
357
OM_uint32 major, tmpmin;
358
struct negoex_auth_mech *mech;
359
struct exchange_message *msg;
360
361
assert(!ctx->initiate && !K5_TAILQ_EMPTY(&ctx->negoex_mechs));
362
363
msg = negoex_locate_exchange_message(messages, nmessages, AP_REQUEST);
364
if (msg == NULL) {
365
/* No input token is okay on the first request or if the mech is
366
* complete. */
367
if (ctx->negoex_step == 1 ||
368
K5_TAILQ_FIRST(&ctx->negoex_mechs)->complete)
369
return GSS_S_COMPLETE;
370
*minor = ERR_NEGOEX_MISSING_AP_REQUEST_MESSAGE;
371
return GSS_S_DEFECTIVE_TOKEN;
372
}
373
374
if (ctx->negoex_step == 1) {
375
/* Ignore the optimistic token if it isn't for our most preferred
376
* mech. */
377
mech = K5_TAILQ_FIRST(&ctx->negoex_mechs);
378
if (!GUID_EQ(msg->scheme, mech->scheme))
379
return GSS_S_COMPLETE;
380
} else {
381
/* The initiator has selected a mech; discard other entries. */
382
mech = negoex_locate_auth_scheme(ctx, msg->scheme);
383
if (mech == NULL) {
384
*minor = ERR_NEGOEX_NO_AVAILABLE_MECHS;
385
return GSS_S_FAILURE;
386
}
387
negoex_select_auth_mech(ctx, mech);
388
}
389
390
if (mech->complete)
391
return GSS_S_COMPLETE;
392
393
if (ctx->internal_name != GSS_C_NO_NAME)
394
gss_release_name(&tmpmin, &ctx->internal_name);
395
if (ctx->deleg_cred != GSS_C_NO_CREDENTIAL)
396
gss_release_cred(&tmpmin, &ctx->deleg_cred);
397
398
major = gss_accept_sec_context(minor, &mech->mech_context, cred,
399
&msg->token, bindings, &ctx->internal_name,
400
&ctx->actual_mech, output_token,
401
&ctx->ctx_flags, time_rec,
402
&ctx->deleg_cred);
403
404
if (major == GSS_S_COMPLETE)
405
mech->complete = 1;
406
407
if (!GSS_ERROR(major)) {
408
major = get_session_keys(minor, mech);
409
} else if (ctx->negoex_step == 1) {
410
/* This was an optimistic token; pretend this never happened. */
411
major = GSS_S_COMPLETE;
412
*minor = 0;
413
gss_release_buffer(&tmpmin, output_token);
414
gss_delete_sec_context(&tmpmin, &mech->mech_context, GSS_C_NO_BUFFER);
415
}
416
417
return major;
418
}
419
420
static krb5_keyusage
421
verify_keyusage(spnego_gss_ctx_id_t ctx, int make_checksum)
422
{
423
/* Of course, these are the wrong way around in the spec. */
424
return (ctx->initiate ^ !make_checksum) ?
425
NEGOEX_KEYUSAGE_ACCEPTOR_CHECKSUM : NEGOEX_KEYUSAGE_INITIATOR_CHECKSUM;
426
}
427
428
static OM_uint32
429
verify_checksum(OM_uint32 *minor, spnego_gss_ctx_id_t ctx,
430
struct negoex_message *messages, size_t nmessages,
431
gss_buffer_t input_token, int *send_alert_out)
432
{
433
krb5_error_code ret;
434
struct negoex_auth_mech *mech = K5_TAILQ_FIRST(&ctx->negoex_mechs);
435
struct verify_message *msg;
436
krb5_crypto_iov iov[3];
437
krb5_keyusage usage = verify_keyusage(ctx, FALSE);
438
krb5_boolean valid;
439
440
*send_alert_out = FALSE;
441
assert(mech != NULL);
442
443
/* The other party may not be ready to send a verify token yet, or (in the
444
* first initiator step) may send one for a mechanism we don't support. */
445
msg = negoex_locate_verify_message(messages, nmessages);
446
if (msg == NULL || !GUID_EQ(msg->scheme, mech->scheme))
447
return GSS_S_COMPLETE;
448
449
/* A recoverable error may cause us to be unable to verify a token from the
450
* other party. In this case we should send an alert. */
451
if (mech->verify_key.enctype == ENCTYPE_NULL) {
452
*send_alert_out = TRUE;
453
return GSS_S_COMPLETE;
454
}
455
456
/* Verify the checksum over the existing transcript and the portion of the
457
* input token leading up to the verify message. */
458
assert(input_token != NULL);
459
iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
460
iov[0].data = make_data(ctx->negoex_transcript.data,
461
ctx->negoex_transcript.len);
462
iov[1].flags = KRB5_CRYPTO_TYPE_DATA;
463
iov[1].data = make_data(input_token->value, msg->offset_in_token);
464
iov[2].flags = KRB5_CRYPTO_TYPE_CHECKSUM;
465
iov[2].data = make_data((uint8_t *)msg->cksum, msg->cksum_len);
466
467
ret = krb5_c_verify_checksum_iov(ctx->kctx, msg->cksum_type,
468
&mech->verify_key, usage, iov, 3, &valid);
469
if (ret) {
470
*minor = ret;
471
return GSS_S_FAILURE;
472
}
473
if (!valid || !krb5_c_is_keyed_cksum(msg->cksum_type)) {
474
*minor = ERR_NEGOEX_INVALID_CHECKSUM;
475
return GSS_S_BAD_SIG;
476
}
477
478
mech->verified_checksum = TRUE;
479
return GSS_S_COMPLETE;
480
}
481
482
static OM_uint32
483
make_checksum(OM_uint32 *minor, spnego_gss_ctx_id_t ctx)
484
{
485
krb5_error_code ret;
486
krb5_data d;
487
krb5_keyusage usage = verify_keyusage(ctx, TRUE);
488
krb5_checksum cksum;
489
struct negoex_auth_mech *mech = K5_TAILQ_FIRST(&ctx->negoex_mechs);
490
491
assert(mech != NULL);
492
493
if (mech->key.enctype == ENCTYPE_NULL) {
494
if (mech->complete) {
495
*minor = ERR_NEGOEX_NO_VERIFY_KEY;
496
return GSS_S_UNAVAILABLE;
497
} else {
498
return GSS_S_COMPLETE;
499
}
500
}
501
502
d = make_data(ctx->negoex_transcript.data, ctx->negoex_transcript.len);
503
ret = krb5_c_make_checksum(ctx->kctx, 0, &mech->key, usage, &d, &cksum);
504
if (ret) {
505
*minor = ret;
506
return GSS_S_FAILURE;
507
}
508
509
negoex_add_verify_message(ctx, mech->scheme, cksum.checksum_type,
510
cksum.contents, cksum.length);
511
512
mech->sent_checksum = TRUE;
513
krb5_free_checksum_contents(ctx->kctx, &cksum);
514
return GSS_S_COMPLETE;
515
}
516
517
/* If the other side sent a VERIFY_NO_KEY pulse alert, clear the checksum state
518
* on the mechanism so that we send another VERIFY message. */
519
static void
520
process_alerts(spnego_gss_ctx_id_t ctx,
521
struct negoex_message *messages, uint32_t nmessages)
522
{
523
struct alert_message *msg;
524
struct negoex_auth_mech *mech;
525
526
msg = negoex_locate_alert_message(messages, nmessages);
527
if (msg != NULL && msg->verify_no_key) {
528
mech = negoex_locate_auth_scheme(ctx, msg->scheme);
529
if (mech != NULL) {
530
mech->sent_checksum = FALSE;
531
krb5_free_keyblock_contents(NULL, &mech->key);
532
krb5_free_keyblock_contents(NULL, &mech->verify_key);
533
}
534
}
535
}
536
537
static OM_uint32
538
make_output_token(OM_uint32 *minor, spnego_gss_ctx_id_t ctx,
539
gss_buffer_t mech_output_token, int send_alert,
540
gss_buffer_t output_token)
541
{
542
OM_uint32 major;
543
struct negoex_auth_mech *mech;
544
enum message_type type;
545
size_t old_transcript_len = ctx->negoex_transcript.len;
546
547
output_token->length = 0;
548
output_token->value = NULL;
549
550
/* If the mech is complete and we previously sent a checksum, we just
551
* processed the last leg and don't need to send another token. */
552
if (mech_output_token->length == 0 &&
553
K5_TAILQ_FIRST(&ctx->negoex_mechs)->sent_checksum)
554
return GSS_S_COMPLETE;
555
556
if (ctx->negoex_step == 1) {
557
if (ctx->initiate)
558
major = emit_initiator_nego(minor, ctx);
559
else
560
major = emit_acceptor_nego(minor, ctx);
561
if (major != GSS_S_COMPLETE)
562
return major;
563
564
type = ctx->initiate ? INITIATOR_META_DATA : ACCEPTOR_META_DATA;
565
K5_TAILQ_FOREACH(mech, &ctx->negoex_mechs, links) {
566
if (mech->metadata.length > 0) {
567
negoex_add_exchange_message(ctx, type, mech->scheme,
568
&mech->metadata);
569
}
570
}
571
}
572
573
mech = K5_TAILQ_FIRST(&ctx->negoex_mechs);
574
575
if (mech_output_token->length > 0) {
576
type = ctx->initiate ? AP_REQUEST : CHALLENGE;
577
negoex_add_exchange_message(ctx, type, mech->scheme,
578
mech_output_token);
579
}
580
581
if (send_alert)
582
negoex_add_verify_no_key_alert(ctx, mech->scheme);
583
584
/* Try to add a VERIFY message if we haven't already done so. */
585
if (!mech->sent_checksum) {
586
major = make_checksum(minor, ctx);
587
if (major != GSS_S_COMPLETE)
588
return major;
589
}
590
591
if (ctx->negoex_transcript.data == NULL) {
592
*minor = ENOMEM;
593
return GSS_S_FAILURE;
594
}
595
596
/* Copy what we added to the transcript into the output token. */
597
output_token->length = ctx->negoex_transcript.len - old_transcript_len;
598
output_token->value = gssalloc_malloc(output_token->length);
599
if (output_token->value == NULL) {
600
*minor = ENOMEM;
601
return GSS_S_FAILURE;
602
}
603
memcpy(output_token->value,
604
(uint8_t *)ctx->negoex_transcript.data + old_transcript_len,
605
output_token->length);
606
607
return GSS_S_COMPLETE;
608
}
609
610
OM_uint32
611
negoex_init(OM_uint32 *minor, spnego_gss_ctx_id_t ctx, gss_cred_id_t cred,
612
gss_name_t target_name, OM_uint32 req_flags, OM_uint32 time_req,
613
gss_buffer_t input_token, gss_channel_bindings_t bindings,
614
gss_buffer_t output_token, OM_uint32 *time_rec)
615
{
616
OM_uint32 major, tmpmin;
617
gss_buffer_desc mech_output_token = GSS_C_EMPTY_BUFFER;
618
struct negoex_message *messages = NULL;
619
struct negoex_auth_mech *mech;
620
size_t nmessages = 0;
621
int send_alert = FALSE;
622
623
if (ctx->negoex_step == 0 && input_token != GSS_C_NO_BUFFER &&
624
input_token->length != 0)
625
return GSS_S_DEFECTIVE_TOKEN;
626
627
major = negoex_prep_context_for_negoex(minor, ctx);
628
if (major != GSS_S_COMPLETE)
629
goto cleanup;
630
631
ctx->negoex_step++;
632
633
if (input_token != GSS_C_NO_BUFFER && input_token->length > 0) {
634
major = negoex_parse_token(minor, ctx, input_token, &messages,
635
&nmessages);
636
if (major != GSS_S_COMPLETE)
637
goto cleanup;
638
}
639
640
process_alerts(ctx, messages, nmessages);
641
642
if (ctx->negoex_step == 1) {
643
/* Choose a random conversation ID. */
644
major = negoex_random(minor, ctx, ctx->negoex_conv_id, GUID_LENGTH);
645
if (major != GSS_S_COMPLETE)
646
goto cleanup;
647
648
/* Query each mech for its metadata (this may prune the mech list). */
649
query_meta_data(ctx, cred, target_name, req_flags);
650
} else if (ctx->negoex_step == 2) {
651
/* See if the mech processed the optimistic token. */
652
check_optimistic_result(ctx, messages, nmessages);
653
654
/* Pass the acceptor metadata to each mech to prune the list. */
655
exchange_meta_data(ctx, cred, target_name, req_flags,
656
messages, nmessages);
657
658
/* Process the ACCEPTOR_NEGO message. */
659
major = process_acceptor_nego(minor, ctx, messages, nmessages);
660
if (major != GSS_S_COMPLETE)
661
goto cleanup;
662
}
663
664
/* Process the input token and/or produce an output token. This may prune
665
* the mech list, but on success there will be at least one mech entry. */
666
major = mech_init(minor, ctx, cred, target_name, req_flags, time_req,
667
messages, nmessages, bindings, &mech_output_token,
668
time_rec);
669
if (major != GSS_S_COMPLETE)
670
goto cleanup;
671
assert(!K5_TAILQ_EMPTY(&ctx->negoex_mechs));
672
673
/* At this point in step 2 we have performed the metadata exchange and
674
* chosen a mech we can use, so discard any fallback mech entries. */
675
if (ctx->negoex_step == 2)
676
negoex_select_auth_mech(ctx, K5_TAILQ_FIRST(&ctx->negoex_mechs));
677
678
major = verify_checksum(minor, ctx, messages, nmessages, input_token,
679
&send_alert);
680
if (major != GSS_S_COMPLETE)
681
goto cleanup;
682
683
if (input_token != GSS_C_NO_BUFFER) {
684
k5_buf_add_len(&ctx->negoex_transcript, input_token->value,
685
input_token->length);
686
}
687
688
major = make_output_token(minor, ctx, &mech_output_token, send_alert,
689
output_token);
690
if (major != GSS_S_COMPLETE)
691
goto cleanup;
692
693
mech = K5_TAILQ_FIRST(&ctx->negoex_mechs);
694
major = (mech->complete && mech->verified_checksum) ? GSS_S_COMPLETE :
695
GSS_S_CONTINUE_NEEDED;
696
697
cleanup:
698
free(messages);
699
gss_release_buffer(&tmpmin, &mech_output_token);
700
negoex_prep_context_for_spnego(ctx);
701
return major;
702
}
703
704
OM_uint32
705
negoex_accept(OM_uint32 *minor, spnego_gss_ctx_id_t ctx, gss_cred_id_t cred,
706
gss_buffer_t input_token, gss_channel_bindings_t bindings,
707
gss_buffer_t output_token, OM_uint32 *time_rec)
708
{
709
OM_uint32 major, tmpmin;
710
gss_buffer_desc mech_output_token = GSS_C_EMPTY_BUFFER;
711
struct negoex_message *messages = NULL;
712
struct negoex_auth_mech *mech;
713
size_t nmessages;
714
int send_alert = FALSE;
715
716
if (input_token == GSS_C_NO_BUFFER || input_token->length == 0) {
717
major = GSS_S_DEFECTIVE_TOKEN;
718
goto cleanup;
719
}
720
721
major = negoex_prep_context_for_negoex(minor, ctx);
722
if (major != GSS_S_COMPLETE)
723
goto cleanup;
724
725
ctx->negoex_step++;
726
727
major = negoex_parse_token(minor, ctx, input_token, &messages, &nmessages);
728
if (major != GSS_S_COMPLETE)
729
goto cleanup;
730
731
process_alerts(ctx, messages, nmessages);
732
733
if (ctx->negoex_step == 1) {
734
/* Read the INITIATOR_NEGO message to prune the candidate mech list. */
735
major = process_initiator_nego(minor, ctx, messages, nmessages);
736
if (major != GSS_S_COMPLETE)
737
goto cleanup;
738
739
/*
740
* Pass the initiator metadata to each mech to prune the list, and
741
* query each mech for its acceptor metadata (which may also prune the
742
* list).
743
*/
744
exchange_meta_data(ctx, cred, GSS_C_NO_NAME, 0, messages, nmessages);
745
query_meta_data(ctx, cred, GSS_C_NO_NAME, 0);
746
747
if (K5_TAILQ_EMPTY(&ctx->negoex_mechs)) {
748
*minor = ERR_NEGOEX_NO_AVAILABLE_MECHS;
749
major = GSS_S_FAILURE;
750
goto cleanup;
751
}
752
}
753
754
/*
755
* Process the input token and possibly produce an output token. This may
756
* prune the list to a single mech. Continue on error if an output token
757
* is generated, so that we send the token to the initiator.
758
*/
759
major = mech_accept(minor, ctx, cred, messages, nmessages, bindings,
760
&mech_output_token, time_rec);
761
if (major != GSS_S_COMPLETE && mech_output_token.length == 0)
762
goto cleanup;
763
764
if (major == GSS_S_COMPLETE) {
765
major = verify_checksum(minor, ctx, messages, nmessages, input_token,
766
&send_alert);
767
if (major != GSS_S_COMPLETE)
768
goto cleanup;
769
}
770
771
k5_buf_add_len(&ctx->negoex_transcript,
772
input_token->value, input_token->length);
773
774
major = make_output_token(minor, ctx, &mech_output_token, send_alert,
775
output_token);
776
if (major != GSS_S_COMPLETE)
777
goto cleanup;
778
779
mech = K5_TAILQ_FIRST(&ctx->negoex_mechs);
780
major = (mech->complete && mech->verified_checksum) ? GSS_S_COMPLETE :
781
GSS_S_CONTINUE_NEEDED;
782
783
cleanup:
784
free(messages);
785
gss_release_buffer(&tmpmin, &mech_output_token);
786
negoex_prep_context_for_spnego(ctx);
787
return major;
788
}
789
790