2 Unix SMB/CIFS implementation.
3 simple kerberos5/SPNEGO routines
4 Copyright (C) Andrew Tridgell 2001
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 generate a negTokenInit packet given a GUID, a list of supported
25 OIDs (the mechanisms) and a principal name string
27 DATA_BLOB spnego_gen_negTokenInit(uint8 guid[16],
29 const char *principal)
35 memset(&data, 0, sizeof(data));
37 asn1_write(&data, guid, 16);
38 asn1_push_tag(&data,ASN1_APPLICATION(0));
39 asn1_write_OID(&data,OID_SPNEGO);
40 asn1_push_tag(&data,ASN1_CONTEXT(0));
41 asn1_push_tag(&data,ASN1_SEQUENCE(0));
43 asn1_push_tag(&data,ASN1_CONTEXT(0));
44 asn1_push_tag(&data,ASN1_SEQUENCE(0));
45 for (i=0; OIDs[i]; i++) {
46 asn1_write_OID(&data,OIDs[i]);
51 asn1_push_tag(&data, ASN1_CONTEXT(3));
52 asn1_push_tag(&data, ASN1_SEQUENCE(0));
53 asn1_push_tag(&data, ASN1_CONTEXT(0));
54 asn1_write_GeneralString(&data,principal);
65 DEBUG(1,("Failed to build negTokenInit at offset %d\n", (int)data.ofs));
69 ret = data_blob(data.data, data.length);
77 parse a negTokenInit packet giving a GUID, a list of supported
78 OIDs (the mechanisms) and a principal name string
80 BOOL spnego_parse_negTokenInit(DATA_BLOB blob,
82 char *OIDs[ASN1_MAX_OIDS],
89 asn1_load(&data, blob);
91 asn1_read(&data, guid, 16);
92 asn1_start_tag(&data,ASN1_APPLICATION(0));
93 asn1_check_OID(&data,OID_SPNEGO);
94 asn1_start_tag(&data,ASN1_CONTEXT(0));
95 asn1_start_tag(&data,ASN1_SEQUENCE(0));
97 asn1_start_tag(&data,ASN1_CONTEXT(0));
98 asn1_start_tag(&data,ASN1_SEQUENCE(0));
99 for (i=0; asn1_tag_remaining(&data) > 0 && i < ASN1_MAX_OIDS; i++) {
101 asn1_read_OID(&data,&oid);
108 asn1_start_tag(&data, ASN1_CONTEXT(3));
109 asn1_start_tag(&data, ASN1_SEQUENCE(0));
110 asn1_start_tag(&data, ASN1_CONTEXT(0));
111 asn1_read_GeneralString(&data,principal);
121 ret = !data.has_error;
128 generate a negTokenTarg packet given a list of OIDs and a security blob
130 DATA_BLOB gen_negTokenTarg(const char *OIDs[], DATA_BLOB blob)
136 memset(&data, 0, sizeof(data));
138 asn1_push_tag(&data, ASN1_APPLICATION(0));
139 asn1_write_OID(&data,OID_SPNEGO);
140 asn1_push_tag(&data, ASN1_CONTEXT(0));
141 asn1_push_tag(&data, ASN1_SEQUENCE(0));
143 asn1_push_tag(&data, ASN1_CONTEXT(0));
144 asn1_push_tag(&data, ASN1_SEQUENCE(0));
145 for (i=0; OIDs[i]; i++) {
146 asn1_write_OID(&data,OIDs[i]);
151 asn1_push_tag(&data, ASN1_CONTEXT(2));
152 asn1_write_OctetString(&data,blob.data,blob.length);
160 if (data.has_error) {
161 DEBUG(1,("Failed to build negTokenTarg at offset %d\n", (int)data.ofs));
165 ret = data_blob(data.data, data.length);
173 parse a negTokenTarg packet giving a list of OIDs and a security blob
175 BOOL parse_negTokenTarg(DATA_BLOB blob, char *OIDs[ASN1_MAX_OIDS], DATA_BLOB *secblob)
180 asn1_load(&data, blob);
181 asn1_start_tag(&data, ASN1_APPLICATION(0));
182 asn1_check_OID(&data,OID_SPNEGO);
183 asn1_start_tag(&data, ASN1_CONTEXT(0));
184 asn1_start_tag(&data, ASN1_SEQUENCE(0));
186 asn1_start_tag(&data, ASN1_CONTEXT(0));
187 asn1_start_tag(&data, ASN1_SEQUENCE(0));
188 for (i=0; asn1_tag_remaining(&data) > 0 && i < ASN1_MAX_OIDS; i++) {
190 asn1_read_OID(&data,&oid);
197 asn1_start_tag(&data, ASN1_CONTEXT(2));
198 asn1_read_OctetString(&data,secblob);
206 if (data.has_error) {
207 DEBUG(1,("Failed to parse negTokenTarg at offset %d\n", (int)data.ofs));
217 generate a krb5 GSS-API wrapper packet given a ticket
219 DATA_BLOB spnego_gen_krb5_wrap(DATA_BLOB ticket)
224 memset(&data, 0, sizeof(data));
226 asn1_push_tag(&data, ASN1_APPLICATION(0));
227 asn1_write_OID(&data, OID_KERBEROS5);
228 asn1_write_BOOLEAN(&data, 0);
229 asn1_write(&data, ticket.data, ticket.length);
232 if (data.has_error) {
233 DEBUG(1,("Failed to build krb5 wrapper at offset %d\n", (int)data.ofs));
237 ret = data_blob(data.data, data.length);
244 parse a krb5 GSS-API wrapper packet giving a ticket
246 BOOL spnego_parse_krb5_wrap(DATA_BLOB blob, DATA_BLOB *ticket)
251 asn1_load(&data, blob);
252 asn1_start_tag(&data, ASN1_APPLICATION(0));
253 asn1_check_OID(&data, OID_KERBEROS5);
254 asn1_check_BOOLEAN(&data, 0);
255 *ticket = data_blob(data.data, asn1_tag_remaining(&data));
256 asn1_read(&data, ticket->data, ticket->length);
259 ret = !data.has_error;
268 generate a SPNEGO negTokenTarg packet, ready for a EXTENDED_SECURITY
269 kerberos session setup
271 DATA_BLOB spnego_gen_negTokenTarg(struct cli_state *cli, char *principal)
273 DATA_BLOB tkt, tkt_wrapped, targ;
274 const char *krb_mechs[] = {OID_KERBEROS5_OLD, OID_NTLMSSP, NULL};
276 /* get a kerberos ticket for the service */
277 tkt = krb5_get_ticket(principal);
279 /* wrap that up in a nice GSS-API wrapping */
280 tkt_wrapped = spnego_gen_krb5_wrap(tkt);
282 /* and wrap that in a shiny SPNEGO wrapper */
283 targ = gen_negTokenTarg(krb_mechs, tkt_wrapped);
285 data_blob_free(&tkt_wrapped);
286 data_blob_free(&tkt);
293 parse a spnego NTLMSSP challenge packet giving two security blobs
295 BOOL spnego_parse_challenge(DATA_BLOB blob,
296 DATA_BLOB *chal1, DATA_BLOB *chal2)
304 asn1_load(&data, blob);
305 asn1_start_tag(&data,ASN1_CONTEXT(1));
306 asn1_start_tag(&data,ASN1_SEQUENCE(0));
308 asn1_start_tag(&data,ASN1_CONTEXT(0));
309 asn1_check_enumerated(&data,1);
312 asn1_start_tag(&data,ASN1_CONTEXT(1));
313 asn1_check_OID(&data, OID_NTLMSSP);
316 asn1_start_tag(&data,ASN1_CONTEXT(2));
317 asn1_read_OctetString(&data, chal1);
320 /* the second challenge is optional (XP doesn't send it) */
321 if (asn1_tag_remaining(&data)) {
322 asn1_start_tag(&data,ASN1_CONTEXT(3));
323 asn1_read_OctetString(&data, chal2);
330 ret = !data.has_error;
337 generate a spnego NTLMSSP challenge packet given two security blobs
338 The second challenge is optional
340 BOOL spnego_gen_challenge(DATA_BLOB *blob,
341 DATA_BLOB *chal1, DATA_BLOB *chal2)
347 asn1_push_tag(&data,ASN1_CONTEXT(1));
348 asn1_push_tag(&data,ASN1_SEQUENCE(0));
350 asn1_push_tag(&data,ASN1_CONTEXT(0));
351 asn1_write_enumerated(&data,1);
354 asn1_push_tag(&data,ASN1_CONTEXT(1));
355 asn1_write_OID(&data, OID_NTLMSSP);
358 asn1_push_tag(&data,ASN1_CONTEXT(2));
359 asn1_write_OctetString(&data, chal1->data, chal1->length);
362 /* the second challenge is optional (XP doesn't send it) */
364 asn1_push_tag(&data,ASN1_CONTEXT(3));
365 asn1_write_OctetString(&data, chal2->data, chal2->length);
372 if (data.has_error) {
376 *blob = data_blob(data.data, data.length);
382 generate a SPNEGO NTLMSSP auth packet. This will contain the encrypted passwords
384 DATA_BLOB spnego_gen_auth(DATA_BLOB blob)
389 memset(&data, 0, sizeof(data));
391 asn1_push_tag(&data, ASN1_CONTEXT(1));
392 asn1_push_tag(&data, ASN1_SEQUENCE(0));
393 asn1_push_tag(&data, ASN1_CONTEXT(2));
394 asn1_write_OctetString(&data,blob.data,blob.length);
399 ret = data_blob(data.data, data.length);
407 parse a SPNEGO NTLMSSP auth packet. This contains the encrypted passwords
409 BOOL spnego_parse_auth(DATA_BLOB blob, DATA_BLOB *auth)
413 asn1_load(&data, blob);
414 asn1_start_tag(&data, ASN1_CONTEXT(1));
415 asn1_start_tag(&data, ASN1_SEQUENCE(0));
416 asn1_start_tag(&data, ASN1_CONTEXT(2));
417 asn1_read_OctetString(&data,auth);
422 if (data.has_error) {
423 DEBUG(3,("spnego_parse_auth failed at %d\n", (int)data.ofs));
434 this is a tiny msrpc packet generator. I am only using this to
435 avoid tying this code to a particular varient of our rpc code. This
436 generator is not general enough for all our rpc needs, its just
437 enough for the spnego/ntlmssp code
439 format specifiers are:
441 U = unicode string (input is unix string)
442 B = data blob (pointer + length)
443 b = data blob in header (pointer + length)
445 C = constant ascii string
447 BOOL msrpc_gen(DATA_BLOB *blob,
448 const char *format, ...)
454 int head_size=0, data_size=0;
455 int head_ofs, data_ofs;
457 /* first scan the format to work out the header and body size */
458 va_start(ap, format);
459 for (i=0; format[i]; i++) {
462 s = va_arg(ap, char *);
464 data_size += str_charnum(s) * 2;
467 b = va_arg(ap, uint8 *);
469 data_size += va_arg(ap, int);
472 b = va_arg(ap, uint8 *);
473 head_size += va_arg(ap, int);
480 s = va_arg(ap, char *);
481 head_size += str_charnum(s) + 1;
487 /* allocate the space, then scan the format again to fill in the values */
488 *blob = data_blob(NULL, head_size + data_size);
491 data_ofs = head_size;
493 va_start(ap, format);
494 for (i=0; format[i]; i++) {
497 s = va_arg(ap, char *);
499 SSVAL(blob->data, head_ofs, n*2); head_ofs += 2;
500 SSVAL(blob->data, head_ofs, n*2); head_ofs += 2;
501 SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4;
502 push_string(NULL, blob->data+data_ofs, s, n*2, STR_UNICODE|STR_NOALIGN);
506 b = va_arg(ap, uint8 *);
508 SSVAL(blob->data, head_ofs, n); head_ofs += 2;
509 SSVAL(blob->data, head_ofs, n); head_ofs += 2;
510 SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4;
511 memcpy(blob->data+data_ofs, b, n);
516 SIVAL(blob->data, head_ofs, n); head_ofs += 4;
519 b = va_arg(ap, uint8 *);
521 memcpy(blob->data + head_ofs, b, n);
525 s = va_arg(ap, char *);
526 head_ofs += push_string(NULL, blob->data+head_ofs, s, -1,
527 STR_ASCII|STR_TERMINATE);
538 this is a tiny msrpc packet parser. This the the partner of msrpc_gen
540 format specifiers are:
542 U = unicode string (input is unix string)
544 b = data blob in header
546 C = constant ascii string
548 BOOL msrpc_parse(DATA_BLOB *blob,
549 const char *format, ...)
561 va_start(ap, format);
562 for (i=0; format[i]; i++) {
565 len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
566 len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
567 ptr = IVAL(blob->data, head_ofs); head_ofs += 4;
568 /* make sure its in the right format - be strict */
569 if (len1 != len2 || (len1&1) || ptr + len1 > blob->length) {
572 ps = va_arg(ap, char **);
573 pull_string(NULL, p, blob->data + ptr, -1, len1,
574 STR_UNICODE|STR_NOALIGN);
578 len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
579 len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
580 ptr = IVAL(blob->data, head_ofs); head_ofs += 4;
581 /* make sure its in the right format - be strict */
582 if (len1 != len2 || ptr + len1 > blob->length) {
585 b = (DATA_BLOB *)va_arg(ap, void *);
586 *b = data_blob(blob->data + ptr, len1);
589 b = (DATA_BLOB *)va_arg(ap, void *);
590 len1 = va_arg(ap, unsigned);
591 *b = data_blob(blob->data + head_ofs, len1);
595 v = va_arg(ap, uint32 *);
596 *v = IVAL(blob->data, head_ofs); head_ofs += 4;
599 s = va_arg(ap, char *);
600 head_ofs += pull_string(NULL, p, blob->data+head_ofs, -1,
601 blob->length - head_ofs,
602 STR_ASCII|STR_TERMINATE);
603 if (strcmp(s, p) != 0) {