Add a bit of 'const' and move a lot of our 'repeditive' DEBUG() statements to
[tprouty/samba.git] / source / 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   B = data blob (pointer + length)
475   b = data blob in header (pointer + length)
476   d = word (4 bytes)
477   C = constant ascii string
478  */
479 BOOL msrpc_gen(DATA_BLOB *blob,
480                const char *format, ...)
481 {
482         int i, n;
483         va_list ap;
484         char *s;
485         uint8 *b;
486         int head_size=0, data_size=0;
487         int head_ofs, data_ofs;
488
489         /* first scan the format to work out the header and body size */
490         va_start(ap, format);
491         for (i=0; format[i]; i++) {
492                 switch (format[i]) {
493                 case 'U':
494                         s = va_arg(ap, char *);
495                         head_size += 8;
496                         data_size += str_charnum(s) * 2;
497                         break;
498                 case 'a':
499                         n = va_arg(ap, int);
500                         s = va_arg(ap, char *);
501                         data_size += (str_charnum(s) * 2) + 4;
502                         break;
503                 case 'B':
504                         b = va_arg(ap, uint8 *);
505                         head_size += 8;
506                         data_size += va_arg(ap, int);
507                         break;
508                 case 'b':
509                         b = va_arg(ap, uint8 *);
510                         head_size += va_arg(ap, int);
511                         break;
512                 case 'd':
513                         n = va_arg(ap, int);
514                         head_size += 4;
515                         break;
516                 case 'C':
517                         s = va_arg(ap, char *);
518                         head_size += str_charnum(s) + 1;
519                         break;
520                 }
521         }
522         va_end(ap);
523
524         /* allocate the space, then scan the format again to fill in the values */
525         *blob = data_blob(NULL, head_size + data_size);
526
527         head_ofs = 0;
528         data_ofs = head_size;
529
530         va_start(ap, format);
531         for (i=0; format[i]; i++) {
532                 switch (format[i]) {
533                 case 'U':
534                         s = va_arg(ap, char *);
535                         n = str_charnum(s);
536                         SSVAL(blob->data, head_ofs, n*2); head_ofs += 2;
537                         SSVAL(blob->data, head_ofs, n*2); head_ofs += 2;
538                         SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4;
539                         push_string(NULL, blob->data+data_ofs, s, n*2, STR_UNICODE|STR_NOALIGN);
540                         data_ofs += n*2;
541                         break;
542                 case 'a':
543                         n = va_arg(ap, int);
544                         SSVAL(blob->data, data_ofs, n); data_ofs += 2;
545                         s = va_arg(ap, char *);
546                         n = str_charnum(s);
547                         SSVAL(blob->data, data_ofs, n*2); data_ofs += 2;
548                         if (0 < n) {
549                                 push_string(NULL, blob->data+data_ofs, s, n*2,
550                                             STR_UNICODE|STR_NOALIGN);
551                         }
552                         data_ofs += n*2;
553                         break;
554                         
555                 case 'B':
556                         b = va_arg(ap, uint8 *);
557                         n = va_arg(ap, int);
558                         SSVAL(blob->data, head_ofs, n); head_ofs += 2;
559                         SSVAL(blob->data, head_ofs, n); head_ofs += 2;
560                         SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4;
561                         memcpy(blob->data+data_ofs, b, n);
562                         data_ofs += n;
563                         break;
564                 case 'd':
565                         n = va_arg(ap, int);
566                         SIVAL(blob->data, head_ofs, n); head_ofs += 4;
567                         break;
568                 case 'b':
569                         b = va_arg(ap, uint8 *);
570                         n = va_arg(ap, int);
571                         memcpy(blob->data + head_ofs, b, n);
572                         head_ofs += n;
573                         break;
574                 case 'C':
575                         s = va_arg(ap, char *);
576                         head_ofs += push_string(NULL, blob->data+head_ofs, s, -1, 
577                                                 STR_ASCII|STR_TERMINATE);
578                         break;
579                 }
580         }
581         va_end(ap);
582
583         return True;
584 }
585
586
587 /*
588   this is a tiny msrpc packet parser. This the the partner of msrpc_gen
589
590   format specifiers are:
591
592   U = unicode string (output is unix string)
593   A = ascii string
594   B = data blob
595   b = data blob in header
596   d = word (4 bytes)
597   C = constant ascii string
598  */
599 BOOL msrpc_parse(DATA_BLOB *blob,
600                  const char *format, ...)
601 {
602         int i;
603         va_list ap;
604         char **ps, *s;
605         DATA_BLOB *b;
606         int head_ofs = 0;
607         uint16 len1, len2;
608         uint32 ptr;
609         uint32 *v;
610         pstring p;
611
612         va_start(ap, format);
613         for (i=0; format[i]; i++) {
614                 switch (format[i]) {
615                 case 'U':
616                         len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
617                         len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
618                         ptr =  IVAL(blob->data, head_ofs); head_ofs += 4;
619                         /* make sure its in the right format - be strict */
620                         if (len1 != len2 || (len1&1) || ptr + len1 > blob->length) {
621                                 return False;
622                         }
623                         ps = va_arg(ap, char **);
624                         pull_string(NULL, p, blob->data + ptr, -1, len1, 
625                                     STR_UNICODE|STR_NOALIGN);
626                         (*ps) = strdup(p);
627                         break;
628                 case 'A':
629                         len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
630                         len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
631                         ptr =  IVAL(blob->data, head_ofs); head_ofs += 4;
632
633                         /* make sure its in the right format - be strict */
634                         if (len1 != len2 || ptr + len1 > blob->length) {
635                                 return False;
636                         }
637                         ps = va_arg(ap, char **);
638                         if (0 < len1) {
639                                 pull_string(NULL, p, blob->data + ptr, -1, 
640                                             len1, STR_ASCII|STR_NOALIGN);
641                                 (*ps) = strdup(p);
642                         } else {
643                                 (*ps) = NULL;
644                         }
645                         break;
646                 case 'B':
647                         len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
648                         len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
649                         ptr =  IVAL(blob->data, head_ofs); head_ofs += 4;
650                         /* make sure its in the right format - be strict */
651                         if (len1 != len2 || ptr + len1 > blob->length) {
652                                 return False;
653                         }
654                         b = (DATA_BLOB *)va_arg(ap, void *);
655                         *b = data_blob(blob->data + ptr, len1);
656                         break;
657                 case 'b':
658                         b = (DATA_BLOB *)va_arg(ap, void *);
659                         len1 = va_arg(ap, unsigned);
660                         *b = data_blob(blob->data + head_ofs, len1);
661                         head_ofs += len1;
662                         break;
663                 case 'd':
664                         v = va_arg(ap, uint32 *);
665                         *v = IVAL(blob->data, head_ofs); head_ofs += 4;
666                         break;
667                 case 'C':
668                         s = va_arg(ap, char *);
669                         head_ofs += pull_string(NULL, p, blob->data+head_ofs, -1, 
670                                                 blob->length - head_ofs, 
671                                                 STR_ASCII|STR_TERMINATE);
672                         if (strcmp(s, p) != 0) {
673                                 return False;
674                         }
675                         break;
676                 }
677         }
678         va_end(ap);
679
680         return True;
681 }
682
683 /**
684  * Print out the NTLMSSP flags for debugging 
685  */
686
687 void debug_ntlmssp_flags(uint32 neg_flags)
688 {
689         DEBUG(3,("Got NTLMSSP neg_flags=0x%08x\n", neg_flags));
690         
691         if (neg_flags & NTLMSSP_NEGOTIATE_UNICODE) 
692                 DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_UNICODE\n"));
693         if (neg_flags & NTLMSSP_NEGOTIATE_OEM) 
694                 DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_OEM\n"));
695         if (neg_flags & NTLMSSP_REQUEST_TARGET) 
696                 DEBUGADD(4, ("  NTLMSSP_REQUEST_TARGET\n"));
697         if (neg_flags & NTLMSSP_NEGOTIATE_SIGN) 
698                 DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_SIGN\n"));
699         if (neg_flags & NTLMSSP_NEGOTIATE_SEAL) 
700                 DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_SEAL\n"));
701         if (neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) 
702                 DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_LM_KEY\n"));
703         if (neg_flags & NTLMSSP_NEGOTIATE_NETWARE) 
704                 DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_NETWARE\n"));
705         if (neg_flags & NTLMSSP_NEGOTIATE_NTLM) 
706                 DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_NTLM\n"));
707         if (neg_flags & NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED) 
708                 DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED\n"));
709         if (neg_flags & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED) 
710                 DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED\n"));
711         if (neg_flags & NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL) 
712                 DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL\n"));
713         if (neg_flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN) 
714                 DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_ALWAYS_SIGN\n"));
715         if (neg_flags & NTLMSSP_NEGOTIATE_NTLM2) 
716                 DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_NTLM2\n"));
717         if (neg_flags & NTLMSSP_CHAL_TARGET_INFO) 
718                 DEBUGADD(4, ("  NTLMSSP_CHAL_TARGET_INFO\n"));
719         if (neg_flags & NTLMSSP_NEGOTIATE_128) 
720                 DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_128\n"));
721         if (neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) 
722                 DEBUGADD(4, ("  NTLMSSP_NEGOTIATE_KEY_EXCH\n"));
723 }
724