Add type A to the small MSRPC generator ...
[samba.git] / source3 / libsmb / clispnego.c
1 /* 
2    Unix SMB/CIFS implementation.
3    simple kerberos5/SPNEGO routines
4    Copyright (C) Andrew Tridgell 2001
5    Copyright (C) Jim McDonough   2002
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                                char *OIDs[ASN1_MAX_OIDS], 
83                                char **principal)
84 {
85         int i;
86         BOOL ret;
87         ASN1_DATA data;
88
89         asn1_load(&data, blob);
90
91         asn1_start_tag(&data,ASN1_APPLICATION(0));
92         asn1_check_OID(&data,OID_SPNEGO);
93         asn1_start_tag(&data,ASN1_CONTEXT(0));
94         asn1_start_tag(&data,ASN1_SEQUENCE(0));
95
96         asn1_start_tag(&data,ASN1_CONTEXT(0));
97         asn1_start_tag(&data,ASN1_SEQUENCE(0));
98         for (i=0; asn1_tag_remaining(&data) > 0 && i < ASN1_MAX_OIDS; i++) {
99                 char *oid = NULL;
100                 asn1_read_OID(&data,&oid);
101                 OIDs[i] = oid;
102         }
103         OIDs[i] = NULL;
104         asn1_end_tag(&data);
105         asn1_end_tag(&data);
106
107         asn1_start_tag(&data, ASN1_CONTEXT(3));
108         asn1_start_tag(&data, ASN1_SEQUENCE(0));
109         asn1_start_tag(&data, ASN1_CONTEXT(0));
110         asn1_read_GeneralString(&data,principal);
111         asn1_end_tag(&data);
112         asn1_end_tag(&data);
113         asn1_end_tag(&data);
114
115         asn1_end_tag(&data);
116         asn1_end_tag(&data);
117
118         asn1_end_tag(&data);
119
120         ret = !data.has_error;
121         asn1_free(&data);
122         return ret;
123 }
124
125
126 /*
127   generate a negTokenTarg packet given a list of OIDs and a security blob
128 */
129 DATA_BLOB gen_negTokenTarg(const char *OIDs[], DATA_BLOB blob)
130 {
131         int i;
132         ASN1_DATA data;
133         DATA_BLOB ret;
134
135         memset(&data, 0, sizeof(data));
136
137         asn1_push_tag(&data, ASN1_APPLICATION(0));
138         asn1_write_OID(&data,OID_SPNEGO);
139         asn1_push_tag(&data, ASN1_CONTEXT(0));
140         asn1_push_tag(&data, ASN1_SEQUENCE(0));
141
142         asn1_push_tag(&data, ASN1_CONTEXT(0));
143         asn1_push_tag(&data, ASN1_SEQUENCE(0));
144         for (i=0; OIDs[i]; i++) {
145                 asn1_write_OID(&data,OIDs[i]);
146         }
147         asn1_pop_tag(&data);
148         asn1_pop_tag(&data);
149
150         asn1_push_tag(&data, ASN1_CONTEXT(2));
151         asn1_write_OctetString(&data,blob.data,blob.length);
152         asn1_pop_tag(&data);
153
154         asn1_pop_tag(&data);
155         asn1_pop_tag(&data);
156
157         asn1_pop_tag(&data);
158
159         if (data.has_error) {
160                 DEBUG(1,("Failed to build negTokenTarg at offset %d\n", (int)data.ofs));
161                 asn1_free(&data);
162         }
163
164         ret = data_blob(data.data, data.length);
165         asn1_free(&data);
166
167         return ret;
168 }
169
170
171 /*
172   parse a negTokenTarg packet giving a list of OIDs and a security blob
173 */
174 BOOL parse_negTokenTarg(DATA_BLOB blob, char *OIDs[ASN1_MAX_OIDS], DATA_BLOB *secblob)
175 {
176         int i;
177         ASN1_DATA data;
178
179         asn1_load(&data, blob);
180         asn1_start_tag(&data, ASN1_APPLICATION(0));
181         asn1_check_OID(&data,OID_SPNEGO);
182         asn1_start_tag(&data, ASN1_CONTEXT(0));
183         asn1_start_tag(&data, ASN1_SEQUENCE(0));
184
185         asn1_start_tag(&data, ASN1_CONTEXT(0));
186         asn1_start_tag(&data, ASN1_SEQUENCE(0));
187         for (i=0; asn1_tag_remaining(&data) > 0 && i < ASN1_MAX_OIDS; i++) {
188                 char *oid = NULL;
189                 asn1_read_OID(&data,&oid);
190                 OIDs[i] = oid;
191         }
192         OIDs[i] = NULL;
193         asn1_end_tag(&data);
194         asn1_end_tag(&data);
195
196         asn1_start_tag(&data, ASN1_CONTEXT(2));
197         asn1_read_OctetString(&data,secblob);
198         asn1_end_tag(&data);
199
200         asn1_end_tag(&data);
201         asn1_end_tag(&data);
202
203         asn1_end_tag(&data);
204
205         if (data.has_error) {
206                 DEBUG(1,("Failed to parse negTokenTarg at offset %d\n", (int)data.ofs));
207                 asn1_free(&data);
208                 return False;
209         }
210
211         asn1_free(&data);
212         return True;
213 }
214
215 /*
216   generate a krb5 GSS-API wrapper packet given a ticket
217 */
218 DATA_BLOB spnego_gen_krb5_wrap(DATA_BLOB ticket)
219 {
220         ASN1_DATA data;
221         DATA_BLOB ret;
222
223         memset(&data, 0, sizeof(data));
224
225         asn1_push_tag(&data, ASN1_APPLICATION(0));
226         asn1_write_OID(&data, OID_KERBEROS5);
227         asn1_write_BOOLEAN(&data, 0);
228         asn1_write(&data, ticket.data, ticket.length);
229         asn1_pop_tag(&data);
230
231         if (data.has_error) {
232                 DEBUG(1,("Failed to build krb5 wrapper at offset %d\n", (int)data.ofs));
233                 asn1_free(&data);
234         }
235
236         ret = data_blob(data.data, data.length);
237         asn1_free(&data);
238
239         return ret;
240 }
241
242 /*
243   parse a krb5 GSS-API wrapper packet giving a ticket
244 */
245 BOOL spnego_parse_krb5_wrap(DATA_BLOB blob, DATA_BLOB *ticket)
246 {
247         BOOL ret;
248         ASN1_DATA data;
249         int data_remaining;
250
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
256         data_remaining = asn1_tag_remaining(&data);
257
258         if (data_remaining < 1) {
259                 data.has_error = True;
260         } else {
261                 
262                 *ticket = data_blob(data.data, data_remaining);
263                 asn1_read(&data, ticket->data, ticket->length);
264         }
265
266         asn1_end_tag(&data);
267
268         ret = !data.has_error;
269
270         asn1_free(&data);
271
272         return ret;
273 }
274
275
276 /* 
277    generate a SPNEGO negTokenTarg packet, ready for a EXTENDED_SECURITY
278    kerberos session setup 
279 */
280 DATA_BLOB spnego_gen_negTokenTarg(const char *principal)
281 {
282         DATA_BLOB tkt, tkt_wrapped, targ;
283         const char *krb_mechs[] = {OID_KERBEROS5_OLD, OID_NTLMSSP, NULL};
284
285         /* get a kerberos ticket for the service */
286         tkt = krb5_get_ticket(principal);
287
288         /* wrap that up in a nice GSS-API wrapping */
289         tkt_wrapped = spnego_gen_krb5_wrap(tkt);
290
291         /* and wrap that in a shiny SPNEGO wrapper */
292         targ = gen_negTokenTarg(krb_mechs, tkt_wrapped);
293
294         data_blob_free(&tkt_wrapped);
295         data_blob_free(&tkt);
296
297         return targ;
298 }
299
300
301 /*
302   parse a spnego NTLMSSP challenge packet giving two security blobs
303 */
304 BOOL spnego_parse_challenge(DATA_BLOB blob,
305                             DATA_BLOB *chal1, DATA_BLOB *chal2)
306 {
307         BOOL ret;
308         ASN1_DATA data;
309
310         ZERO_STRUCTP(chal1);
311         ZERO_STRUCTP(chal2);
312
313         asn1_load(&data, blob);
314         asn1_start_tag(&data,ASN1_CONTEXT(1));
315         asn1_start_tag(&data,ASN1_SEQUENCE(0));
316
317         asn1_start_tag(&data,ASN1_CONTEXT(0));
318         asn1_check_enumerated(&data,1);
319         asn1_end_tag(&data);
320
321         asn1_start_tag(&data,ASN1_CONTEXT(1));
322         asn1_check_OID(&data, OID_NTLMSSP);
323         asn1_end_tag(&data);
324
325         asn1_start_tag(&data,ASN1_CONTEXT(2));
326         asn1_read_OctetString(&data, chal1);
327         asn1_end_tag(&data);
328
329         /* the second challenge is optional (XP doesn't send it) */
330         if (asn1_tag_remaining(&data)) {
331                 asn1_start_tag(&data,ASN1_CONTEXT(3));
332                 asn1_read_OctetString(&data, chal2);
333                 asn1_end_tag(&data);
334         }
335
336         asn1_end_tag(&data);
337         asn1_end_tag(&data);
338
339         ret = !data.has_error;
340         asn1_free(&data);
341         return ret;
342 }
343
344
345 /*
346   generate a spnego NTLMSSP challenge packet given two security blobs
347   The second challenge is optional
348 */
349 BOOL spnego_gen_challenge(DATA_BLOB *blob,
350                           DATA_BLOB *chal1, DATA_BLOB *chal2)
351 {
352         ASN1_DATA data;
353
354         ZERO_STRUCT(data);
355
356         asn1_push_tag(&data,ASN1_CONTEXT(1));
357         asn1_push_tag(&data,ASN1_SEQUENCE(0));
358
359         asn1_push_tag(&data,ASN1_CONTEXT(0));
360         asn1_write_enumerated(&data,1);
361         asn1_pop_tag(&data);
362
363         asn1_push_tag(&data,ASN1_CONTEXT(1));
364         asn1_write_OID(&data, OID_NTLMSSP);
365         asn1_pop_tag(&data);
366
367         asn1_push_tag(&data,ASN1_CONTEXT(2));
368         asn1_write_OctetString(&data, chal1->data, chal1->length);
369         asn1_pop_tag(&data);
370
371         /* the second challenge is optional (XP doesn't send it) */
372         if (chal2) {
373                 asn1_push_tag(&data,ASN1_CONTEXT(3));
374                 asn1_write_OctetString(&data, chal2->data, chal2->length);
375                 asn1_pop_tag(&data);
376         }
377
378         asn1_pop_tag(&data);
379         asn1_pop_tag(&data);
380
381         if (data.has_error) {
382                 return False;
383         }
384
385         *blob = data_blob(data.data, data.length);
386         asn1_free(&data);
387         return True;
388 }
389
390 /*
391  generate a SPNEGO NTLMSSP auth packet. This will contain the encrypted passwords
392 */
393 DATA_BLOB spnego_gen_auth(DATA_BLOB blob)
394 {
395         ASN1_DATA data;
396         DATA_BLOB ret;
397
398         memset(&data, 0, sizeof(data));
399
400         asn1_push_tag(&data, ASN1_CONTEXT(1));
401         asn1_push_tag(&data, ASN1_SEQUENCE(0));
402         asn1_push_tag(&data, ASN1_CONTEXT(2));
403         asn1_write_OctetString(&data,blob.data,blob.length);    
404         asn1_pop_tag(&data);
405         asn1_pop_tag(&data);
406         asn1_pop_tag(&data);
407
408         ret = data_blob(data.data, data.length);
409
410         asn1_free(&data);
411
412         return ret;
413 }
414
415 /*
416  parse a SPNEGO NTLMSSP auth packet. This contains the encrypted passwords
417 */
418 BOOL spnego_parse_auth(DATA_BLOB blob, DATA_BLOB *auth)
419 {
420         ASN1_DATA data;
421
422         asn1_load(&data, blob);
423         asn1_start_tag(&data, ASN1_CONTEXT(1));
424         asn1_start_tag(&data, ASN1_SEQUENCE(0));
425         asn1_start_tag(&data, ASN1_CONTEXT(2));
426         asn1_read_OctetString(&data,auth);
427         asn1_end_tag(&data);
428         asn1_end_tag(&data);
429         asn1_end_tag(&data);
430
431         if (data.has_error) {
432                 DEBUG(3,("spnego_parse_auth failed at %d\n", (int)data.ofs));
433                 asn1_free(&data);
434                 return False;
435         }
436
437         asn1_free(&data);
438         return True;
439 }
440
441 /*
442   generate a minimal SPNEGO NTLMSSP response packet.  Doesn't contain much.
443 */
444 DATA_BLOB spnego_gen_auth_response(void)
445 {
446         ASN1_DATA data;
447         DATA_BLOB ret;
448
449         memset(&data, 0, sizeof(data));
450
451         asn1_push_tag(&data, ASN1_CONTEXT(1));
452         asn1_push_tag(&data, ASN1_SEQUENCE(0));
453         asn1_push_tag(&data, ASN1_CONTEXT(0));
454         asn1_write_enumerated(&data, 0);        
455         asn1_pop_tag(&data);
456         asn1_pop_tag(&data);
457         asn1_pop_tag(&data);
458
459         ret = data_blob(data.data, data.length);
460         asn1_free(&data);
461         return ret;
462 }
463
464 /*
465   this is a tiny msrpc packet generator. I am only using this to
466   avoid tying this code to a particular varient of our rpc code. This
467   generator is not general enough for all our rpc needs, its just
468   enough for the spnego/ntlmssp code
469
470   format specifiers are:
471
472   U = unicode string (input is unix string)
473   a = address (1 byte type, 1 byte length, unicode string, all inline)
474   A = ASCII string (pointer + length) Actually same as B
475   B = data blob (pointer + length)
476   b = data blob in header (pointer + length)
477   d = word (4 bytes)
478   C = constant ascii string
479  */
480 BOOL msrpc_gen(DATA_BLOB *blob,
481                const char *format, ...)
482 {
483         int i, n;
484         va_list ap;
485         char *s;
486         uint8 *b;
487         int head_size=0, data_size=0;
488         int head_ofs, data_ofs;
489
490         /* first scan the format to work out the header and body size */
491         va_start(ap, format);
492         for (i=0; format[i]; i++) {
493                 switch (format[i]) {
494                 case 'U':
495                         s = va_arg(ap, char *);
496                         head_size += 8;
497                         data_size += str_charnum(s) * 2;
498                         break;
499                 case 'a':
500                         n = va_arg(ap, int);
501                         s = va_arg(ap, char *);
502                         data_size += (str_charnum(s) * 2) + 4;
503                         break;
504                 case 'A':
505                 case 'B':
506                         b = va_arg(ap, uint8 *);
507                         head_size += 8;
508                         data_size += va_arg(ap, int);
509                         break;
510                 case 'b':
511                         b = va_arg(ap, uint8 *);
512                         head_size += va_arg(ap, int);
513                         break;
514                 case 'd':
515                         n = va_arg(ap, int);
516                         head_size += 4;
517                         break;
518                 case 'C':
519                         s = va_arg(ap, char *);
520                         head_size += str_charnum(s) + 1;
521                         break;
522                 }
523         }
524         va_end(ap);
525
526         /* allocate the space, then scan the format again to fill in the values */
527         *blob = data_blob(NULL, head_size + data_size);
528
529         head_ofs = 0;
530         data_ofs = head_size;
531
532         va_start(ap, format);
533         for (i=0; format[i]; i++) {
534                 switch (format[i]) {
535                 case 'U':
536                         s = va_arg(ap, char *);
537                         n = str_charnum(s);
538                         SSVAL(blob->data, head_ofs, n*2); head_ofs += 2;
539                         SSVAL(blob->data, head_ofs, n*2); head_ofs += 2;
540                         SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4;
541                         push_string(NULL, blob->data+data_ofs, s, n*2, STR_UNICODE|STR_NOALIGN);
542                         data_ofs += n*2;
543                         break;
544                 case 'a':
545                         n = va_arg(ap, int);
546                         SSVAL(blob->data, data_ofs, n); data_ofs += 2;
547                         s = va_arg(ap, char *);
548                         n = str_charnum(s);
549                         SSVAL(blob->data, data_ofs, n*2); data_ofs += 2;
550                         if (0 < n) {
551                                 push_string(NULL, blob->data+data_ofs, s, n*2,
552                                             STR_UNICODE|STR_NOALIGN);
553                         }
554                         data_ofs += n*2;
555                         break;
556                         
557                 case 'B':
558                         b = va_arg(ap, uint8 *);
559                         n = va_arg(ap, int);
560                         SSVAL(blob->data, head_ofs, n); head_ofs += 2;
561                         SSVAL(blob->data, head_ofs, n); head_ofs += 2;
562                         SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4;
563                         memcpy(blob->data+data_ofs, b, n);
564                         data_ofs += n;
565                         break;
566                 case 'd':
567                         n = va_arg(ap, int);
568                         SIVAL(blob->data, head_ofs, n); head_ofs += 4;
569                         break;
570                 case 'b':
571                         b = va_arg(ap, uint8 *);
572                         n = va_arg(ap, int);
573                         memcpy(blob->data + head_ofs, b, n);
574                         head_ofs += n;
575                         break;
576                 case 'C':
577                         s = va_arg(ap, char *);
578                         head_ofs += push_string(NULL, blob->data+head_ofs, s, -1, 
579                                                 STR_ASCII|STR_TERMINATE);
580                         break;
581                 }
582         }
583         va_end(ap);
584
585         return True;
586 }
587
588
589 /*
590   this is a tiny msrpc packet parser. This the the partner of msrpc_gen
591
592   format specifiers are:
593
594   U = unicode string (output is unix string)
595   A = ascii string
596   B = data blob
597   b = data blob in header
598   d = word (4 bytes)
599   C = constant ascii string
600  */
601 BOOL msrpc_parse(DATA_BLOB *blob,
602                  const char *format, ...)
603 {
604         int i;
605         va_list ap;
606         char **ps, *s;
607         DATA_BLOB *b;
608         int head_ofs = 0;
609         uint16 len1, len2;
610         uint32 ptr;
611         uint32 *v;
612         pstring p;
613
614         va_start(ap, format);
615         for (i=0; format[i]; i++) {
616                 switch (format[i]) {
617                 case 'U':
618                         len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
619                         len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
620                         ptr =  IVAL(blob->data, head_ofs); head_ofs += 4;
621                         /* make sure its in the right format - be strict */
622                         if (len1 != len2 || (len1&1) || ptr + len1 > blob->length) {
623                                 return False;
624                         }
625                         ps = va_arg(ap, char **);
626                         pull_string(NULL, p, blob->data + ptr, -1, len1, 
627                                     STR_UNICODE|STR_NOALIGN);
628                         (*ps) = strdup(p);
629                         break;
630                 case 'A':
631                         len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
632                         len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
633                         ptr =  IVAL(blob->data, head_ofs); head_ofs += 4;
634
635                         /* make sure its in the right format - be strict */
636                         if (len1 != len2 || ptr + len1 > blob->length) {
637                                 return False;
638                         }
639                         ps = va_arg(ap, char **);
640                         if (0 < len1) {
641                                 pull_string(NULL, p, blob->data + ptr, -1, 
642                                             len1, STR_ASCII|STR_NOALIGN);
643                                 (*ps) = strdup(p);
644                         } else {
645                                 (*ps) = NULL;
646                         }
647                         break;
648                 case 'B':
649                         len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
650                         len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
651                         ptr =  IVAL(blob->data, head_ofs); head_ofs += 4;
652                         /* make sure its in the right format - be strict */
653                         if (len1 != len2 || ptr + len1 > blob->length) {
654                                 return False;
655                         }
656                         b = (DATA_BLOB *)va_arg(ap, void *);
657                         *b = data_blob(blob->data + ptr, len1);
658                         break;
659                 case 'b':
660                         b = (DATA_BLOB *)va_arg(ap, void *);
661                         len1 = va_arg(ap, unsigned);
662                         *b = data_blob(blob->data + head_ofs, len1);
663                         head_ofs += len1;
664                         break;
665                 case 'd':
666                         v = va_arg(ap, uint32 *);
667                         *v = IVAL(blob->data, head_ofs); head_ofs += 4;
668                         break;
669                 case 'C':
670                         s = va_arg(ap, char *);
671                         head_ofs += pull_string(NULL, p, blob->data+head_ofs, -1, 
672                                                 blob->length - head_ofs, 
673                                                 STR_ASCII|STR_TERMINATE);
674                         if (strcmp(s, p) != 0) {
675                                 return False;
676                         }
677                         break;
678                 }
679         }
680         va_end(ap);
681
682         return True;
683 }
684
685 /**
686  * Print out the NTLMSSP flags for debugging 
687  */
688
689 void debug_ntlmssp_flags(uint32 neg_flags)
690 {
691         DEBUG(3,("Got NTLMSSP neg_flags=0x%08x\n", neg_flags));
692         
693         if (neg_flags & NTLMSSP_NEGOTIATE_UNICODE) 
694                 DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_UNICODE\n"));
695         if (neg_flags & NTLMSSP_NEGOTIATE_OEM) 
696                 DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_OEM\n"));
697         if (neg_flags & NTLMSSP_REQUEST_TARGET) 
698                 DEBUGADD(4, ("  NTLMSSP_REQUEST_TARGET\n"));
699         if (neg_flags & NTLMSSP_NEGOTIATE_SIGN) 
700                 DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_SIGN\n"));
701         if (neg_flags & NTLMSSP_NEGOTIATE_SEAL) 
702                 DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_SEAL\n"));
703         if (neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) 
704                 DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_LM_KEY\n"));
705         if (neg_flags & NTLMSSP_NEGOTIATE_NETWARE) 
706                 DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_NETWARE\n"));
707         if (neg_flags & NTLMSSP_NEGOTIATE_NTLM) 
708                 DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_NTLM\n"));
709         if (neg_flags & NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED) 
710                 DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED\n"));
711         if (neg_flags & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED) 
712                 DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED\n"));
713         if (neg_flags & NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL) 
714                 DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL\n"));
715         if (neg_flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN) 
716                 DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_ALWAYS_SIGN\n"));
717         if (neg_flags & NTLMSSP_NEGOTIATE_NTLM2) 
718                 DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_NTLM2\n"));
719         if (neg_flags & NTLMSSP_CHAL_TARGET_INFO) 
720                 DEBUGADD(4, ("  NTLMSSP_CHAL_TARGET_INFO\n"));
721         if (neg_flags & NTLMSSP_NEGOTIATE_128) 
722                 DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_128\n"));
723         if (neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) 
724                 DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_KEY_EXCH\n"));
725 }
726