added basic NTLMSSP support in smbd. This is still quite rough, and
[samba.git] / source / 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 principle name string 
27 */
28 DATA_BLOB spnego_gen_negTokenInit(uint8 guid[16], 
29                                   const char *OIDs[], 
30                                   const char *principle)
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,principle);
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 principle name string 
80 */
81 BOOL spnego_parse_negTokenInit(DATA_BLOB blob,
82                                uint8 guid[16], 
83                                char *OIDs[ASN1_MAX_OIDS], 
84                                char **principle)
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,principle);
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 /* 
246    generate a SPNEGO negTokenTarg packet, ready for a EXTENDED_SECURITY
247    kerberos session setup 
248 */
249 DATA_BLOB spnego_gen_negTokenTarg(struct cli_state *cli, char *principle)
250 {
251         char *p;
252         fstring service;
253         char *realm;
254         DATA_BLOB tkt, tkt_wrapped, targ;
255         const char *krb_mechs[] = {OID_KERBEROS5_OLD, OID_NTLMSSP, NULL};
256
257         fstrcpy(service, principle);
258         p = strchr_m(service, '@');
259         if (!p) {
260                 DEBUG(1,("Malformed principle [%s] in spnego_gen_negTokenTarg\n",
261                          principle));
262                 return data_blob(NULL, 0);
263         }
264         *p = 0;
265         realm = p+1;
266
267         /* get a kerberos ticket for the service */
268         tkt = krb5_get_ticket(service, realm);
269
270         /* wrap that up in a nice GSS-API wrapping */
271         tkt_wrapped = spnego_gen_krb5_wrap(tkt);
272
273         /* and wrap that in a shiny SPNEGO wrapper */
274         targ = gen_negTokenTarg(krb_mechs, tkt_wrapped);
275
276         data_blob_free(&tkt_wrapped);
277         data_blob_free(&tkt);
278
279         return targ;
280 }
281
282
283 /*
284   parse a spnego NTLMSSP challenge packet giving two security blobs
285 */
286 BOOL spnego_parse_challenge(DATA_BLOB blob,
287                             DATA_BLOB *chal1, DATA_BLOB *chal2)
288 {
289         BOOL ret;
290         ASN1_DATA data;
291
292         ZERO_STRUCTP(chal1);
293         ZERO_STRUCTP(chal2);
294
295         asn1_load(&data, blob);
296         asn1_start_tag(&data,ASN1_CONTEXT(1));
297         asn1_start_tag(&data,ASN1_SEQUENCE(0));
298
299         asn1_start_tag(&data,ASN1_CONTEXT(0));
300         asn1_check_enumerated(&data,1);
301         asn1_end_tag(&data);
302
303         asn1_start_tag(&data,ASN1_CONTEXT(1));
304         asn1_check_OID(&data, OID_NTLMSSP);
305         asn1_end_tag(&data);
306
307         asn1_start_tag(&data,ASN1_CONTEXT(2));
308         asn1_read_OctetString(&data, chal1);
309         asn1_end_tag(&data);
310
311         /* the second challenge is optional (XP doesn't send it) */
312         if (asn1_tag_remaining(&data)) {
313                 asn1_start_tag(&data,ASN1_CONTEXT(3));
314                 asn1_read_OctetString(&data, chal2);
315                 asn1_end_tag(&data);
316         }
317
318         asn1_end_tag(&data);
319         asn1_end_tag(&data);
320
321         ret = !data.has_error;
322         asn1_free(&data);
323         return ret;
324 }
325
326
327 /*
328   generate a spnego NTLMSSP challenge packet given two security blobs
329   The second challenge is optional
330 */
331 BOOL spnego_gen_challenge(DATA_BLOB *blob,
332                           DATA_BLOB *chal1, DATA_BLOB *chal2)
333 {
334         ASN1_DATA data;
335
336         ZERO_STRUCT(data);
337
338         asn1_push_tag(&data,ASN1_CONTEXT(1));
339         asn1_push_tag(&data,ASN1_SEQUENCE(0));
340
341         asn1_push_tag(&data,ASN1_CONTEXT(0));
342         asn1_write_enumerated(&data,1);
343         asn1_pop_tag(&data);
344
345         asn1_push_tag(&data,ASN1_CONTEXT(1));
346         asn1_write_OID(&data, OID_NTLMSSP);
347         asn1_pop_tag(&data);
348
349         asn1_push_tag(&data,ASN1_CONTEXT(2));
350         asn1_write_OctetString(&data, chal1->data, chal1->length);
351         asn1_pop_tag(&data);
352
353         /* the second challenge is optional (XP doesn't send it) */
354         if (chal2) {
355                 asn1_push_tag(&data,ASN1_CONTEXT(3));
356                 asn1_write_OctetString(&data, chal2->data, chal2->length);
357                 asn1_pop_tag(&data);
358         }
359
360         asn1_pop_tag(&data);
361         asn1_pop_tag(&data);
362
363         if (data.has_error) {
364                 return False;
365         }
366
367         *blob = data_blob(data.data, data.length);
368         asn1_free(&data);
369         return True;
370 }
371
372 /*
373  generate a SPNEGO NTLMSSP auth packet. This will contain the encrypted passwords
374 */
375 DATA_BLOB spnego_gen_auth(DATA_BLOB blob)
376 {
377         ASN1_DATA data;
378         DATA_BLOB ret;
379
380         memset(&data, 0, sizeof(data));
381
382         asn1_push_tag(&data, ASN1_CONTEXT(1));
383         asn1_push_tag(&data, ASN1_SEQUENCE(0));
384         asn1_push_tag(&data, ASN1_CONTEXT(2));
385         asn1_write_OctetString(&data,blob.data,blob.length);    
386         asn1_pop_tag(&data);
387         asn1_pop_tag(&data);
388         asn1_pop_tag(&data);
389
390         ret = data_blob(data.data, data.length);
391
392         asn1_free(&data);
393
394         return ret;
395 }
396
397 /*
398  parse a SPNEGO NTLMSSP auth packet. This contains the encrypted passwords
399 */
400 BOOL spnego_parse_auth(DATA_BLOB blob, DATA_BLOB *auth)
401 {
402         ASN1_DATA data;
403
404         asn1_load(&data, blob);
405         asn1_start_tag(&data, ASN1_CONTEXT(1));
406         asn1_start_tag(&data, ASN1_SEQUENCE(0));
407         asn1_start_tag(&data, ASN1_CONTEXT(2));
408         asn1_read_OctetString(&data,auth);
409         asn1_end_tag(&data);
410         asn1_end_tag(&data);
411         asn1_end_tag(&data);
412
413         if (data.has_error) {
414                 DEBUG(3,("spnego_parse_auth failed at %d\n", (int)data.ofs));
415                 asn1_free(&data);
416                 return False;
417         }
418
419         asn1_free(&data);
420         return True;
421 }
422
423
424 /*
425   this is a tiny msrpc packet generator. I am only using this to
426   avoid tying this code to a particular varient of our rpc code. This
427   generator is not general enough for all our rpc needs, its just
428   enough for the spnego/ntlmssp code
429
430   format specifiers are:
431
432   U = unicode string (input is unix string)
433   B = data blob (pointer + length)
434   b = data blob in header (pointer + length)
435   d = word (4 bytes)
436   C = constant ascii string
437  */
438 BOOL msrpc_gen(DATA_BLOB *blob,
439                const char *format, ...)
440 {
441         int i, n;
442         va_list ap;
443         char *s;
444         uint8 *b;
445         int head_size=0, data_size=0;
446         int head_ofs, data_ofs;
447
448         /* first scan the format to work out the header and body size */
449         va_start(ap, format);
450         for (i=0; format[i]; i++) {
451                 switch (format[i]) {
452                 case 'U':
453                         s = va_arg(ap, char *);
454                         head_size += 8;
455                         data_size += str_charnum(s) * 2;
456                         break;
457                 case 'B':
458                         b = va_arg(ap, uint8 *);
459                         head_size += 8;
460                         data_size += va_arg(ap, int);
461                         break;
462                 case 'b':
463                         b = va_arg(ap, uint8 *);
464                         head_size += va_arg(ap, int);
465                         break;
466                 case 'd':
467                         n = va_arg(ap, int);
468                         head_size += 4;
469                         break;
470                 case 'C':
471                         s = va_arg(ap, char *);
472                         head_size += str_charnum(s) + 1;
473                         break;
474                 }
475         }
476         va_end(ap);
477
478         /* allocate the space, then scan the format again to fill in the values */
479         blob->data = malloc(head_size + data_size);
480         blob->length = head_size + data_size;
481         if (!blob->data) return False;
482
483         head_ofs = 0;
484         data_ofs = head_size;
485
486         va_start(ap, format);
487         for (i=0; format[i]; i++) {
488                 switch (format[i]) {
489                 case 'U':
490                         s = va_arg(ap, char *);
491                         n = str_charnum(s);
492                         SSVAL(blob->data, head_ofs, n*2); head_ofs += 2;
493                         SSVAL(blob->data, head_ofs, n*2); head_ofs += 2;
494                         SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4;
495                         push_string(NULL, blob->data+data_ofs, s, n*2, STR_UNICODE|STR_NOALIGN);
496                         data_ofs += n*2;
497                         break;
498                 case 'B':
499                         b = va_arg(ap, uint8 *);
500                         n = va_arg(ap, int);
501                         SSVAL(blob->data, head_ofs, n); head_ofs += 2;
502                         SSVAL(blob->data, head_ofs, n); head_ofs += 2;
503                         SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4;
504                         memcpy(blob->data+data_ofs, b, n);
505                         data_ofs += n;
506                         break;
507                 case 'd':
508                         n = va_arg(ap, int);
509                         SIVAL(blob->data, head_ofs, n); head_ofs += 4;
510                         break;
511                 case 'b':
512                         b = va_arg(ap, uint8 *);
513                         n = va_arg(ap, int);
514                         memcpy(blob->data + head_ofs, b, n);
515                         head_ofs += n;
516                         break;
517                 case 'C':
518                         s = va_arg(ap, char *);
519                         head_ofs += push_string(NULL, blob->data+head_ofs, s, -1, 
520                                                 STR_ASCII|STR_TERMINATE);
521                         break;
522                 }
523         }
524         va_end(ap);
525
526         return True;
527 }
528
529
530 /*
531   this is a tiny msrpc packet parser. This the the partner of msrpc_gen
532
533   format specifiers are:
534
535   U = unicode string (input is unix string)
536   B = data blob
537   b = data blob in header
538   d = word (4 bytes)
539   C = constant ascii string
540  */
541 BOOL msrpc_parse(DATA_BLOB *blob,
542                  const char *format, ...)
543 {
544         int i;
545         va_list ap;
546         char **ps, *s;
547         DATA_BLOB *b;
548         int head_ofs = 0;
549         uint16 len1, len2;
550         uint32 ptr;
551         uint32 *v;
552         pstring p;
553
554         va_start(ap, format);
555         for (i=0; format[i]; i++) {
556                 switch (format[i]) {
557                 case 'U':
558                         len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
559                         len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
560                         ptr =  IVAL(blob->data, head_ofs); head_ofs += 4;
561                         /* make sure its in the right format - be strict */
562                         if (len1 != len2 || (len1&1) || ptr + len1 > blob->length) {
563                                 return False;
564                         }
565                         ps = va_arg(ap, char **);
566                         pull_string(NULL, p, blob->data + ptr, -1, len1, 
567                                     STR_UNICODE|STR_NOALIGN);
568                         (*ps) = strdup(p);
569                         break;
570                 case 'B':
571                         len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
572                         len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
573                         ptr =  IVAL(blob->data, head_ofs); head_ofs += 4;
574                         /* make sure its in the right format - be strict */
575                         if (len1 != len2 || ptr + len1 > blob->length) {
576                                 return False;
577                         }
578                         b = (DATA_BLOB *)va_arg(ap, void *);
579                         *b = data_blob(blob->data + ptr, len1);
580                         break;
581                 case 'b':
582                         b = (DATA_BLOB *)va_arg(ap, void *);
583                         len1 = va_arg(ap, unsigned);
584                         *b = data_blob(blob->data + head_ofs, len1);
585                         head_ofs += len1;
586                         break;
587                 case 'd':
588                         v = va_arg(ap, uint32 *);
589                         *v = IVAL(blob->data, head_ofs); head_ofs += 4;
590                         break;
591                 case 'C':
592                         s = va_arg(ap, char *);
593                         head_ofs += pull_string(NULL, p, blob->data+head_ofs, -1, 
594                                                 blob->length - head_ofs, 
595                                                 STR_ASCII|STR_TERMINATE);
596                         if (strcmp(s, p) != 0) {
597                                 return False;
598                         }
599                         break;
600                 }
601         }
602         va_end(ap);
603
604         return True;
605 }