Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/krb5/src/tests/gssapi/t_spnego.c
34890 views
1
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
/*
3
* Copyright 2010 by the Massachusetts Institute of Technology.
4
* All Rights Reserved.
5
*
6
* Export of this software from the United States of America may
7
* require a specific license from the United States Government.
8
* It is the responsibility of any person or organization contemplating
9
* export to obtain such a license before exporting.
10
*
11
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
12
* distribute this software and its documentation for any purpose and
13
* without fee is hereby granted, provided that the above copyright
14
* notice appear in all copies and that both that copyright notice and
15
* this permission notice appear in supporting documentation, and that
16
* the name of M.I.T. not be used in advertising or publicity pertaining
17
* to distribution of the software without specific, written prior
18
* permission. Furthermore if you modify this software you must label
19
* your software as modified software and not distribute it in such a
20
* fashion that it might be confused with the original M.I.T. software.
21
* M.I.T. makes no representations about the suitability of
22
* this software for any purpose. It is provided "as is" without express
23
* or implied warranty.
24
*
25
*/
26
27
#include <stdio.h>
28
#include <stdlib.h>
29
#include <string.h>
30
#include <assert.h>
31
32
#include "common.h"
33
34
static gss_OID_desc mech_krb5_wrong = {
35
9, "\052\206\110\202\367\022\001\002\002"
36
};
37
gss_OID_set_desc mechset_krb5_wrong = { 1, &mech_krb5_wrong };
38
39
/*
40
* Test program for SPNEGO and gss_set_neg_mechs
41
*
42
* Example usage:
43
*
44
* kinit testuser
45
* ./t_spnego host/test.host@REALM testhost.keytab
46
*/
47
48
/* Replace *tok and *len with the concatenation of prefix and *tok. */
49
static void
50
prepend(const void *prefix, size_t plen, uint8_t **tok, size_t *len)
51
{
52
uint8_t *newtok;
53
54
newtok = malloc(plen + *len);
55
assert(newtok != NULL);
56
memcpy(newtok, prefix, plen);
57
memcpy(newtok + plen, *tok, *len);
58
free(*tok);
59
*tok = newtok;
60
*len = plen + *len;
61
}
62
63
/* Replace *tok and *len with *tok wrapped in a DER tag with the given tag
64
* byte. *len must be less than 2^16. */
65
static void
66
der_wrap(uint8_t tag, uint8_t **tok, size_t *len)
67
{
68
char lenbuf[3];
69
uint8_t *wrapped;
70
size_t llen;
71
72
if (*len < 128) {
73
lenbuf[0] = *len;
74
llen = 1;
75
} else if (*len < 256) {
76
lenbuf[0] = 0x81;
77
lenbuf[1] = *len;
78
llen = 2;
79
} else {
80
assert(*len >> 16 == 0);
81
lenbuf[0] = 0x82;
82
lenbuf[1] = *len >> 8;
83
lenbuf[2] = *len & 0xFF;
84
llen = 3;
85
}
86
wrapped = malloc(1 + llen + *len);
87
assert(wrapped != NULL);
88
*wrapped = tag;
89
memcpy(wrapped + 1, lenbuf, llen);
90
memcpy(wrapped + 1 + llen, *tok, *len);
91
free(*tok);
92
*tok = wrapped;
93
*len = 1 + llen + *len;
94
}
95
96
/*
97
* Create a SPNEGO initiator token for the erroneous Microsoft krb5 mech OID,
98
* wrapping a krb5 token ktok. The token should look like:
99
*
100
* 60 <len> (GSS framing sequence)
101
* 06 06 2B 06 01 05 05 02 (SPNEGO OID)
102
* A0 <len> (NegotiationToken choice 0, negTokenInit)
103
* 30 <len> (sequence)
104
* A0 0D (context tag 0, mechTypes)
105
* 30 0B (sequence of)
106
* 06 09 2A 86 48 82 F7 12 01 02 02 (wrong krb5 OID)
107
* A2 <len> (context tag 2, mechToken)
108
* 04 <len> (octet string)
109
* <mech token octets>
110
*/
111
static void
112
create_mskrb5_spnego_token(gss_buffer_t ktok, gss_buffer_desc *tok_out)
113
{
114
uint8_t *tok;
115
size_t len;
116
117
len = ktok->length;
118
tok = malloc(len);
119
assert(tok != NULL);
120
memcpy(tok, ktok->value, len);
121
/* Wrap the krb5 token in OCTET STRING and [2] tags. */
122
der_wrap(0x04, &tok, &len);
123
der_wrap(0xA2, &tok, &len);
124
/* Prepend the wrong krb5 OID inside OBJECT IDENTIFIER and [0] tags. */
125
prepend("\xA0\x0D\x30\x0B\x06\x09\x2A\x86\x48\x82\xF7\x12\x01\x02\x02", 15,
126
&tok, &len);
127
/* Wrap the previous two things in SEQUENCE and [0] tags. */
128
der_wrap(0x30, &tok, &len);
129
der_wrap(0xA0, &tok, &len);
130
/* Prepend the SPNEGO OID in an OBJECT IDENTIFIER tag. */
131
prepend("\x06\x06\x2B\x06\x01\x05\x05\x02", 8, &tok, &len);
132
/* Wrap the whole thing in an [APPLICATION 0] tag. */
133
der_wrap(0x60, &tok, &len);
134
tok_out->value = tok;
135
tok_out->length = len;
136
}
137
138
/*
139
* Test that the SPNEGO acceptor code accepts and properly reflects back the
140
* erroneous Microsoft mech OID in the supportedMech field of the NegTokenResp
141
* message. Use acred as the verifier cred handle.
142
*/
143
static void
144
test_mskrb_oid(gss_name_t tname, gss_cred_id_t acred)
145
{
146
OM_uint32 major, minor;
147
gss_ctx_id_t ictx = GSS_C_NO_CONTEXT, actx = GSS_C_NO_CONTEXT;
148
gss_buffer_desc atok = GSS_C_EMPTY_BUFFER, ktok = GSS_C_EMPTY_BUFFER, stok;
149
const unsigned char *atok_oid;
150
151
/*
152
* Our SPNEGO mech no longer acquires creds for the wrong mech OID, so we
153
* have to construct a SPNEGO token ourselves.
154
*/
155
major = gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL, &ictx, tname,
156
&mech_krb5, 0, GSS_C_INDEFINITE,
157
GSS_C_NO_CHANNEL_BINDINGS, &atok, NULL, &ktok,
158
NULL, NULL);
159
check_gsserr("gss_init_sec_context(mskrb)", major, minor);
160
assert(major == GSS_S_COMPLETE);
161
create_mskrb5_spnego_token(&ktok, &stok);
162
163
/*
164
* Look directly at the DER encoding of the response token. Since we
165
* didn't request mutual authentication, the SPNEGO reply will contain no
166
* underlying mech token; therefore, the encoding of the correct
167
* NegotiationToken response is completely predictable:
168
*
169
* A1 14 (choice 1, length 20, meaning negTokenResp)
170
* 30 12 (sequence, length 18)
171
* A0 03 (context tag 0, length 3)
172
* 0A 01 00 (enumerated value 0, meaning accept-completed)
173
* A1 0B (context tag 1, length 11)
174
* 06 09 (object identifier, length 9)
175
* 2A 86 48 82 F7 12 01 02 02 (the erroneous krb5 OID)
176
*
177
* So we can just compare the length to 22 and the nine bytes at offset 13
178
* to the expected OID.
179
*/
180
major = gss_accept_sec_context(&minor, &actx, acred, &stok,
181
GSS_C_NO_CHANNEL_BINDINGS, NULL,
182
NULL, &atok, NULL, NULL, NULL);
183
check_gsserr("gss_accept_sec_context(mskrb)", major, minor);
184
assert(atok.length == 22);
185
atok_oid = (unsigned char *)atok.value + 13;
186
assert(memcmp(atok_oid, mech_krb5_wrong.elements, 9) == 0);
187
188
(void)gss_delete_sec_context(&minor, &ictx, NULL);
189
(void)gss_delete_sec_context(&minor, &actx, NULL);
190
(void)gss_release_buffer(&minor, &ktok);
191
(void)gss_release_buffer(&minor, &atok);
192
free(stok.value);
193
}
194
195
/* Check that we return a compatibility NegTokenInit2 message containing
196
* NegHints for an empty initiator token. */
197
static void
198
test_neghints(void)
199
{
200
OM_uint32 major, minor;
201
gss_buffer_desc itok = GSS_C_EMPTY_BUFFER, atok;
202
gss_ctx_id_t actx = GSS_C_NO_CONTEXT;
203
const char *expected =
204
/* RFC 2743 token framing: [APPLICATION 0] IMPLICIT SEQUENCE followed
205
* by OBJECT IDENTIFIER and the SPNEGO OID */
206
"\x60\x47\x06\x06" "\x2B\x06\x01\x05\x05\x02"
207
/* [0] SEQUENCE for the NegotiationToken negtokenInit choice */
208
"\xA0\x3D\x30\x3B"
209
/* [0] MechTypeList containing the krb5 OID */
210
"\xA0\x0D\x30\x0B\x06\x09" "\x2A\x86\x48\x86\xF7\x12\x01\x02\x02"
211
/* [3] NegHints containing [0] GeneralString containing the dummy
212
* hintName string defined in [MS-SPNG] */
213
"\xA3\x2A\x30\x28\xA0\x26\x1B\x24"
214
"not_defined_in_RFC4178@please_ignore";
215
216
/* Produce a hint token. */
217
major = gss_accept_sec_context(&minor, &actx, GSS_C_NO_CREDENTIAL, &itok,
218
GSS_C_NO_CHANNEL_BINDINGS, NULL, NULL,
219
&atok, NULL, NULL, NULL);
220
check_gsserr("gss_accept_sec_context(neghints)", major, minor);
221
222
/* Verify it against the expected contents, which are fixed as long as we
223
* only list the krb5 mech in the token. */
224
assert(atok.length == strlen(expected));
225
assert(memcmp(atok.value, expected, atok.length) == 0);
226
227
(void)gss_release_buffer(&minor, &atok);
228
(void)gss_delete_sec_context(&minor, &actx, NULL);
229
}
230
231
int
232
main(int argc, char *argv[])
233
{
234
OM_uint32 minor, major, flags;
235
gss_cred_id_t verifier_cred_handle = GSS_C_NO_CREDENTIAL;
236
gss_cred_id_t initiator_cred_handle = GSS_C_NO_CREDENTIAL;
237
gss_OID_set actual_mechs = GSS_C_NO_OID_SET;
238
gss_ctx_id_t initiator_context, acceptor_context;
239
gss_name_t target_name, source_name = GSS_C_NO_NAME;
240
gss_OID mech = GSS_C_NO_OID;
241
gss_OID_desc pref_oids[2];
242
gss_OID_set_desc pref_mechs;
243
244
if (argc < 2 || argc > 3) {
245
fprintf(stderr, "Usage: %s target_name [keytab]\n", argv[0]);
246
exit(1);
247
}
248
249
target_name = import_name(argv[1]);
250
251
if (argc >= 3) {
252
major = krb5_gss_register_acceptor_identity(argv[2]);
253
check_gsserr("krb5_gss_register_acceptor_identity", major, 0);
254
}
255
256
/* Get default initiator cred. */
257
major = gss_acquire_cred(&minor, GSS_C_NO_NAME, GSS_C_INDEFINITE,
258
&mechset_spnego, GSS_C_INITIATE,
259
&initiator_cred_handle, NULL, NULL);
260
check_gsserr("gss_acquire_cred(initiator)", major, minor);
261
262
/*
263
* The following test is designed to exercise SPNEGO reselection on the
264
* client and server. Unfortunately, it no longer does so after tickets
265
* #8217 and #8021, since SPNEGO now only acquires a single krb5 cred and
266
* there is no way to expand the underlying creds with gss_set_neg_mechs().
267
* To fix this we need gss_acquire_cred_with_cred() or some other way to
268
* turn a cred with a specifically requested mech set into a SPNEGO cred.
269
*/
270
271
/* Make the initiator prefer IAKERB and offer krb5 as an alternative. */
272
pref_oids[0] = mech_iakerb;
273
pref_oids[1] = mech_krb5;
274
pref_mechs.count = 2;
275
pref_mechs.elements = pref_oids;
276
major = gss_set_neg_mechs(&minor, initiator_cred_handle, &pref_mechs);
277
check_gsserr("gss_set_neg_mechs(initiator)", major, minor);
278
279
/* Get default acceptor cred. */
280
major = gss_acquire_cred(&minor, GSS_C_NO_NAME, GSS_C_INDEFINITE,
281
&mechset_spnego, GSS_C_ACCEPT,
282
&verifier_cred_handle, &actual_mechs, NULL);
283
check_gsserr("gss_acquire_cred(acceptor)", major, minor);
284
285
/* Restrict the acceptor to krb5 (which will force a reselection). */
286
major = gss_set_neg_mechs(&minor, verifier_cred_handle, &mechset_krb5);
287
check_gsserr("gss_set_neg_mechs(acceptor)", major, minor);
288
289
flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
290
establish_contexts(&mech_spnego, initiator_cred_handle,
291
verifier_cred_handle, target_name, flags,
292
&initiator_context, &acceptor_context, &source_name,
293
&mech, NULL);
294
295
display_canon_name("Source name", source_name, &mech_krb5);
296
display_oid("Source mech", mech);
297
298
/* Test acceptance of the erroneous Microsoft krb5 OID, with and without an
299
* acceptor cred. */
300
test_mskrb_oid(target_name, verifier_cred_handle);
301
test_mskrb_oid(target_name, GSS_C_NO_CREDENTIAL);
302
303
test_neghints();
304
305
(void)gss_delete_sec_context(&minor, &initiator_context, NULL);
306
(void)gss_delete_sec_context(&minor, &acceptor_context, NULL);
307
(void)gss_release_name(&minor, &source_name);
308
(void)gss_release_name(&minor, &target_name);
309
(void)gss_release_cred(&minor, &initiator_cred_handle);
310
(void)gss_release_cred(&minor, &verifier_cred_handle);
311
(void)gss_release_oid_set(&minor, &actual_mechs);
312
313
return 0;
314
}
315
316