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