Ok, I know it's a language thing and it shouldn't matter.... but a kerberos
[samba.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 static 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 = malloc(head_size + data_size);
490         blob->length = head_size + data_size;
491         if (!blob->data) return False;
492
493         head_ofs = 0;
494         data_ofs = head_size;
495
496         va_start(ap, format);
497         for (i=0; format[i]; i++) {
498                 switch (format[i]) {
499                 case 'U':
500                         s = va_arg(ap, char *);
501                         n = str_charnum(s);
502                         SSVAL(blob->data, head_ofs, n*2); head_ofs += 2;
503                         SSVAL(blob->data, head_ofs, n*2); head_ofs += 2;
504                         SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4;
505                         push_string(NULL, blob->data+data_ofs, s, n*2, STR_UNICODE|STR_NOALIGN);
506                         data_ofs += n*2;
507                         break;
508                 case 'B':
509                         b = va_arg(ap, uint8 *);
510                         n = va_arg(ap, int);
511                         SSVAL(blob->data, head_ofs, n); head_ofs += 2;
512                         SSVAL(blob->data, head_ofs, n); head_ofs += 2;
513                         SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4;
514                         memcpy(blob->data+data_ofs, b, n);
515                         data_ofs += n;
516                         break;
517                 case 'd':
518                         n = va_arg(ap, int);
519                         SIVAL(blob->data, head_ofs, n); head_ofs += 4;
520                         break;
521                 case 'b':
522                         b = va_arg(ap, uint8 *);
523                         n = va_arg(ap, int);
524                         memcpy(blob->data + head_ofs, b, n);
525                         head_ofs += n;
526                         break;
527                 case 'C':
528                         s = va_arg(ap, char *);
529                         head_ofs += push_string(NULL, blob->data+head_ofs, s, -1, 
530                                                 STR_ASCII|STR_TERMINATE);
531                         break;
532                 }
533         }
534         va_end(ap);
535
536         return True;
537 }
538
539
540 /*
541   this is a tiny msrpc packet parser. This the the partner of msrpc_gen
542
543   format specifiers are:
544
545   U = unicode string (input is unix string)
546   B = data blob
547   b = data blob in header
548   d = word (4 bytes)
549   C = constant ascii string
550  */
551 BOOL msrpc_parse(DATA_BLOB *blob,
552                  const char *format, ...)
553 {
554         int i;
555         va_list ap;
556         char **ps, *s;
557         DATA_BLOB *b;
558         int head_ofs = 0;
559         uint16 len1, len2;
560         uint32 ptr;
561         uint32 *v;
562         pstring p;
563
564         va_start(ap, format);
565         for (i=0; format[i]; i++) {
566                 switch (format[i]) {
567                 case 'U':
568                         len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
569                         len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
570                         ptr =  IVAL(blob->data, head_ofs); head_ofs += 4;
571                         /* make sure its in the right format - be strict */
572                         if (len1 != len2 || (len1&1) || ptr + len1 > blob->length) {
573                                 return False;
574                         }
575                         ps = va_arg(ap, char **);
576                         pull_string(NULL, p, blob->data + ptr, -1, len1, 
577                                     STR_UNICODE|STR_NOALIGN);
578                         (*ps) = strdup(p);
579                         break;
580                 case 'B':
581                         len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
582                         len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
583                         ptr =  IVAL(blob->data, head_ofs); head_ofs += 4;
584                         /* make sure its in the right format - be strict */
585                         if (len1 != len2 || ptr + len1 > blob->length) {
586                                 return False;
587                         }
588                         b = (DATA_BLOB *)va_arg(ap, void *);
589                         *b = data_blob(blob->data + ptr, len1);
590                         break;
591                 case 'b':
592                         b = (DATA_BLOB *)va_arg(ap, void *);
593                         len1 = va_arg(ap, unsigned);
594                         *b = data_blob(blob->data + head_ofs, len1);
595                         head_ofs += len1;
596                         break;
597                 case 'd':
598                         v = va_arg(ap, uint32 *);
599                         *v = IVAL(blob->data, head_ofs); head_ofs += 4;
600                         break;
601                 case 'C':
602                         s = va_arg(ap, char *);
603                         head_ofs += pull_string(NULL, p, blob->data+head_ofs, -1, 
604                                                 blob->length - head_ofs, 
605                                                 STR_ASCII|STR_TERMINATE);
606                         if (strcmp(s, p) != 0) {
607                                 return False;
608                         }
609                         break;
610                 }
611         }
612         va_end(ap);
613
614         return True;
615 }