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