updated the 3.0 branch from the head branch - ready for alpha18
[jra/samba/.git] / source3 / libsmb / clispnego.c
1 /* 
2    Unix SMB/CIFS implementation.
3    simple kerberos5/SPNEGO routines
4    Copyright (C) Andrew Tridgell 2001
5    
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.
10    
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.
15    
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.
19 */
20
21 #include "includes.h"
22
23 /*
24   generate a negTokenInit packet given a GUID, a list of supported
25   OIDs (the mechanisms) and a principal name string 
26 */
27 DATA_BLOB spnego_gen_negTokenInit(uint8 guid[16], 
28                                   const char *OIDs[], 
29                                   const char *principal)
30 {
31         int i;
32         ASN1_DATA data;
33         DATA_BLOB ret;
34
35         memset(&data, 0, sizeof(data));
36
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));
42
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]);
47         }
48         asn1_pop_tag(&data);
49         asn1_pop_tag(&data);
50
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);
55         asn1_pop_tag(&data);
56         asn1_pop_tag(&data);
57         asn1_pop_tag(&data);
58
59         asn1_pop_tag(&data);
60         asn1_pop_tag(&data);
61
62         asn1_pop_tag(&data);
63
64         if (data.has_error) {
65                 DEBUG(1,("Failed to build negTokenInit at offset %d\n", (int)data.ofs));
66                 asn1_free(&data);
67         }
68
69         ret = data_blob(data.data, data.length);
70         asn1_free(&data);
71
72         return ret;
73 }
74
75
76 /*
77   parse a negTokenInit packet giving a GUID, a list of supported
78   OIDs (the mechanisms) and a principal name string 
79 */
80 BOOL spnego_parse_negTokenInit(DATA_BLOB blob,
81                                uint8 guid[16], 
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_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));
96
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++) {
100                 char *oid = NULL;
101                 asn1_read_OID(&data,&oid);
102                 OIDs[i] = oid;
103         }
104         OIDs[i] = NULL;
105         asn1_end_tag(&data);
106         asn1_end_tag(&data);
107
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);
112         asn1_end_tag(&data);
113         asn1_end_tag(&data);
114         asn1_end_tag(&data);
115
116         asn1_end_tag(&data);
117         asn1_end_tag(&data);
118
119         asn1_end_tag(&data);
120
121         ret = !data.has_error;
122         asn1_free(&data);
123         return ret;
124 }
125
126
127 /*
128   generate a negTokenTarg packet given a list of OIDs and a security blob
129 */
130 DATA_BLOB gen_negTokenTarg(const char *OIDs[], DATA_BLOB blob)
131 {
132         int i;
133         ASN1_DATA data;
134         DATA_BLOB ret;
135
136         memset(&data, 0, sizeof(data));
137
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));
142
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]);
147         }
148         asn1_pop_tag(&data);
149         asn1_pop_tag(&data);
150
151         asn1_push_tag(&data, ASN1_CONTEXT(2));
152         asn1_write_OctetString(&data,blob.data,blob.length);
153         asn1_pop_tag(&data);
154
155         asn1_pop_tag(&data);
156         asn1_pop_tag(&data);
157
158         asn1_pop_tag(&data);
159
160         if (data.has_error) {
161                 DEBUG(1,("Failed to build negTokenTarg at offset %d\n", (int)data.ofs));
162                 asn1_free(&data);
163         }
164
165         ret = data_blob(data.data, data.length);
166         asn1_free(&data);
167
168         return ret;
169 }
170
171
172 /*
173   parse a negTokenTarg packet giving a list of OIDs and a security blob
174 */
175 BOOL parse_negTokenTarg(DATA_BLOB blob, char *OIDs[ASN1_MAX_OIDS], DATA_BLOB *secblob)
176 {
177         int i;
178         ASN1_DATA data;
179
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));
185
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++) {
189                 char *oid = NULL;
190                 asn1_read_OID(&data,&oid);
191                 OIDs[i] = oid;
192         }
193         OIDs[i] = NULL;
194         asn1_end_tag(&data);
195         asn1_end_tag(&data);
196
197         asn1_start_tag(&data, ASN1_CONTEXT(2));
198         asn1_read_OctetString(&data,secblob);
199         asn1_end_tag(&data);
200
201         asn1_end_tag(&data);
202         asn1_end_tag(&data);
203
204         asn1_end_tag(&data);
205
206         if (data.has_error) {
207                 DEBUG(1,("Failed to parse negTokenTarg at offset %d\n", (int)data.ofs));
208                 asn1_free(&data);
209                 return False;
210         }
211
212         asn1_free(&data);
213         return True;
214 }
215
216 /*
217   generate a krb5 GSS-API wrapper packet given a ticket
218 */
219 DATA_BLOB spnego_gen_krb5_wrap(DATA_BLOB ticket)
220 {
221         ASN1_DATA data;
222         DATA_BLOB ret;
223
224         memset(&data, 0, sizeof(data));
225
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);
230         asn1_pop_tag(&data);
231
232         if (data.has_error) {
233                 DEBUG(1,("Failed to build krb5 wrapper at offset %d\n", (int)data.ofs));
234                 asn1_free(&data);
235         }
236
237         ret = data_blob(data.data, data.length);
238         asn1_free(&data);
239
240         return ret;
241 }
242
243 /*
244   parse a krb5 GSS-API wrapper packet giving a ticket
245 */
246 BOOL spnego_parse_krb5_wrap(DATA_BLOB blob, DATA_BLOB *ticket)
247 {
248         BOOL ret;
249         ASN1_DATA data;
250         int data_remaining;
251
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
257         data_remaining = asn1_tag_remaining(&data);
258
259         if (data_remaining < 1) {
260                 data.has_error = True;
261         } else {
262                 
263                 *ticket = data_blob(data.data, data_remaining);
264                 asn1_read(&data, ticket->data, ticket->length);
265         }
266
267         asn1_end_tag(&data);
268
269         ret = !data.has_error;
270
271         asn1_free(&data);
272
273         return ret;
274 }
275
276
277 /* 
278    generate a SPNEGO negTokenTarg packet, ready for a EXTENDED_SECURITY
279    kerberos session setup 
280 */
281 DATA_BLOB spnego_gen_negTokenTarg(struct cli_state *cli, char *principal)
282 {
283         DATA_BLOB tkt, tkt_wrapped, targ;
284         const char *krb_mechs[] = {OID_KERBEROS5_OLD, OID_NTLMSSP, NULL};
285
286         /* get a kerberos ticket for the service */
287         tkt = krb5_get_ticket(principal);
288
289         /* wrap that up in a nice GSS-API wrapping */
290         tkt_wrapped = spnego_gen_krb5_wrap(tkt);
291
292         /* and wrap that in a shiny SPNEGO wrapper */
293         targ = gen_negTokenTarg(krb_mechs, tkt_wrapped);
294
295         data_blob_free(&tkt_wrapped);
296         data_blob_free(&tkt);
297
298         return targ;
299 }
300
301
302 /*
303   parse a spnego NTLMSSP challenge packet giving two security blobs
304 */
305 BOOL spnego_parse_challenge(DATA_BLOB blob,
306                             DATA_BLOB *chal1, DATA_BLOB *chal2)
307 {
308         BOOL ret;
309         ASN1_DATA data;
310
311         ZERO_STRUCTP(chal1);
312         ZERO_STRUCTP(chal2);
313
314         asn1_load(&data, blob);
315         asn1_start_tag(&data,ASN1_CONTEXT(1));
316         asn1_start_tag(&data,ASN1_SEQUENCE(0));
317
318         asn1_start_tag(&data,ASN1_CONTEXT(0));
319         asn1_check_enumerated(&data,1);
320         asn1_end_tag(&data);
321
322         asn1_start_tag(&data,ASN1_CONTEXT(1));
323         asn1_check_OID(&data, OID_NTLMSSP);
324         asn1_end_tag(&data);
325
326         asn1_start_tag(&data,ASN1_CONTEXT(2));
327         asn1_read_OctetString(&data, chal1);
328         asn1_end_tag(&data);
329
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);
335         }
336
337         asn1_end_tag(&data);
338         asn1_end_tag(&data);
339
340         ret = !data.has_error;
341         asn1_free(&data);
342         return ret;
343 }
344
345
346 /*
347   generate a spnego NTLMSSP challenge packet given two security blobs
348   The second challenge is optional
349 */
350 BOOL spnego_gen_challenge(DATA_BLOB *blob,
351                           DATA_BLOB *chal1, DATA_BLOB *chal2)
352 {
353         ASN1_DATA data;
354
355         ZERO_STRUCT(data);
356
357         asn1_push_tag(&data,ASN1_CONTEXT(1));
358         asn1_push_tag(&data,ASN1_SEQUENCE(0));
359
360         asn1_push_tag(&data,ASN1_CONTEXT(0));
361         asn1_write_enumerated(&data,1);
362         asn1_pop_tag(&data);
363
364         asn1_push_tag(&data,ASN1_CONTEXT(1));
365         asn1_write_OID(&data, OID_NTLMSSP);
366         asn1_pop_tag(&data);
367
368         asn1_push_tag(&data,ASN1_CONTEXT(2));
369         asn1_write_OctetString(&data, chal1->data, chal1->length);
370         asn1_pop_tag(&data);
371
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);
377         }
378
379         asn1_pop_tag(&data);
380         asn1_pop_tag(&data);
381
382         if (data.has_error) {
383                 return False;
384         }
385
386         *blob = data_blob(data.data, data.length);
387         asn1_free(&data);
388         return True;
389 }
390
391 /*
392  generate a SPNEGO NTLMSSP auth packet. This will contain the encrypted passwords
393 */
394 DATA_BLOB spnego_gen_auth(DATA_BLOB blob)
395 {
396         ASN1_DATA data;
397         DATA_BLOB ret;
398
399         memset(&data, 0, sizeof(data));
400
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);
408
409         ret = data_blob(data.data, data.length);
410
411         asn1_free(&data);
412
413         return ret;
414 }
415
416 /*
417  parse a SPNEGO NTLMSSP auth packet. This contains the encrypted passwords
418 */
419 BOOL spnego_parse_auth(DATA_BLOB blob, DATA_BLOB *auth)
420 {
421         ASN1_DATA data;
422
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);
431
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;
436         }
437
438         asn1_free(&data);
439         return True;
440 }
441
442
443 /*
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
448
449   format specifiers are:
450
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
456  */
457 BOOL msrpc_gen(DATA_BLOB *blob,
458                const char *format, ...)
459 {
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;
466
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;
493                 }
494         }
495         va_end(ap);
496
497         /* allocate the space, then scan the format again to fill in the values */
498         *blob = data_blob(NULL, head_size + data_size);
499
500         head_ofs = 0;
501         data_ofs = head_size;
502
503         va_start(ap, format);
504         for (i=0; format[i]; i++) {
505                 switch (format[i]) {
506                 case 'U':
507                         s = va_arg(ap, char *);
508                         n = str_charnum(s);
509                         SSVAL(blob->data, head_ofs, n*2); head_ofs += 2;
510                         SSVAL(blob->data, head_ofs, n*2); head_ofs += 2;
511                         SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4;
512                         push_string(NULL, blob->data+data_ofs, s, n*2, STR_UNICODE|STR_NOALIGN);
513                         data_ofs += n*2;
514                         break;
515                 case 'B':
516                         b = va_arg(ap, uint8 *);
517                         n = va_arg(ap, int);
518                         SSVAL(blob->data, head_ofs, n); head_ofs += 2;
519                         SSVAL(blob->data, head_ofs, n); head_ofs += 2;
520                         SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4;
521                         memcpy(blob->data+data_ofs, b, n);
522                         data_ofs += n;
523                         break;
524                 case 'd':
525                         n = va_arg(ap, int);
526                         SIVAL(blob->data, head_ofs, n); head_ofs += 4;
527                         break;
528                 case 'b':
529                         b = va_arg(ap, uint8 *);
530                         n = va_arg(ap, int);
531                         memcpy(blob->data + head_ofs, b, n);
532                         head_ofs += n;
533                         break;
534                 case 'C':
535                         s = va_arg(ap, char *);
536                         head_ofs += push_string(NULL, blob->data+head_ofs, s, -1, 
537                                                 STR_ASCII|STR_TERMINATE);
538                         break;
539                 }
540         }
541         va_end(ap);
542
543         return True;
544 }
545
546
547 /*
548   this is a tiny msrpc packet parser. This the the partner of msrpc_gen
549
550   format specifiers are:
551
552   U = unicode string (output is unix string)
553   B = data blob
554   b = data blob in header
555   d = word (4 bytes)
556   C = constant ascii string
557  */
558 BOOL msrpc_parse(DATA_BLOB *blob,
559                  const char *format, ...)
560 {
561         int i;
562         va_list ap;
563         char **ps, *s;
564         DATA_BLOB *b;
565         int head_ofs = 0;
566         uint16 len1, len2;
567         uint32 ptr;
568         uint32 *v;
569         pstring p;
570
571         va_start(ap, format);
572         for (i=0; format[i]; i++) {
573                 switch (format[i]) {
574                 case 'U':
575                         len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
576                         len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
577                         ptr =  IVAL(blob->data, head_ofs); head_ofs += 4;
578                         /* make sure its in the right format - be strict */
579                         if (len1 != len2 || (len1&1) || ptr + len1 > blob->length) {
580                                 return False;
581                         }
582                         ps = va_arg(ap, char **);
583                         pull_string(NULL, p, blob->data + ptr, -1, len1, 
584                                     STR_UNICODE|STR_NOALIGN);
585                         (*ps) = strdup(p);
586                         break;
587                 case 'B':
588                         len1 = SVAL(blob->data, head_ofs); head_ofs += 2;
589                         len2 = SVAL(blob->data, head_ofs); head_ofs += 2;
590                         ptr =  IVAL(blob->data, head_ofs); head_ofs += 4;
591                         /* make sure its in the right format - be strict */
592                         if (len1 != len2 || ptr + len1 > blob->length) {
593                                 return False;
594                         }
595                         b = (DATA_BLOB *)va_arg(ap, void *);
596                         *b = data_blob(blob->data + ptr, len1);
597                         break;
598                 case 'b':
599                         b = (DATA_BLOB *)va_arg(ap, void *);
600                         len1 = va_arg(ap, unsigned);
601                         *b = data_blob(blob->data + head_ofs, len1);
602                         head_ofs += len1;
603                         break;
604                 case 'd':
605                         v = va_arg(ap, uint32 *);
606                         *v = IVAL(blob->data, head_ofs); head_ofs += 4;
607                         break;
608                 case 'C':
609                         s = va_arg(ap, char *);
610                         head_ofs += pull_string(NULL, p, blob->data+head_ofs, -1, 
611                                                 blob->length - head_ofs, 
612                                                 STR_ASCII|STR_TERMINATE);
613                         if (strcmp(s, p) != 0) {
614                                 return False;
615                         }
616                         break;
617                 }
618         }
619         va_end(ap);
620
621         return True;
622 }
623
624 /**
625  * Print out the NTLMSSP flags for debugging 
626  */
627
628 void debug_ntlmssp_flags(uint32 neg_flags)
629 {
630         if (neg_flags & NTLMSSP_NEGOTIATE_UNICODE) 
631                 DEBUG(4, ("  NTLMSSP_NEGOTIATE_UNICODE\n"));
632         if (neg_flags & NTLMSSP_NEGOTIATE_OEM) 
633                 DEBUG(4, ("  NTLMSSP_NEGOTIATE_OEM\n"));
634         if (neg_flags & NTLMSSP_REQUEST_TARGET) 
635                 DEBUG(4, ("  NTLMSSP_REQUEST_TARGET\n"));
636         if (neg_flags & NTLMSSP_NEGOTIATE_SIGN) 
637                 DEBUG(4, ("  NTLMSSP_NEGOTIATE_SIGN\n"));
638         if (neg_flags & NTLMSSP_NEGOTIATE_SIGN) 
639                 DEBUG(4, ("  NTLMSSP_NEGOTIATE_SEAL\n"));
640         if (neg_flags & NTLMSSP_NEGOTIATE_LM_KEY) 
641                 DEBUG(4, ("  NTLMSSP_NEGOTIATE_LM_KEY\n"));
642         if (neg_flags & NTLMSSP_NEGOTIATE_NETWARE) 
643                 DEBUG(4, ("  NTLMSSP_NEGOTIATE_NETWARE\n"));
644         if (neg_flags & NTLMSSP_NEGOTIATE_NTLM) 
645                 DEBUG(4, ("  NTLMSSP_NEGOTIATE_NTLM\n"));
646         if (neg_flags & NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED) 
647                 DEBUG(4, ("  NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED\n"));
648         if (neg_flags & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED) 
649                 DEBUG(4, ("  NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED\n"));
650         if (neg_flags & NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL) 
651                 DEBUG(4, ("  NTLMSSP_NEGOTIATE_THIS_IS_LOCAL_CALL\n"));
652         if (neg_flags & NTLMSSP_NEGOTIATE_ALWAYS_SIGN) 
653                 DEBUG(4, ("  NTLMSSP_NEGOTIATE_ALWAYS_SIGN\n"));
654         if (neg_flags & NTLMSSP_NEGOTIATE_NTLM2) 
655                 DEBUG(4, ("  NTLMSSP_NEGOTIATE_NTLM2\n"));
656         if (neg_flags & NTLMSSP_CHAL_TARGET_INFO) 
657                 DEBUG(4, ("  NTLMSSP_CHAL_TARGET_INFO\n"));
658         if (neg_flags & NTLMSSP_NEGOTIATE_128) 
659                 DEBUG(4, ("  NTLMSSP_NEGOTIATE_128\n"));
660         if (neg_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) 
661                 DEBUG(4, ("  NTLMSSP_NEGOTIATE_KEY_EXCH\n"));
662 }
663