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