Removed version number from file header.
[samba.git] / source / libsmb / clispnego.c
1 /* 
2    Unix SMB/CIFS implementation.
3    simple kerberos5/SPNEGO routines
4    Copyright (C) Andrew Tridgell 2001
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include "includes.h"
22
23 /*
24   generate a negTokenInit packet given a GUID, a list of supported
25   OIDs (the mechanisms) and a principal name string 
26 */
27 DATA_BLOB spnego_gen_negTokenInit(uint8 guid[16], 
28                                   const char *OIDs[], 
29                                   const char *principal)
30 {
31         int i;
32         ASN1_DATA data;
33         DATA_BLOB ret;
34
35         memset(&data, 0, sizeof(data));
36
37         asn1_write(&data, guid, 16);
38         asn1_push_tag(&data,ASN1_APPLICATION(0));
39         asn1_write_OID(&data,OID_SPNEGO);
40         asn1_push_tag(&data,ASN1_CONTEXT(0));
41         asn1_push_tag(&data,ASN1_SEQUENCE(0));
42
43         asn1_push_tag(&data,ASN1_CONTEXT(0));
44         asn1_push_tag(&data,ASN1_SEQUENCE(0));
45         for (i=0; OIDs[i]; i++) {
46                 asn1_write_OID(&data,OIDs[i]);
47         }
48         asn1_pop_tag(&data);
49         asn1_pop_tag(&data);
50
51         asn1_push_tag(&data, ASN1_CONTEXT(3));
52         asn1_push_tag(&data, ASN1_SEQUENCE(0));
53         asn1_push_tag(&data, ASN1_CONTEXT(0));
54         asn1_write_GeneralString(&data,principal);
55         asn1_pop_tag(&data);
56         asn1_pop_tag(&data);
57         asn1_pop_tag(&data);
58
59         asn1_pop_tag(&data);
60         asn1_pop_tag(&data);
61
62         asn1_pop_tag(&data);
63
64         if (data.has_error) {
65                 DEBUG(1,("Failed to build negTokenInit at offset %d\n", (int)data.ofs));
66                 asn1_free(&data);
67         }
68
69         ret = data_blob(data.data, data.length);
70         asn1_free(&data);
71
72         return ret;
73 }
74
75
76 /*
77   parse a negTokenInit packet giving a GUID, a list of supported
78   OIDs (the mechanisms) and a principal name string 
79 */
80 BOOL spnego_parse_negTokenInit(DATA_BLOB blob,
81                                uint8 guid[16], 
82                                char *OIDs[ASN1_MAX_OIDS], 
83                                char **principal)
84 {
85         int i;
86         BOOL ret;
87         ASN1_DATA data;
88
89         asn1_load(&data, blob);
90
91         asn1_read(&data, guid, 16);
92         asn1_start_tag(&data,ASN1_APPLICATION(0));
93         asn1_check_OID(&data,OID_SPNEGO);
94         asn1_start_tag(&data,ASN1_CONTEXT(0));
95         asn1_start_tag(&data,ASN1_SEQUENCE(0));
96
97         asn1_start_tag(&data,ASN1_CONTEXT(0));
98         asn1_start_tag(&data,ASN1_SEQUENCE(0));
99         for (i=0; asn1_tag_remaining(&data) > 0 && i < ASN1_MAX_OIDS; i++) {
100                 char *oid = NULL;
101                 asn1_read_OID(&data,&oid);
102                 OIDs[i] = oid;
103         }
104         OIDs[i] = NULL;
105         asn1_end_tag(&data);
106         asn1_end_tag(&data);
107
108         asn1_start_tag(&data, ASN1_CONTEXT(3));
109         asn1_start_tag(&data, ASN1_SEQUENCE(0));
110         asn1_start_tag(&data, ASN1_CONTEXT(0));
111         asn1_read_GeneralString(&data,principal);
112         asn1_end_tag(&data);
113         asn1_end_tag(&data);
114         asn1_end_tag(&data);
115
116         asn1_end_tag(&data);
117         asn1_end_tag(&data);
118
119         asn1_end_tag(&data);
120
121         ret = !data.has_error;
122         asn1_free(&data);
123         return ret;
124 }
125
126
127 /*
128   generate a negTokenTarg packet given a list of OIDs and a security blob
129 */
130 DATA_BLOB gen_negTokenTarg(const char *OIDs[], DATA_BLOB blob)
131 {
132         int i;
133         ASN1_DATA data;
134         DATA_BLOB ret;
135
136         memset(&data, 0, sizeof(data));
137
138         asn1_push_tag(&data, ASN1_APPLICATION(0));
139         asn1_write_OID(&data,OID_SPNEGO);
140         asn1_push_tag(&data, ASN1_CONTEXT(0));
141         asn1_push_tag(&data, ASN1_SEQUENCE(0));
142
143         asn1_push_tag(&data, ASN1_CONTEXT(0));
144         asn1_push_tag(&data, ASN1_SEQUENCE(0));
145         for (i=0; OIDs[i]; i++) {
146                 asn1_write_OID(&data,OIDs[i]);
147         }
148         asn1_pop_tag(&data);
149         asn1_pop_tag(&data);
150
151         asn1_push_tag(&data, ASN1_CONTEXT(2));
152         asn1_write_OctetString(&data,blob.data,blob.length);
153         asn1_pop_tag(&data);
154
155         asn1_pop_tag(&data);
156         asn1_pop_tag(&data);
157
158         asn1_pop_tag(&data);
159
160         if (data.has_error) {
161                 DEBUG(1,("Failed to build negTokenTarg at offset %d\n", (int)data.ofs));
162                 asn1_free(&data);
163         }
164
165         ret = data_blob(data.data, data.length);
166         asn1_free(&data);
167
168         return ret;
169 }
170
171
172 /*
173   parse a negTokenTarg packet giving a list of OIDs and a security blob
174 */
175 BOOL parse_negTokenTarg(DATA_BLOB blob, char *OIDs[ASN1_MAX_OIDS], DATA_BLOB *secblob)
176 {
177         int i;
178         ASN1_DATA data;
179
180         asn1_load(&data, blob);
181         asn1_start_tag(&data, ASN1_APPLICATION(0));
182         asn1_check_OID(&data,OID_SPNEGO);
183         asn1_start_tag(&data, ASN1_CONTEXT(0));
184         asn1_start_tag(&data, ASN1_SEQUENCE(0));
185
186         asn1_start_tag(&data, ASN1_CONTEXT(0));
187         asn1_start_tag(&data, ASN1_SEQUENCE(0));
188         for (i=0; asn1_tag_remaining(&data) > 0 && i < ASN1_MAX_OIDS; i++) {
189                 char *oid = NULL;
190                 asn1_read_OID(&data,&oid);
191                 OIDs[i] = oid;
192         }
193         OIDs[i] = NULL;
194         asn1_end_tag(&data);
195         asn1_end_tag(&data);
196
197         asn1_start_tag(&data, ASN1_CONTEXT(2));
198         asn1_read_OctetString(&data,secblob);
199         asn1_end_tag(&data);
200
201         asn1_end_tag(&data);
202         asn1_end_tag(&data);
203
204         asn1_end_tag(&data);
205
206         if (data.has_error) {
207                 DEBUG(1,("Failed to parse negTokenTarg at offset %d\n", (int)data.ofs));
208                 asn1_free(&data);
209                 return False;
210         }
211
212         asn1_free(&data);
213         return True;
214 }
215
216 /*
217   generate a krb5 GSS-API wrapper packet given a ticket
218 */
219 DATA_BLOB spnego_gen_krb5_wrap(DATA_BLOB ticket)
220 {
221         ASN1_DATA data;
222         DATA_BLOB ret;
223
224         memset(&data, 0, sizeof(data));
225
226         asn1_push_tag(&data, ASN1_APPLICATION(0));
227         asn1_write_OID(&data, OID_KERBEROS5);
228         asn1_write_BOOLEAN(&data, 0);
229         asn1_write(&data, ticket.data, ticket.length);
230         asn1_pop_tag(&data);
231
232         if (data.has_error) {
233                 DEBUG(1,("Failed to build krb5 wrapper at offset %d\n", (int)data.ofs));
234                 asn1_free(&data);
235         }
236
237         ret = data_blob(data.data, data.length);
238         asn1_free(&data);
239
240         return ret;
241 }
242
243 /*
244   parse a krb5 GSS-API wrapper packet giving a ticket
245 */
246 BOOL spnego_parse_krb5_wrap(DATA_BLOB blob, DATA_BLOB *ticket)
247 {
248         BOOL ret;
249         ASN1_DATA data;
250
251         asn1_load(&data, blob);
252         asn1_start_tag(&data, ASN1_APPLICATION(0));
253         asn1_check_OID(&data, OID_KERBEROS5);
254         asn1_check_BOOLEAN(&data, 0);
255         *ticket = data_blob(data.data, asn1_tag_remaining(&data));
256         asn1_read(&data, ticket->data, ticket->length);
257         asn1_end_tag(&data);
258
259         ret = !data.has_error;
260
261         asn1_free(&data);
262
263         return ret;
264 }
265
266
267 /* 
268    generate a SPNEGO negTokenTarg packet, ready for a EXTENDED_SECURITY
269    kerberos session setup 
270 */
271 DATA_BLOB spnego_gen_negTokenTarg(struct cli_state *cli, char *principal)
272 {
273         DATA_BLOB tkt, tkt_wrapped, targ;
274         const char *krb_mechs[] = {OID_KERBEROS5_OLD, OID_NTLMSSP, NULL};
275
276         /* get a kerberos ticket for the service */
277         tkt = krb5_get_ticket(principal);
278
279         /* wrap that up in a nice GSS-API wrapping */
280         tkt_wrapped = spnego_gen_krb5_wrap(tkt);
281
282         /* and wrap that in a shiny SPNEGO wrapper */
283         targ = gen_negTokenTarg(krb_mechs, tkt_wrapped);
284
285         data_blob_free(&tkt_wrapped);
286         data_blob_free(&tkt);
287
288         return targ;
289 }
290
291
292 /*
293   parse a spnego NTLMSSP challenge packet giving two security blobs
294 */
295 BOOL spnego_parse_challenge(DATA_BLOB blob,
296                             DATA_BLOB *chal1, DATA_BLOB *chal2)
297 {
298         BOOL ret;
299         ASN1_DATA data;
300
301         ZERO_STRUCTP(chal1);
302         ZERO_STRUCTP(chal2);
303
304         asn1_load(&data, blob);
305         asn1_start_tag(&data,ASN1_CONTEXT(1));
306         asn1_start_tag(&data,ASN1_SEQUENCE(0));
307
308         asn1_start_tag(&data,ASN1_CONTEXT(0));
309         asn1_check_enumerated(&data,1);
310         asn1_end_tag(&data);
311
312         asn1_start_tag(&data,ASN1_CONTEXT(1));
313         asn1_check_OID(&data, OID_NTLMSSP);
314         asn1_end_tag(&data);
315
316         asn1_start_tag(&data,ASN1_CONTEXT(2));
317         asn1_read_OctetString(&data, chal1);
318         asn1_end_tag(&data);
319
320         /* the second challenge is optional (XP doesn't send it) */
321         if (asn1_tag_remaining(&data)) {
322                 asn1_start_tag(&data,ASN1_CONTEXT(3));
323                 asn1_read_OctetString(&data, chal2);
324                 asn1_end_tag(&data);
325         }
326
327         asn1_end_tag(&data);
328         asn1_end_tag(&data);
329
330         ret = !data.has_error;
331         asn1_free(&data);
332         return ret;
333 }
334
335
336 /*
337   generate a spnego NTLMSSP challenge packet given two security blobs
338   The second challenge is optional
339 */
340 BOOL spnego_gen_challenge(DATA_BLOB *blob,
341                           DATA_BLOB *chal1, DATA_BLOB *chal2)
342 {
343         ASN1_DATA data;
344
345         ZERO_STRUCT(data);
346
347         asn1_push_tag(&data,ASN1_CONTEXT(1));
348         asn1_push_tag(&data,ASN1_SEQUENCE(0));
349
350         asn1_push_tag(&data,ASN1_CONTEXT(0));
351         asn1_write_enumerated(&data,1);
352         asn1_pop_tag(&data);
353
354         asn1_push_tag(&data,ASN1_CONTEXT(1));
355         asn1_write_OID(&data, OID_NTLMSSP);
356         asn1_pop_tag(&data);
357
358         asn1_push_tag(&data,ASN1_CONTEXT(2));
359         asn1_write_OctetString(&data, chal1->data, chal1->length);
360         asn1_pop_tag(&data);
361
362         /* the second challenge is optional (XP doesn't send it) */
363         if (chal2) {
364                 asn1_push_tag(&data,ASN1_CONTEXT(3));
365                 asn1_write_OctetString(&data, chal2->data, chal2->length);
366                 asn1_pop_tag(&data);
367         }
368
369         asn1_pop_tag(&data);
370         asn1_pop_tag(&data);
371
372         if (data.has_error) {
373                 return False;
374         }
375
376         *blob = data_blob(data.data, data.length);
377         asn1_free(&data);
378         return True;
379 }
380
381 /*
382  generate a SPNEGO NTLMSSP auth packet. This will contain the encrypted passwords
383 */
384 DATA_BLOB spnego_gen_auth(DATA_BLOB blob)
385 {
386         ASN1_DATA data;
387         DATA_BLOB ret;
388
389         memset(&data, 0, sizeof(data));
390
391         asn1_push_tag(&data, ASN1_CONTEXT(1));
392         asn1_push_tag(&data, ASN1_SEQUENCE(0));
393         asn1_push_tag(&data, ASN1_CONTEXT(2));
394         asn1_write_OctetString(&data,blob.data,blob.length);    
395         asn1_pop_tag(&data);
396         asn1_pop_tag(&data);
397         asn1_pop_tag(&data);
398
399         ret = data_blob(data.data, data.length);
400
401         asn1_free(&data);
402
403         return ret;
404 }
405
406 /*
407  parse a SPNEGO NTLMSSP auth packet. This contains the encrypted passwords
408 */
409 BOOL spnego_parse_auth(DATA_BLOB blob, DATA_BLOB *auth)
410 {
411         ASN1_DATA data;
412
413         asn1_load(&data, blob);
414         asn1_start_tag(&data, ASN1_CONTEXT(1));
415         asn1_start_tag(&data, ASN1_SEQUENCE(0));
416         asn1_start_tag(&data, ASN1_CONTEXT(2));
417         asn1_read_OctetString(&data,auth);
418         asn1_end_tag(&data);
419         asn1_end_tag(&data);
420         asn1_end_tag(&data);
421
422         if (data.has_error) {
423                 DEBUG(3,("spnego_parse_auth failed at %d\n", (int)data.ofs));
424                 asn1_free(&data);
425                 return False;
426         }
427
428         asn1_free(&data);
429         return True;
430 }
431
432
433 /*
434   this is a tiny msrpc packet generator. I am only using this to
435   avoid tying this code to a particular varient of our rpc code. This
436   generator is not general enough for all our rpc needs, its just
437   enough for the spnego/ntlmssp code
438
439   format specifiers are:
440
441   U = unicode string (input is unix string)
442   B = data blob (pointer + length)
443   b = data blob in header (pointer + length)
444   d = word (4 bytes)
445   C = constant ascii string
446  */
447 BOOL msrpc_gen(DATA_BLOB *blob,
448                const char *format, ...)
449 {
450         int i, n;
451         va_list ap;
452         char *s;
453         uint8 *b;
454         int head_size=0, data_size=0;
455         int head_ofs, data_ofs;
456
457         /* first scan the format to work out the header and body size */
458         va_start(ap, format);
459         for (i=0; format[i]; i++) {
460                 switch (format[i]) {
461                 case 'U':
462                         s = va_arg(ap, char *);
463                         head_size += 8;
464                         data_size += str_charnum(s) * 2;
465                         break;
466                 case 'B':
467                         b = va_arg(ap, uint8 *);
468                         head_size += 8;
469                         data_size += va_arg(ap, int);
470                         break;
471                 case 'b':
472                         b = va_arg(ap, uint8 *);
473                         head_size += va_arg(ap, int);
474                         break;
475                 case 'd':
476                         n = va_arg(ap, int);
477                         head_size += 4;
478                         break;
479                 case 'C':
480                         s = va_arg(ap, char *);
481                         head_size += str_charnum(s) + 1;
482                         break;
483                 }
484         }
485         va_end(ap);
486
487         /* allocate the space, then scan the format again to fill in the values */
488         *blob = data_blob(NULL, head_size + data_size);
489
490         head_ofs = 0;
491         data_ofs = head_size;
492
493         va_start(ap, format);
494         for (i=0; format[i]; i++) {
495                 switch (format[i]) {
496                 case 'U':
497                         s = va_arg(ap, char *);
498                         n = str_charnum(s);
499                         SSVAL(blob->data, head_ofs, n*2); head_ofs += 2;
500                         SSVAL(blob->data, head_ofs, n*2); head_ofs += 2;
501                         SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4;
502                         push_string(NULL, blob->data+data_ofs, s, n*2, STR_UNICODE|STR_NOALIGN);
503                         data_ofs += n*2;
504                         break;
505                 case 'B':
506                         b = va_arg(ap, uint8 *);
507                         n = va_arg(ap, int);
508                         SSVAL(blob->data, head_ofs, n); head_ofs += 2;
509                         SSVAL(blob->data, head_ofs, n); head_ofs += 2;
510                         SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4;
511                         memcpy(blob->data+data_ofs, b, n);
512                         data_ofs += n;
513                         break;
514                 case 'd':
515                         n = va_arg(ap, int);
516                         SIVAL(blob->data, head_ofs, n); head_ofs += 4;
517                         break;
518                 case 'b':
519                         b = va_arg(ap, uint8 *);
520                         n = va_arg(ap, int);
521                         memcpy(blob->data + head_ofs, b, n);
522                         head_ofs += n;
523                         break;
524                 case 'C':
525                         s = va_arg(ap, char *);
526                         head_ofs += push_string(NULL, blob->data+head_ofs, s, -1, 
527                                                 STR_ASCII|STR_TERMINATE);
528                         break;
529                 }
530         }
531         va_end(ap);
532
533         return True;
534 }
535
536
537 /*
538   this is a tiny msrpc packet parser. This the the partner of msrpc_gen
539
540   format specifiers are:
541
542   U = unicode string (input is unix string)
543   B = data blob
544   b = data blob in header
545   d = word (4 bytes)
546   C = constant ascii string
547  */
548 BOOL msrpc_parse(DATA_BLOB *blob,
549                  const char *format, ...)
550 {
551         int i;
552         va_list ap;
553         char **ps, *s;
554         DATA_BLOB *b;
555         int head_ofs = 0;
556         uint16 len1, len2;
557         uint32 ptr;
558         uint32 *v;
559         pstring p;
560
561         va_start(ap, format);
562         for (i=0; format[i]; i++) {
563                 switch (format[i]) {
564                 case 'U':
565                         len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
566                         len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
567                         ptr =  IVAL(blob->data, head_ofs); head_ofs += 4;
568                         /* make sure its in the right format - be strict */
569                         if (len1 != len2 || (len1&1) || ptr + len1 > blob->length) {
570                                 return False;
571                         }
572                         ps = va_arg(ap, char **);
573                         pull_string(NULL, p, blob->data + ptr, -1, len1, 
574                                     STR_UNICODE|STR_NOALIGN);
575                         (*ps) = strdup(p);
576                         break;
577                 case 'B':
578                         len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
579                         len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
580                         ptr =  IVAL(blob->data, head_ofs); head_ofs += 4;
581                         /* make sure its in the right format - be strict */
582                         if (len1 != len2 || ptr + len1 > blob->length) {
583                                 return False;
584                         }
585                         b = (DATA_BLOB *)va_arg(ap, void *);
586                         *b = data_blob(blob->data + ptr, len1);
587                         break;
588                 case 'b':
589                         b = (DATA_BLOB *)va_arg(ap, void *);
590                         len1 = va_arg(ap, unsigned);
591                         *b = data_blob(blob->data + head_ofs, len1);
592                         head_ofs += len1;
593                         break;
594                 case 'd':
595                         v = va_arg(ap, uint32 *);
596                         *v = IVAL(blob->data, head_ofs); head_ofs += 4;
597                         break;
598                 case 'C':
599                         s = va_arg(ap, char *);
600                         head_ofs += pull_string(NULL, p, blob->data+head_ofs, -1, 
601                                                 blob->length - head_ofs, 
602                                                 STR_ASCII|STR_TERMINATE);
603                         if (strcmp(s, p) != 0) {
604                                 return False;
605                         }
606                         break;
607                 }
608         }
609         va_end(ap);
610
611         return True;
612 }