removed two unneeded files after Richard backed out these changes.
[Samba.git] / source / libsmb / clispnego.c
blob512a2f6033281f3a4cdbcaaa202aefff2de04c05
1 /*
2 Unix SMB/Netbios implementation.
3 Version 3.0
4 simple kerberos5/SPNEGO routines
5 Copyright (C) Andrew Tridgell 2001
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.
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.
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.
22 #include "includes.h"
25 generate a negTokenInit packet given a GUID, a list of supported
26 OIDs (the mechanisms) and a principal name string
28 DATA_BLOB spnego_gen_negTokenInit(uint8 guid[16],
29 const char *OIDs[],
30 const char *principal)
32 int i;
33 ASN1_DATA data;
34 DATA_BLOB ret;
36 memset(&data, 0, sizeof(data));
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));
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]);
49 asn1_pop_tag(&data);
50 asn1_pop_tag(&data);
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);
60 asn1_pop_tag(&data);
61 asn1_pop_tag(&data);
63 asn1_pop_tag(&data);
65 if (data.has_error) {
66 DEBUG(1,("Failed to build negTokenInit at offset %d\n", (int)data.ofs));
67 asn1_free(&data);
70 ret = data_blob(data.data, data.length);
71 asn1_free(&data);
73 return ret;
78 parse a negTokenInit packet giving a GUID, a list of supported
79 OIDs (the mechanisms) and a principal name string
81 BOOL spnego_parse_negTokenInit(DATA_BLOB blob,
82 uint8 guid[16],
83 char *OIDs[ASN1_MAX_OIDS],
84 char **principal)
86 int i;
87 BOOL ret;
88 ASN1_DATA data;
90 asn1_load(&data, blob);
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));
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;
105 OIDs[i] = NULL;
106 asn1_end_tag(&data);
107 asn1_end_tag(&data);
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);
117 asn1_end_tag(&data);
118 asn1_end_tag(&data);
120 asn1_end_tag(&data);
122 ret = !data.has_error;
123 asn1_free(&data);
124 return ret;
129 generate a negTokenTarg packet given a list of OIDs and a security blob
131 DATA_BLOB gen_negTokenTarg(const char *OIDs[], DATA_BLOB blob)
133 int i;
134 ASN1_DATA data;
135 DATA_BLOB ret;
137 memset(&data, 0, sizeof(data));
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));
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]);
149 asn1_pop_tag(&data);
150 asn1_pop_tag(&data);
152 asn1_push_tag(&data, ASN1_CONTEXT(2));
153 asn1_write_OctetString(&data,blob.data,blob.length);
154 asn1_pop_tag(&data);
156 asn1_pop_tag(&data);
157 asn1_pop_tag(&data);
159 asn1_pop_tag(&data);
161 if (data.has_error) {
162 DEBUG(1,("Failed to build negTokenTarg at offset %d\n", (int)data.ofs));
163 asn1_free(&data);
166 ret = data_blob(data.data, data.length);
167 asn1_free(&data);
169 return ret;
174 parse a negTokenTarg packet giving a list of OIDs and a security blob
176 BOOL parse_negTokenTarg(DATA_BLOB blob, char *OIDs[ASN1_MAX_OIDS], DATA_BLOB *secblob)
178 int i;
179 ASN1_DATA data;
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));
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;
194 OIDs[i] = NULL;
195 asn1_end_tag(&data);
196 asn1_end_tag(&data);
198 asn1_start_tag(&data, ASN1_CONTEXT(2));
199 asn1_read_OctetString(&data,secblob);
200 asn1_end_tag(&data);
202 asn1_end_tag(&data);
203 asn1_end_tag(&data);
205 asn1_end_tag(&data);
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;
213 asn1_free(&data);
214 return True;
218 generate a krb5 GSS-API wrapper packet given a ticket
220 static DATA_BLOB spnego_gen_krb5_wrap(DATA_BLOB ticket)
222 ASN1_DATA data;
223 DATA_BLOB ret;
225 memset(&data, 0, sizeof(data));
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);
233 if (data.has_error) {
234 DEBUG(1,("Failed to build krb5 wrapper at offset %d\n", (int)data.ofs));
235 asn1_free(&data);
238 ret = data_blob(data.data, data.length);
239 asn1_free(&data);
241 return ret;
245 parse a krb5 GSS-API wrapper packet giving a ticket
247 BOOL spnego_parse_krb5_wrap(DATA_BLOB blob, DATA_BLOB *ticket)
249 BOOL ret;
250 ASN1_DATA data;
251 int ata_remaining;
253 asn1_load(&data, blob);
254 asn1_start_tag(&data, ASN1_APPLICATION(0));
255 asn1_check_OID(&data, OID_KERBEROS5);
256 asn1_check_BOOLEAN(&data, 0);
258 data_remaining = asn1_tag_remaining(&data);
260 if (data_remaining < 1) {
261 data.has_error = True;
262 } else {
263 *ticket = data_blob(data.data, data_remaining);
264 asn1_read(&data, ticket->data, ticket->length);
267 asn1_end_tag(&data);
269 ret = !data.has_error;
271 asn1_free(&data);
273 return ret;
278 generate a SPNEGO negTokenTarg packet, ready for a EXTENDED_SECURITY
279 kerberos session setup
281 DATA_BLOB spnego_gen_negTokenTarg(struct cli_state *cli, char *principal)
283 DATA_BLOB tkt, tkt_wrapped, targ;
284 const char *krb_mechs[] = {OID_KERBEROS5_OLD, OID_NTLMSSP, NULL};
286 /* get a kerberos ticket for the service */
287 tkt = krb5_get_ticket(principal);
289 /* wrap that up in a nice GSS-API wrapping */
290 tkt_wrapped = spnego_gen_krb5_wrap(tkt);
292 /* and wrap that in a shiny SPNEGO wrapper */
293 targ = gen_negTokenTarg(krb_mechs, tkt_wrapped);
295 data_blob_free(&tkt_wrapped);
296 data_blob_free(&tkt);
298 return targ;
303 parse a spnego NTLMSSP challenge packet giving two security blobs
305 BOOL spnego_parse_challenge(DATA_BLOB blob,
306 DATA_BLOB *chal1, DATA_BLOB *chal2)
308 BOOL ret;
309 ASN1_DATA data;
311 ZERO_STRUCTP(chal1);
312 ZERO_STRUCTP(chal2);
314 asn1_load(&data, blob);
315 asn1_start_tag(&data,ASN1_CONTEXT(1));
316 asn1_start_tag(&data,ASN1_SEQUENCE(0));
318 asn1_start_tag(&data,ASN1_CONTEXT(0));
319 asn1_check_enumerated(&data,1);
320 asn1_end_tag(&data);
322 asn1_start_tag(&data,ASN1_CONTEXT(1));
323 asn1_check_OID(&data, OID_NTLMSSP);
324 asn1_end_tag(&data);
326 asn1_start_tag(&data,ASN1_CONTEXT(2));
327 asn1_read_OctetString(&data, chal1);
328 asn1_end_tag(&data);
330 /* the second challenge is optional (XP doesn't send it) */
331 if (asn1_tag_remaining(&data)) {
332 asn1_start_tag(&data,ASN1_CONTEXT(3));
333 asn1_read_OctetString(&data, chal2);
334 asn1_end_tag(&data);
337 asn1_end_tag(&data);
338 asn1_end_tag(&data);
340 ret = !data.has_error;
341 asn1_free(&data);
342 return ret;
347 generate a spnego NTLMSSP challenge packet given two security blobs
348 The second challenge is optional
350 BOOL spnego_gen_challenge(DATA_BLOB *blob,
351 DATA_BLOB *chal1, DATA_BLOB *chal2)
353 ASN1_DATA data;
355 ZERO_STRUCT(data);
357 asn1_push_tag(&data,ASN1_CONTEXT(1));
358 asn1_push_tag(&data,ASN1_SEQUENCE(0));
360 asn1_push_tag(&data,ASN1_CONTEXT(0));
361 asn1_write_enumerated(&data,1);
362 asn1_pop_tag(&data);
364 asn1_push_tag(&data,ASN1_CONTEXT(1));
365 asn1_write_OID(&data, OID_NTLMSSP);
366 asn1_pop_tag(&data);
368 asn1_push_tag(&data,ASN1_CONTEXT(2));
369 asn1_write_OctetString(&data, chal1->data, chal1->length);
370 asn1_pop_tag(&data);
372 /* the second challenge is optional (XP doesn't send it) */
373 if (chal2) {
374 asn1_push_tag(&data,ASN1_CONTEXT(3));
375 asn1_write_OctetString(&data, chal2->data, chal2->length);
376 asn1_pop_tag(&data);
379 asn1_pop_tag(&data);
380 asn1_pop_tag(&data);
382 if (data.has_error) {
383 return False;
386 *blob = data_blob(data.data, data.length);
387 asn1_free(&data);
388 return True;
392 generate a SPNEGO NTLMSSP auth packet. This will contain the encrypted passwords
394 DATA_BLOB spnego_gen_auth(DATA_BLOB blob)
396 ASN1_DATA data;
397 DATA_BLOB ret;
399 memset(&data, 0, sizeof(data));
401 asn1_push_tag(&data, ASN1_CONTEXT(1));
402 asn1_push_tag(&data, ASN1_SEQUENCE(0));
403 asn1_push_tag(&data, ASN1_CONTEXT(2));
404 asn1_write_OctetString(&data,blob.data,blob.length);
405 asn1_pop_tag(&data);
406 asn1_pop_tag(&data);
407 asn1_pop_tag(&data);
409 ret = data_blob(data.data, data.length);
411 asn1_free(&data);
413 return ret;
417 parse a SPNEGO NTLMSSP auth packet. This contains the encrypted passwords
419 BOOL spnego_parse_auth(DATA_BLOB blob, DATA_BLOB *auth)
421 ASN1_DATA data;
423 asn1_load(&data, blob);
424 asn1_start_tag(&data, ASN1_CONTEXT(1));
425 asn1_start_tag(&data, ASN1_SEQUENCE(0));
426 asn1_start_tag(&data, ASN1_CONTEXT(2));
427 asn1_read_OctetString(&data,auth);
428 asn1_end_tag(&data);
429 asn1_end_tag(&data);
430 asn1_end_tag(&data);
432 if (data.has_error) {
433 DEBUG(3,("spnego_parse_auth failed at %d\n", (int)data.ofs));
434 asn1_free(&data);
435 return False;
438 asn1_free(&data);
439 return True;
444 this is a tiny msrpc packet generator. I am only using this to
445 avoid tying this code to a particular varient of our rpc code. This
446 generator is not general enough for all our rpc needs, its just
447 enough for the spnego/ntlmssp code
449 format specifiers are:
451 U = unicode string (input is unix string)
452 B = data blob (pointer + length)
453 b = data blob in header (pointer + length)
454 d = word (4 bytes)
455 C = constant ascii string
457 BOOL msrpc_gen(DATA_BLOB *blob,
458 const char *format, ...)
460 int i, n;
461 va_list ap;
462 char *s;
463 uint8 *b;
464 int head_size=0, data_size=0;
465 int head_ofs, data_ofs;
467 /* first scan the format to work out the header and body size */
468 va_start(ap, format);
469 for (i=0; format[i]; i++) {
470 switch (format[i]) {
471 case 'U':
472 s = va_arg(ap, char *);
473 head_size += 8;
474 data_size += str_charnum(s) * 2;
475 break;
476 case 'B':
477 b = va_arg(ap, uint8 *);
478 head_size += 8;
479 data_size += va_arg(ap, int);
480 break;
481 case 'b':
482 b = va_arg(ap, uint8 *);
483 head_size += va_arg(ap, int);
484 break;
485 case 'd':
486 n = va_arg(ap, int);
487 head_size += 4;
488 break;
489 case 'C':
490 s = va_arg(ap, char *);
491 head_size += str_charnum(s) + 1;
492 break;
495 va_end(ap);
497 /* allocate the space, then scan the format again to fill in the values */
498 blob->data = malloc(head_size + data_size);
499 blob->length = head_size + data_size;
500 if (!blob->data) return False;
502 head_ofs = 0;
503 data_ofs = head_size;
505 va_start(ap, format);
506 for (i=0; format[i]; i++) {
507 switch (format[i]) {
508 case 'U':
509 s = va_arg(ap, char *);
510 n = str_charnum(s);
511 SSVAL(blob->data, head_ofs, n*2); head_ofs += 2;
512 SSVAL(blob->data, head_ofs, n*2); head_ofs += 2;
513 SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4;
514 push_string(NULL, blob->data+data_ofs, s, n*2, STR_UNICODE|STR_NOALIGN);
515 data_ofs += n*2;
516 break;
517 case 'B':
518 b = va_arg(ap, uint8 *);
519 n = va_arg(ap, int);
520 SSVAL(blob->data, head_ofs, n); head_ofs += 2;
521 SSVAL(blob->data, head_ofs, n); head_ofs += 2;
522 SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4;
523 memcpy(blob->data+data_ofs, b, n);
524 data_ofs += n;
525 break;
526 case 'd':
527 n = va_arg(ap, int);
528 SIVAL(blob->data, head_ofs, n); head_ofs += 4;
529 break;
530 case 'b':
531 b = va_arg(ap, uint8 *);
532 n = va_arg(ap, int);
533 memcpy(blob->data + head_ofs, b, n);
534 head_ofs += n;
535 break;
536 case 'C':
537 s = va_arg(ap, char *);
538 head_ofs += push_string(NULL, blob->data+head_ofs, s, -1,
539 STR_ASCII|STR_TERMINATE);
540 break;
543 va_end(ap);
545 return True;
550 this is a tiny msrpc packet parser. This the the partner of msrpc_gen
552 format specifiers are:
554 U = unicode string (input is unix string)
555 B = data blob
556 b = data blob in header
557 d = word (4 bytes)
558 C = constant ascii string
560 BOOL msrpc_parse(DATA_BLOB *blob,
561 const char *format, ...)
563 int i;
564 va_list ap;
565 char **ps, *s;
566 DATA_BLOB *b;
567 int head_ofs = 0;
568 uint16 len1, len2;
569 uint32 ptr;
570 uint32 *v;
571 pstring p;
573 va_start(ap, format);
574 for (i=0; format[i]; i++) {
575 switch (format[i]) {
576 case 'U':
577 len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
578 len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
579 ptr = IVAL(blob->data, head_ofs); head_ofs += 4;
580 /* make sure its in the right format - be strict */
581 if (len1 != len2 || (len1&1) || ptr + len1 > blob->length) {
582 return False;
584 ps = va_arg(ap, char **);
585 pull_string(NULL, p, blob->data + ptr, -1, len1,
586 STR_UNICODE|STR_NOALIGN);
587 (*ps) = strdup(p);
588 break;
589 case 'B':
590 len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
591 len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
592 ptr = IVAL(blob->data, head_ofs); head_ofs += 4;
593 /* make sure its in the right format - be strict */
594 if (len1 != len2 || ptr + len1 > blob->length) {
595 return False;
597 b = (DATA_BLOB *)va_arg(ap, void *);
598 *b = data_blob(blob->data + ptr, len1);
599 break;
600 case 'b':
601 b = (DATA_BLOB *)va_arg(ap, void *);
602 len1 = va_arg(ap, unsigned);
603 *b = data_blob(blob->data + head_ofs, len1);
604 head_ofs += len1;
605 break;
606 case 'd':
607 v = va_arg(ap, uint32 *);
608 *v = IVAL(blob->data, head_ofs); head_ofs += 4;
609 break;
610 case 'C':
611 s = va_arg(ap, char *);
612 head_ofs += pull_string(NULL, p, blob->data+head_ofs, -1,
613 blob->length - head_ofs,
614 STR_ASCII|STR_TERMINATE);
615 if (strcmp(s, p) != 0) {
616 return False;
618 break;
621 va_end(ap);
623 return True;