s3-kerberos: only use krb5 headers where required.
[samba.git] / source3 / libsmb / clispnego.c
1 /* 
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
7    
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.
12    
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.
17    
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/>.
20 */
21
22 #include "includes.h"
23 #include "../libcli/auth/spnego.h"
24 #include "smb_krb5.h"
25
26 /*
27   generate a negTokenInit packet given a GUID, a list of supported
28   OIDs (the mechanisms) and a principal name string 
29 */
30 DATA_BLOB spnego_gen_negTokenInit(char guid[16], 
31                                   const char *OIDs[], 
32                                   const char *principal)
33 {
34         int i;
35         ASN1_DATA *data;
36         DATA_BLOB ret;
37
38         data = asn1_init(talloc_tos());
39         if (data == NULL) {
40                 return data_blob_null;
41         }
42
43         asn1_write(data, guid, 16);
44         asn1_push_tag(data,ASN1_APPLICATION(0));
45         asn1_write_OID(data,OID_SPNEGO);
46         asn1_push_tag(data,ASN1_CONTEXT(0));
47         asn1_push_tag(data,ASN1_SEQUENCE(0));
48
49         asn1_push_tag(data,ASN1_CONTEXT(0));
50         asn1_push_tag(data,ASN1_SEQUENCE(0));
51         for (i=0; OIDs[i]; i++) {
52                 asn1_write_OID(data,OIDs[i]);
53         }
54         asn1_pop_tag(data);
55         asn1_pop_tag(data);
56
57         asn1_push_tag(data, ASN1_CONTEXT(3));
58         asn1_push_tag(data, ASN1_SEQUENCE(0));
59         asn1_push_tag(data, ASN1_CONTEXT(0));
60         asn1_write_GeneralString(data,principal);
61         asn1_pop_tag(data);
62         asn1_pop_tag(data);
63         asn1_pop_tag(data);
64
65         asn1_pop_tag(data);
66         asn1_pop_tag(data);
67
68         asn1_pop_tag(data);
69
70         if (data->has_error) {
71                 DEBUG(1,("Failed to build negTokenInit at offset %d\n", (int)data->ofs));
72         }
73
74         ret = data_blob(data->data, data->length);
75         asn1_free(data);
76
77         return ret;
78 }
79
80 /*
81   Generate a negTokenInit as used by the client side ... It has a mechType
82   (OID), and a mechToken (a security blob) ... 
83
84   Really, we need to break out the NTLMSSP stuff as well, because it could be
85   raw in the packets!
86 */
87 DATA_BLOB gen_negTokenInit(const char *OID, DATA_BLOB blob)
88 {
89         ASN1_DATA *data;
90         DATA_BLOB ret;
91
92         data = asn1_init(talloc_tos());
93         if (data == NULL) {
94                 return data_blob_null;
95         }
96
97         asn1_push_tag(data, ASN1_APPLICATION(0));
98         asn1_write_OID(data,OID_SPNEGO);
99         asn1_push_tag(data, ASN1_CONTEXT(0));
100         asn1_push_tag(data, ASN1_SEQUENCE(0));
101
102         asn1_push_tag(data, ASN1_CONTEXT(0));
103         asn1_push_tag(data, ASN1_SEQUENCE(0));
104         asn1_write_OID(data, OID);
105         asn1_pop_tag(data);
106         asn1_pop_tag(data);
107
108         asn1_push_tag(data, ASN1_CONTEXT(2));
109         asn1_write_OctetString(data,blob.data,blob.length);
110         asn1_pop_tag(data);
111
112         asn1_pop_tag(data);
113         asn1_pop_tag(data);
114
115         asn1_pop_tag(data);
116
117         if (data->has_error) {
118                 DEBUG(1,("Failed to build negTokenInit at offset %d\n", (int)data->ofs));
119         }
120
121         ret = data_blob(data->data, data->length);
122         asn1_free(data);
123
124         return ret;
125 }
126
127 /*
128   parse a negTokenInit packet giving a GUID, a list of supported
129   OIDs (the mechanisms) and a principal name string 
130 */
131 bool spnego_parse_negTokenInit(DATA_BLOB blob,
132                                char *OIDs[ASN1_MAX_OIDS],
133                                char **principal)
134 {
135         int i;
136         bool ret;
137         ASN1_DATA *data;
138
139         data = asn1_init(talloc_tos());
140         if (data == NULL) {
141                 return false;
142         }
143
144         asn1_load(data, blob);
145
146         asn1_start_tag(data,ASN1_APPLICATION(0));
147
148         asn1_check_OID(data,OID_SPNEGO);
149         asn1_start_tag(data,ASN1_CONTEXT(0));
150         asn1_start_tag(data,ASN1_SEQUENCE(0));
151
152         asn1_start_tag(data,ASN1_CONTEXT(0));
153         asn1_start_tag(data,ASN1_SEQUENCE(0));
154         for (i=0; asn1_tag_remaining(data) > 0 && i < ASN1_MAX_OIDS-1; i++) {
155                 const char *oid_str = NULL;
156                 asn1_read_OID(data,talloc_autofree_context(),&oid_str);
157                 OIDs[i] = CONST_DISCARD(char *, oid_str);
158         }
159         OIDs[i] = NULL;
160         asn1_end_tag(data);
161         asn1_end_tag(data);
162
163         *principal = NULL;
164         if (asn1_tag_remaining(data) > 0) {
165                 asn1_start_tag(data, ASN1_CONTEXT(3));
166                 asn1_start_tag(data, ASN1_SEQUENCE(0));
167                 asn1_start_tag(data, ASN1_CONTEXT(0));
168                 asn1_read_GeneralString(data,talloc_autofree_context(),principal);
169                 asn1_end_tag(data);
170                 asn1_end_tag(data);
171                 asn1_end_tag(data);
172         }
173
174         asn1_end_tag(data);
175         asn1_end_tag(data);
176
177         asn1_end_tag(data);
178
179         ret = !data->has_error;
180         if (data->has_error) {
181                 int j;
182                 TALLOC_FREE(*principal);
183                 for(j = 0; j < i && j < ASN1_MAX_OIDS-1; j++) {
184                         TALLOC_FREE(OIDs[j]);
185                 }
186         }
187
188         asn1_free(data);
189         return ret;
190 }
191
192 /*
193   generate a negTokenTarg packet given a list of OIDs and a security blob
194 */
195 DATA_BLOB gen_negTokenTarg(const char *OIDs[], DATA_BLOB blob)
196 {
197         int i;
198         ASN1_DATA *data;
199         DATA_BLOB ret;
200
201         data = asn1_init(talloc_tos());
202         if (data == NULL) {
203                 return data_blob_null;
204         }
205
206         asn1_push_tag(data, ASN1_APPLICATION(0));
207         asn1_write_OID(data,OID_SPNEGO);
208         asn1_push_tag(data, ASN1_CONTEXT(0));
209         asn1_push_tag(data, ASN1_SEQUENCE(0));
210
211         asn1_push_tag(data, ASN1_CONTEXT(0));
212         asn1_push_tag(data, ASN1_SEQUENCE(0));
213         for (i=0; OIDs[i]; i++) {
214                 asn1_write_OID(data,OIDs[i]);
215         }
216         asn1_pop_tag(data);
217         asn1_pop_tag(data);
218
219         asn1_push_tag(data, ASN1_CONTEXT(2));
220         asn1_write_OctetString(data,blob.data,blob.length);
221         asn1_pop_tag(data);
222
223         asn1_pop_tag(data);
224         asn1_pop_tag(data);
225
226         asn1_pop_tag(data);
227
228         if (data->has_error) {
229                 DEBUG(1,("Failed to build negTokenTarg at offset %d\n", (int)data->ofs));
230         }
231
232         ret = data_blob(data->data, data->length);
233         asn1_free(data);
234
235         return ret;
236 }
237
238 /*
239   parse a negTokenTarg packet giving a list of OIDs and a security blob
240 */
241 bool parse_negTokenTarg(DATA_BLOB blob, char *OIDs[ASN1_MAX_OIDS], DATA_BLOB *secblob)
242 {
243         int i;
244         ASN1_DATA *data;
245
246         data = asn1_init(talloc_tos());
247         if (data == NULL) {
248                 return false;
249         }
250
251         asn1_load(data, blob);
252         asn1_start_tag(data, ASN1_APPLICATION(0));
253         asn1_check_OID(data,OID_SPNEGO);
254         asn1_start_tag(data, ASN1_CONTEXT(0));
255         asn1_start_tag(data, ASN1_SEQUENCE(0));
256
257         asn1_start_tag(data, ASN1_CONTEXT(0));
258         asn1_start_tag(data, ASN1_SEQUENCE(0));
259         for (i=0; asn1_tag_remaining(data) > 0 && i < ASN1_MAX_OIDS-1; i++) {
260                 const char *oid_str = NULL;
261                 asn1_read_OID(data,talloc_autofree_context(),&oid_str);
262                 OIDs[i] = CONST_DISCARD(char *, oid_str);
263         }
264         OIDs[i] = NULL;
265         asn1_end_tag(data);
266         asn1_end_tag(data);
267
268         /* Skip any optional req_flags that are sent per RFC 4178 */
269         if (asn1_peek_tag(data, ASN1_CONTEXT(1))) {
270                 uint8 flags;
271
272                 asn1_start_tag(data, ASN1_CONTEXT(1));
273                 asn1_start_tag(data, ASN1_BIT_STRING);
274                 while (asn1_tag_remaining(data) > 0)
275                         asn1_read_uint8(data, &flags);
276                 asn1_end_tag(data);
277                 asn1_end_tag(data);
278         }
279
280         asn1_start_tag(data, ASN1_CONTEXT(2));
281         asn1_read_OctetString(data,talloc_autofree_context(),secblob);
282         asn1_end_tag(data);
283
284         asn1_end_tag(data);
285         asn1_end_tag(data);
286
287         asn1_end_tag(data);
288
289         if (data->has_error) {
290                 int j;
291                 data_blob_free(secblob);
292                 for(j = 0; j < i && j < ASN1_MAX_OIDS-1; j++) {
293                         TALLOC_FREE(OIDs[j]);
294                 }
295                 DEBUG(1,("Failed to parse negTokenTarg at offset %d\n", (int)data->ofs));
296                 asn1_free(data);
297                 return False;
298         }
299
300         asn1_free(data);
301         return True;
302 }
303
304 /*
305   generate a krb5 GSS-API wrapper packet given a ticket
306 */
307 DATA_BLOB spnego_gen_krb5_wrap(const DATA_BLOB ticket, const uint8 tok_id[2])
308 {
309         ASN1_DATA *data;
310         DATA_BLOB ret;
311
312         data = asn1_init(talloc_tos());
313         if (data == NULL) {
314                 return data_blob_null;
315         }
316
317         asn1_push_tag(data, ASN1_APPLICATION(0));
318         asn1_write_OID(data, OID_KERBEROS5);
319
320         asn1_write(data, tok_id, 2);
321         asn1_write(data, ticket.data, ticket.length);
322         asn1_pop_tag(data);
323
324         if (data->has_error) {
325                 DEBUG(1,("Failed to build krb5 wrapper at offset %d\n", (int)data->ofs));
326         }
327
328         ret = data_blob(data->data, data->length);
329         asn1_free(data);
330
331         return ret;
332 }
333
334 /*
335   parse a krb5 GSS-API wrapper packet giving a ticket
336 */
337 bool spnego_parse_krb5_wrap(DATA_BLOB blob, DATA_BLOB *ticket, uint8 tok_id[2])
338 {
339         bool ret;
340         ASN1_DATA *data;
341         int data_remaining;
342
343         data = asn1_init(talloc_tos());
344         if (data == NULL) {
345                 return false;
346         }
347
348         asn1_load(data, blob);
349         asn1_start_tag(data, ASN1_APPLICATION(0));
350         asn1_check_OID(data, OID_KERBEROS5);
351
352         data_remaining = asn1_tag_remaining(data);
353
354         if (data_remaining < 3) {
355                 data->has_error = True;
356         } else {
357                 asn1_read(data, tok_id, 2);
358                 data_remaining -= 2;
359                 *ticket = data_blob(NULL, data_remaining);
360                 asn1_read(data, ticket->data, ticket->length);
361         }
362
363         asn1_end_tag(data);
364
365         ret = !data->has_error;
366
367         if (data->has_error) {
368                 data_blob_free(ticket);
369         }
370
371         asn1_free(data);
372
373         return ret;
374 }
375
376
377 /* 
378    generate a SPNEGO negTokenTarg packet, ready for a EXTENDED_SECURITY
379    kerberos session setup 
380 */
381 int spnego_gen_negTokenTarg(const char *principal, int time_offset, 
382                             DATA_BLOB *targ, 
383                             DATA_BLOB *session_key_krb5, uint32 extra_ap_opts,
384                             time_t *expire_time)
385 {
386         int retval;
387         DATA_BLOB tkt, tkt_wrapped;
388         const char *krb_mechs[] = {OID_KERBEROS5_OLD, OID_KERBEROS5, OID_NTLMSSP, NULL};
389
390         /* get a kerberos ticket for the service and extract the session key */
391         retval = cli_krb5_get_ticket(principal, time_offset,
392                                         &tkt, session_key_krb5, extra_ap_opts, NULL, 
393                                         expire_time, NULL);
394
395         if (retval)
396                 return retval;
397
398         /* wrap that up in a nice GSS-API wrapping */
399         tkt_wrapped = spnego_gen_krb5_wrap(tkt, TOK_ID_KRB_AP_REQ);
400
401         /* and wrap that in a shiny SPNEGO wrapper */
402         *targ = gen_negTokenTarg(krb_mechs, tkt_wrapped);
403
404         data_blob_free(&tkt_wrapped);
405         data_blob_free(&tkt);
406
407         return retval;
408 }
409
410
411 /*
412   parse a spnego NTLMSSP challenge packet giving two security blobs
413 */
414 bool spnego_parse_challenge(const DATA_BLOB blob,
415                             DATA_BLOB *chal1, DATA_BLOB *chal2)
416 {
417         bool ret;
418         ASN1_DATA *data;
419
420         ZERO_STRUCTP(chal1);
421         ZERO_STRUCTP(chal2);
422
423         data = asn1_init(talloc_tos());
424         if (data == NULL) {
425                 return false;
426         }
427
428         asn1_load(data, blob);
429         asn1_start_tag(data,ASN1_CONTEXT(1));
430         asn1_start_tag(data,ASN1_SEQUENCE(0));
431
432         asn1_start_tag(data,ASN1_CONTEXT(0));
433         asn1_check_enumerated(data,1);
434         asn1_end_tag(data);
435
436         asn1_start_tag(data,ASN1_CONTEXT(1));
437         asn1_check_OID(data, OID_NTLMSSP);
438         asn1_end_tag(data);
439
440         asn1_start_tag(data,ASN1_CONTEXT(2));
441         asn1_read_OctetString(data, talloc_autofree_context(), chal1);
442         asn1_end_tag(data);
443
444         /* the second challenge is optional (XP doesn't send it) */
445         if (asn1_tag_remaining(data)) {
446                 asn1_start_tag(data,ASN1_CONTEXT(3));
447                 asn1_read_OctetString(data, talloc_autofree_context(), chal2);
448                 asn1_end_tag(data);
449         }
450
451         asn1_end_tag(data);
452         asn1_end_tag(data);
453
454         ret = !data->has_error;
455
456         if (data->has_error) {
457                 data_blob_free(chal1);
458                 data_blob_free(chal2);
459         }
460
461         asn1_free(data);
462         return ret;
463 }
464
465
466 /*
467  generate a SPNEGO auth packet. This will contain the encrypted passwords
468 */
469 DATA_BLOB spnego_gen_auth(DATA_BLOB blob)
470 {
471         ASN1_DATA *data;
472         DATA_BLOB ret;
473
474         data = asn1_init(talloc_tos());
475         if (data == NULL) {
476                 return data_blob_null;
477         }
478
479         asn1_push_tag(data, ASN1_CONTEXT(1));
480         asn1_push_tag(data, ASN1_SEQUENCE(0));
481         asn1_push_tag(data, ASN1_CONTEXT(2));
482         asn1_write_OctetString(data,blob.data,blob.length);
483         asn1_pop_tag(data);
484         asn1_pop_tag(data);
485         asn1_pop_tag(data);
486
487         ret = data_blob(data->data, data->length);
488
489         asn1_free(data);
490
491         return ret;
492 }
493
494 /*
495  parse a SPNEGO auth packet. This contains the encrypted passwords
496 */
497 bool spnego_parse_auth(DATA_BLOB blob, DATA_BLOB *auth)
498 {
499         ssize_t len;
500         struct spnego_data token;
501
502         len = spnego_read_data(talloc_tos(), blob, &token);
503         if (len == -1) {
504                 DEBUG(3,("spnego_parse_auth: spnego_read_data failed\n"));
505                 return false;
506         }
507
508         if (token.type != SPNEGO_NEG_TOKEN_TARG) {
509                 DEBUG(3,("spnego_parse_auth: wrong token type: %d\n",
510                         token.type));
511                 spnego_free_data(&token);
512                 return false;
513         }
514
515         *auth = data_blob_talloc(talloc_tos(),
516                                  token.negTokenTarg.responseToken.data,
517                                  token.negTokenTarg.responseToken.length);
518         spnego_free_data(&token);
519
520         return true;
521 }
522
523 /*
524   generate a minimal SPNEGO response packet.  Doesn't contain much.
525 */
526 DATA_BLOB spnego_gen_auth_response(DATA_BLOB *reply, NTSTATUS nt_status,
527                                    const char *mechOID)
528 {
529         ASN1_DATA *data;
530         DATA_BLOB ret;
531         uint8 negResult;
532
533         if (NT_STATUS_IS_OK(nt_status)) {
534                 negResult = SPNEGO_ACCEPT_COMPLETED;
535         } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
536                 negResult = SPNEGO_ACCEPT_INCOMPLETE;
537         } else {
538                 negResult = SPNEGO_REJECT;
539         }
540
541         data = asn1_init(talloc_tos());
542         if (data == NULL) {
543                 return data_blob_null;
544         }
545
546         asn1_push_tag(data, ASN1_CONTEXT(1));
547         asn1_push_tag(data, ASN1_SEQUENCE(0));
548         asn1_push_tag(data, ASN1_CONTEXT(0));
549         asn1_write_enumerated(data, negResult);
550         asn1_pop_tag(data);
551
552         if (mechOID) {
553                 asn1_push_tag(data,ASN1_CONTEXT(1));
554                 asn1_write_OID(data, mechOID);
555                 asn1_pop_tag(data);
556         }
557
558         if (reply && reply->data != NULL) {
559                 asn1_push_tag(data,ASN1_CONTEXT(2));
560                 asn1_write_OctetString(data, reply->data, reply->length);
561                 asn1_pop_tag(data);
562         }
563
564         asn1_pop_tag(data);
565         asn1_pop_tag(data);
566
567         ret = data_blob(data->data, data->length);
568         asn1_free(data);
569         return ret;
570 }
571
572 /*
573  parse a SPNEGO auth packet. This contains the encrypted passwords
574 */
575 bool spnego_parse_auth_response(DATA_BLOB blob, NTSTATUS nt_status,
576                                 const char *mechOID,
577                                 DATA_BLOB *auth)
578 {
579         ASN1_DATA *data;
580         uint8 negResult;
581
582         if (NT_STATUS_IS_OK(nt_status)) {
583                 negResult = SPNEGO_ACCEPT_COMPLETED;
584         } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
585                 negResult = SPNEGO_ACCEPT_INCOMPLETE;
586         } else {
587                 negResult = SPNEGO_REJECT;
588         }
589
590         data = asn1_init(talloc_tos());
591         if (data == NULL) {
592                 return false;
593         }
594
595         asn1_load(data, blob);
596         asn1_start_tag(data, ASN1_CONTEXT(1));
597         asn1_start_tag(data, ASN1_SEQUENCE(0));
598         asn1_start_tag(data, ASN1_CONTEXT(0));
599         asn1_check_enumerated(data, negResult);
600         asn1_end_tag(data);
601
602         *auth = data_blob_null;
603
604         if (asn1_tag_remaining(data)) {
605                 asn1_start_tag(data,ASN1_CONTEXT(1));
606                 asn1_check_OID(data, mechOID);
607                 asn1_end_tag(data);
608
609                 if (asn1_tag_remaining(data)) {
610                         asn1_start_tag(data,ASN1_CONTEXT(2));
611                         asn1_read_OctetString(data, talloc_autofree_context(), auth);
612                         asn1_end_tag(data);
613                 }
614         } else if (negResult == SPNEGO_ACCEPT_INCOMPLETE) {
615                 data->has_error = 1;
616         }
617
618         /* Binding against Win2K DC returns a duplicate of the responseToken in
619          * the optional mechListMIC field. This is a bug in Win2K. We ignore
620          * this field if it exists. Win2K8 may return a proper mechListMIC at
621          * which point we need to implement the integrity checking. */
622         if (asn1_tag_remaining(data)) {
623                 DATA_BLOB mechList = data_blob_null;
624                 asn1_start_tag(data, ASN1_CONTEXT(3));
625                 asn1_read_OctetString(data, talloc_autofree_context(), &mechList);
626                 asn1_end_tag(data);
627                 data_blob_free(&mechList);
628                 DEBUG(5,("spnego_parse_auth_response received mechListMIC, "
629                     "ignoring.\n"));
630         }
631
632         asn1_end_tag(data);
633         asn1_end_tag(data);
634
635         if (data->has_error) {
636                 DEBUG(3,("spnego_parse_auth_response failed at %d\n", (int)data->ofs));
637                 asn1_free(data);
638                 data_blob_free(auth);
639                 return False;
640         }
641
642         asn1_free(data);
643         return True;
644 }