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