2
Unix SMB/CIFS implementation.
3
simple kerberos5/SPNEGO routines
4
Copyright (C) Andrew Tridgell 2001
5
Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
6
Copyright (C) Luke Howard 2003
8
This program is free software; you can redistribute it and/or modify
9
it under the terms of the GNU General Public License as published by
10
the Free Software Foundation; either version 3 of the License, or
11
(at your option) any later version.
13
This program is distributed in the hope that it will be useful,
14
but WITHOUT ANY WARRANTY; without even the implied warranty of
15
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
GNU General Public License for more details.
18
You should have received a copy of the GNU General Public License
19
along with this program. If not, see <http://www.gnu.org/licenses/>.
26
generate a negTokenInit packet given a GUID, a list of supported
27
OIDs (the mechanisms) and a principal name string
29
DATA_BLOB spnego_gen_negTokenInit(char guid[16],
31
const char *principal)
37
data = asn1_init(talloc_tos());
39
return data_blob_null;
42
asn1_write(data, guid, 16);
43
asn1_push_tag(data,ASN1_APPLICATION(0));
44
asn1_write_OID(data,OID_SPNEGO);
45
asn1_push_tag(data,ASN1_CONTEXT(0));
46
asn1_push_tag(data,ASN1_SEQUENCE(0));
48
asn1_push_tag(data,ASN1_CONTEXT(0));
49
asn1_push_tag(data,ASN1_SEQUENCE(0));
50
for (i=0; OIDs[i]; i++) {
51
asn1_write_OID(data,OIDs[i]);
56
asn1_push_tag(data, ASN1_CONTEXT(3));
57
asn1_push_tag(data, ASN1_SEQUENCE(0));
58
asn1_push_tag(data, ASN1_CONTEXT(0));
59
asn1_write_GeneralString(data,principal);
69
if (data->has_error) {
70
DEBUG(1,("Failed to build negTokenInit at offset %d\n", (int)data->ofs));
73
ret = data_blob(data->data, data->length);
80
Generate a negTokenInit as used by the client side ... It has a mechType
81
(OID), and a mechToken (a security blob) ...
83
Really, we need to break out the NTLMSSP stuff as well, because it could be
86
DATA_BLOB gen_negTokenInit(const char *OID, DATA_BLOB blob)
91
data = asn1_init(talloc_tos());
93
return data_blob_null;
96
asn1_push_tag(data, ASN1_APPLICATION(0));
97
asn1_write_OID(data,OID_SPNEGO);
98
asn1_push_tag(data, ASN1_CONTEXT(0));
99
asn1_push_tag(data, ASN1_SEQUENCE(0));
101
asn1_push_tag(data, ASN1_CONTEXT(0));
102
asn1_push_tag(data, ASN1_SEQUENCE(0));
103
asn1_write_OID(data, OID);
107
asn1_push_tag(data, ASN1_CONTEXT(2));
108
asn1_write_OctetString(data,blob.data,blob.length);
116
if (data->has_error) {
117
DEBUG(1,("Failed to build negTokenInit at offset %d\n", (int)data->ofs));
120
ret = data_blob(data->data, data->length);
127
parse a negTokenInit packet giving a GUID, a list of supported
128
OIDs (the mechanisms) and a principal name string
130
bool spnego_parse_negTokenInit(DATA_BLOB blob,
131
char *OIDs[ASN1_MAX_OIDS],
138
data = asn1_init(talloc_tos());
143
asn1_load(data, blob);
145
asn1_start_tag(data,ASN1_APPLICATION(0));
147
asn1_check_OID(data,OID_SPNEGO);
148
asn1_start_tag(data,ASN1_CONTEXT(0));
149
asn1_start_tag(data,ASN1_SEQUENCE(0));
151
asn1_start_tag(data,ASN1_CONTEXT(0));
152
asn1_start_tag(data,ASN1_SEQUENCE(0));
153
for (i=0; asn1_tag_remaining(data) > 0 && i < ASN1_MAX_OIDS-1; i++) {
154
const char *oid_str = NULL;
155
asn1_read_OID(data,NULL,&oid_str);
156
OIDs[i] = CONST_DISCARD(char *, oid_str);
163
if (asn1_tag_remaining(data) > 0) {
164
asn1_start_tag(data, ASN1_CONTEXT(3));
165
asn1_start_tag(data, ASN1_SEQUENCE(0));
166
asn1_start_tag(data, ASN1_CONTEXT(0));
167
asn1_read_GeneralString(data,NULL,principal);
178
ret = !data->has_error;
179
if (data->has_error) {
181
TALLOC_FREE(*principal);
182
for(j = 0; j < i && j < ASN1_MAX_OIDS-1; j++) {
183
TALLOC_FREE(OIDs[j]);
192
generate a negTokenTarg packet given a list of OIDs and a security blob
194
DATA_BLOB gen_negTokenTarg(const char *OIDs[], DATA_BLOB blob)
200
data = asn1_init(talloc_tos());
202
return data_blob_null;
205
asn1_push_tag(data, ASN1_APPLICATION(0));
206
asn1_write_OID(data,OID_SPNEGO);
207
asn1_push_tag(data, ASN1_CONTEXT(0));
208
asn1_push_tag(data, ASN1_SEQUENCE(0));
210
asn1_push_tag(data, ASN1_CONTEXT(0));
211
asn1_push_tag(data, ASN1_SEQUENCE(0));
212
for (i=0; OIDs[i]; i++) {
213
asn1_write_OID(data,OIDs[i]);
218
asn1_push_tag(data, ASN1_CONTEXT(2));
219
asn1_write_OctetString(data,blob.data,blob.length);
227
if (data->has_error) {
228
DEBUG(1,("Failed to build negTokenTarg at offset %d\n", (int)data->ofs));
231
ret = data_blob(data->data, data->length);
238
parse a negTokenTarg packet giving a list of OIDs and a security blob
240
bool parse_negTokenTarg(DATA_BLOB blob, char *OIDs[ASN1_MAX_OIDS], DATA_BLOB *secblob)
245
data = asn1_init(talloc_tos());
250
asn1_load(data, blob);
251
asn1_start_tag(data, ASN1_APPLICATION(0));
252
asn1_check_OID(data,OID_SPNEGO);
253
asn1_start_tag(data, ASN1_CONTEXT(0));
254
asn1_start_tag(data, ASN1_SEQUENCE(0));
256
asn1_start_tag(data, ASN1_CONTEXT(0));
257
asn1_start_tag(data, ASN1_SEQUENCE(0));
258
for (i=0; asn1_tag_remaining(data) > 0 && i < ASN1_MAX_OIDS-1; i++) {
259
const char *oid_str = NULL;
260
asn1_read_OID(data,NULL,&oid_str);
261
OIDs[i] = CONST_DISCARD(char *, oid_str);
267
/* Skip any optional req_flags that are sent per RFC 4178 */
268
if (asn1_peek_tag(data, ASN1_CONTEXT(1))) {
271
asn1_start_tag(data, ASN1_CONTEXT(1));
272
asn1_start_tag(data, ASN1_BITFIELD);
273
while (asn1_tag_remaining(data) > 0)
274
asn1_read_uint8(data, &flags);
279
asn1_start_tag(data, ASN1_CONTEXT(2));
280
asn1_read_OctetString(data,NULL,secblob);
288
if (data->has_error) {
290
data_blob_free(secblob);
291
for(j = 0; j < i && j < ASN1_MAX_OIDS-1; j++) {
292
TALLOC_FREE(OIDs[j]);
294
DEBUG(1,("Failed to parse negTokenTarg at offset %d\n", (int)data->ofs));
304
generate a krb5 GSS-API wrapper packet given a ticket
306
DATA_BLOB spnego_gen_krb5_wrap(const DATA_BLOB ticket, const uint8 tok_id[2])
311
data = asn1_init(talloc_tos());
313
return data_blob_null;
316
asn1_push_tag(data, ASN1_APPLICATION(0));
317
asn1_write_OID(data, OID_KERBEROS5);
319
asn1_write(data, tok_id, 2);
320
asn1_write(data, ticket.data, ticket.length);
323
if (data->has_error) {
324
DEBUG(1,("Failed to build krb5 wrapper at offset %d\n", (int)data->ofs));
327
ret = data_blob(data->data, data->length);
334
parse a krb5 GSS-API wrapper packet giving a ticket
336
bool spnego_parse_krb5_wrap(DATA_BLOB blob, DATA_BLOB *ticket, uint8 tok_id[2])
342
data = asn1_init(talloc_tos());
347
asn1_load(data, blob);
348
asn1_start_tag(data, ASN1_APPLICATION(0));
349
asn1_check_OID(data, OID_KERBEROS5);
351
data_remaining = asn1_tag_remaining(data);
353
if (data_remaining < 3) {
354
data->has_error = True;
356
asn1_read(data, tok_id, 2);
358
*ticket = data_blob(NULL, data_remaining);
359
asn1_read(data, ticket->data, ticket->length);
364
ret = !data->has_error;
366
if (data->has_error) {
367
data_blob_free(ticket);
377
generate a SPNEGO negTokenTarg packet, ready for a EXTENDED_SECURITY
378
kerberos session setup
380
int spnego_gen_negTokenTarg(const char *principal, int time_offset,
382
DATA_BLOB *session_key_krb5, uint32 extra_ap_opts,
386
DATA_BLOB tkt, tkt_wrapped;
387
const char *krb_mechs[] = {OID_KERBEROS5_OLD, OID_KERBEROS5, OID_NTLMSSP, NULL};
389
/* get a kerberos ticket for the service and extract the session key */
390
retval = cli_krb5_get_ticket(principal, time_offset,
391
&tkt, session_key_krb5, extra_ap_opts, NULL,
397
/* wrap that up in a nice GSS-API wrapping */
398
tkt_wrapped = spnego_gen_krb5_wrap(tkt, TOK_ID_KRB_AP_REQ);
400
/* and wrap that in a shiny SPNEGO wrapper */
401
*targ = gen_negTokenTarg(krb_mechs, tkt_wrapped);
403
data_blob_free(&tkt_wrapped);
404
data_blob_free(&tkt);
411
parse a spnego NTLMSSP challenge packet giving two security blobs
413
bool spnego_parse_challenge(const DATA_BLOB blob,
414
DATA_BLOB *chal1, DATA_BLOB *chal2)
422
data = asn1_init(talloc_tos());
427
asn1_load(data, blob);
428
asn1_start_tag(data,ASN1_CONTEXT(1));
429
asn1_start_tag(data,ASN1_SEQUENCE(0));
431
asn1_start_tag(data,ASN1_CONTEXT(0));
432
asn1_check_enumerated(data,1);
435
asn1_start_tag(data,ASN1_CONTEXT(1));
436
asn1_check_OID(data, OID_NTLMSSP);
439
asn1_start_tag(data,ASN1_CONTEXT(2));
440
asn1_read_OctetString(data, NULL, chal1);
443
/* the second challenge is optional (XP doesn't send it) */
444
if (asn1_tag_remaining(data)) {
445
asn1_start_tag(data,ASN1_CONTEXT(3));
446
asn1_read_OctetString(data, NULL, chal2);
453
ret = !data->has_error;
455
if (data->has_error) {
456
data_blob_free(chal1);
457
data_blob_free(chal2);
466
generate a SPNEGO auth packet. This will contain the encrypted passwords
468
DATA_BLOB spnego_gen_auth(DATA_BLOB blob)
473
data = asn1_init(talloc_tos());
475
return data_blob_null;
478
asn1_push_tag(data, ASN1_CONTEXT(1));
479
asn1_push_tag(data, ASN1_SEQUENCE(0));
480
asn1_push_tag(data, ASN1_CONTEXT(2));
481
asn1_write_OctetString(data,blob.data,blob.length);
486
ret = data_blob(data->data, data->length);
494
parse a SPNEGO auth packet. This contains the encrypted passwords
496
bool spnego_parse_auth(DATA_BLOB blob, DATA_BLOB *auth)
501
len = read_spnego_data(talloc_tos(), blob, &token);
503
DEBUG(3,("spnego_parse_auth: read_spnego_data failed\n"));
507
if (token.type != SPNEGO_NEG_TOKEN_TARG) {
508
DEBUG(3,("spnego_parse_auth: wrong token type: %d\n",
510
free_spnego_data(&token);
514
*auth = data_blob_talloc(talloc_tos(),
515
token.negTokenTarg.responseToken.data,
516
token.negTokenTarg.responseToken.length);
517
free_spnego_data(&token);
523
generate a minimal SPNEGO response packet. Doesn't contain much.
525
DATA_BLOB spnego_gen_auth_response(DATA_BLOB *reply, NTSTATUS nt_status,
532
if (NT_STATUS_IS_OK(nt_status)) {
533
negResult = SPNEGO_NEG_RESULT_ACCEPT;
534
} else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
535
negResult = SPNEGO_NEG_RESULT_INCOMPLETE;
537
negResult = SPNEGO_NEG_RESULT_REJECT;
540
data = asn1_init(talloc_tos());
542
return data_blob_null;
545
asn1_push_tag(data, ASN1_CONTEXT(1));
546
asn1_push_tag(data, ASN1_SEQUENCE(0));
547
asn1_push_tag(data, ASN1_CONTEXT(0));
548
asn1_write_enumerated(data, negResult);
552
asn1_push_tag(data,ASN1_CONTEXT(1));
553
asn1_write_OID(data, mechOID);
557
if (reply && reply->data != NULL) {
558
asn1_push_tag(data,ASN1_CONTEXT(2));
559
asn1_write_OctetString(data, reply->data, reply->length);
566
ret = data_blob(data->data, data->length);
572
parse a SPNEGO auth packet. This contains the encrypted passwords
574
bool spnego_parse_auth_response(DATA_BLOB blob, NTSTATUS nt_status,
581
if (NT_STATUS_IS_OK(nt_status)) {
582
negResult = SPNEGO_NEG_RESULT_ACCEPT;
583
} else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
584
negResult = SPNEGO_NEG_RESULT_INCOMPLETE;
586
negResult = SPNEGO_NEG_RESULT_REJECT;
589
data = asn1_init(talloc_tos());
594
asn1_load(data, blob);
595
asn1_start_tag(data, ASN1_CONTEXT(1));
596
asn1_start_tag(data, ASN1_SEQUENCE(0));
597
asn1_start_tag(data, ASN1_CONTEXT(0));
598
asn1_check_enumerated(data, negResult);
601
*auth = data_blob_null;
603
if (asn1_tag_remaining(data)) {
604
asn1_start_tag(data,ASN1_CONTEXT(1));
605
asn1_check_OID(data, mechOID);
608
if (asn1_tag_remaining(data)) {
609
asn1_start_tag(data,ASN1_CONTEXT(2));
610
asn1_read_OctetString(data, NULL, auth);
613
} else if (negResult == SPNEGO_NEG_RESULT_INCOMPLETE) {
617
/* Binding against Win2K DC returns a duplicate of the responseToken in
618
* the optional mechListMIC field. This is a bug in Win2K. We ignore
619
* this field if it exists. Win2K8 may return a proper mechListMIC at
620
* which point we need to implement the integrity checking. */
621
if (asn1_tag_remaining(data)) {
622
DATA_BLOB mechList = data_blob_null;
623
asn1_start_tag(data, ASN1_CONTEXT(3));
624
asn1_read_OctetString(data, NULL, &mechList);
626
data_blob_free(&mechList);
627
DEBUG(5,("spnego_parse_auth_response received mechListMIC, "
634
if (data->has_error) {
635
DEBUG(3,("spnego_parse_auth_response failed at %d\n", (int)data->ofs));
637
data_blob_free(auth);