merge from 2.2 and regenerate
[Samba.git] / source / libsmb / clispnego.c
blob035b47b4171c9a784a95b9e451ba3a1bfd942ccf
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 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;
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);
260 ret = !data.has_error;
262 asn1_free(&data);
264 return ret;
269 generate a SPNEGO negTokenTarg packet, ready for a EXTENDED_SECURITY
270 kerberos session setup
272 DATA_BLOB spnego_gen_negTokenTarg(struct cli_state *cli, char *principal)
274 DATA_BLOB tkt, tkt_wrapped, targ;
275 const char *krb_mechs[] = {OID_KERBEROS5_OLD, OID_NTLMSSP, NULL};
277 /* get a kerberos ticket for the service */
278 tkt = krb5_get_ticket(principal);
280 /* wrap that up in a nice GSS-API wrapping */
281 tkt_wrapped = spnego_gen_krb5_wrap(tkt);
283 /* and wrap that in a shiny SPNEGO wrapper */
284 targ = gen_negTokenTarg(krb_mechs, tkt_wrapped);
286 data_blob_free(&tkt_wrapped);
287 data_blob_free(&tkt);
289 return targ;
294 parse a spnego NTLMSSP challenge packet giving two security blobs
296 BOOL spnego_parse_challenge(DATA_BLOB blob,
297 DATA_BLOB *chal1, DATA_BLOB *chal2)
299 BOOL ret;
300 ASN1_DATA data;
302 ZERO_STRUCTP(chal1);
303 ZERO_STRUCTP(chal2);
305 asn1_load(&data, blob);
306 asn1_start_tag(&data,ASN1_CONTEXT(1));
307 asn1_start_tag(&data,ASN1_SEQUENCE(0));
309 asn1_start_tag(&data,ASN1_CONTEXT(0));
310 asn1_check_enumerated(&data,1);
311 asn1_end_tag(&data);
313 asn1_start_tag(&data,ASN1_CONTEXT(1));
314 asn1_check_OID(&data, OID_NTLMSSP);
315 asn1_end_tag(&data);
317 asn1_start_tag(&data,ASN1_CONTEXT(2));
318 asn1_read_OctetString(&data, chal1);
319 asn1_end_tag(&data);
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);
328 asn1_end_tag(&data);
329 asn1_end_tag(&data);
331 ret = !data.has_error;
332 asn1_free(&data);
333 return ret;
338 generate a spnego NTLMSSP challenge packet given two security blobs
339 The second challenge is optional
341 BOOL spnego_gen_challenge(DATA_BLOB *blob,
342 DATA_BLOB *chal1, DATA_BLOB *chal2)
344 ASN1_DATA data;
346 ZERO_STRUCT(data);
348 asn1_push_tag(&data,ASN1_CONTEXT(1));
349 asn1_push_tag(&data,ASN1_SEQUENCE(0));
351 asn1_push_tag(&data,ASN1_CONTEXT(0));
352 asn1_write_enumerated(&data,1);
353 asn1_pop_tag(&data);
355 asn1_push_tag(&data,ASN1_CONTEXT(1));
356 asn1_write_OID(&data, OID_NTLMSSP);
357 asn1_pop_tag(&data);
359 asn1_push_tag(&data,ASN1_CONTEXT(2));
360 asn1_write_OctetString(&data, chal1->data, chal1->length);
361 asn1_pop_tag(&data);
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);
370 asn1_pop_tag(&data);
371 asn1_pop_tag(&data);
373 if (data.has_error) {
374 return False;
377 *blob = data_blob(data.data, data.length);
378 asn1_free(&data);
379 return True;
383 generate a SPNEGO NTLMSSP auth packet. This will contain the encrypted passwords
385 DATA_BLOB spnego_gen_auth(DATA_BLOB blob)
387 ASN1_DATA data;
388 DATA_BLOB ret;
390 memset(&data, 0, sizeof(data));
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);
400 ret = data_blob(data.data, data.length);
402 asn1_free(&data);
404 return ret;
408 parse a SPNEGO NTLMSSP auth packet. This contains the encrypted passwords
410 BOOL spnego_parse_auth(DATA_BLOB blob, DATA_BLOB *auth)
412 ASN1_DATA data;
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);
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;
429 asn1_free(&data);
430 return True;
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
440 format specifiers are:
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
448 BOOL msrpc_gen(DATA_BLOB *blob,
449 const char *format, ...)
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;
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;
486 va_end(ap);
488 /* allocate the space, then scan the format again to fill in the values */
489 *blob = data_blob(NULL, head_size + data_size);
491 head_ofs = 0;
492 data_ofs = head_size;
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;
532 va_end(ap);
534 return True;
539 this is a tiny msrpc packet parser. This the the partner of msrpc_gen
541 format specifiers are:
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
549 BOOL msrpc_parse(DATA_BLOB *blob,
550 const char *format, ...)
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;
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;
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;
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;
607 break;
610 va_end(ap);
612 return True;