RIP BOOL. Convert BOOL -> bool. I found a few interesting
[ira/wip.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 <jmcd@us.ibm.com> 2002
6    Copyright (C) Luke Howard     2003
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
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(char 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   Generate a negTokenInit as used by the client side ... It has a mechType
78   (OID), and a mechToken (a security blob) ... 
79
80   Really, we need to break out the NTLMSSP stuff as well, because it could be
81   raw in the packets!
82 */
83 DATA_BLOB gen_negTokenInit(const char *OID, DATA_BLOB blob)
84 {
85         ASN1_DATA data;
86         DATA_BLOB ret;
87
88         memset(&data, 0, sizeof(data));
89
90         asn1_push_tag(&data, ASN1_APPLICATION(0));
91         asn1_write_OID(&data,OID_SPNEGO);
92         asn1_push_tag(&data, ASN1_CONTEXT(0));
93         asn1_push_tag(&data, ASN1_SEQUENCE(0));
94
95         asn1_push_tag(&data, ASN1_CONTEXT(0));
96         asn1_push_tag(&data, ASN1_SEQUENCE(0));
97         asn1_write_OID(&data, OID);
98         asn1_pop_tag(&data);
99         asn1_pop_tag(&data);
100
101         asn1_push_tag(&data, ASN1_CONTEXT(2));
102         asn1_write_OctetString(&data,blob.data,blob.length);
103         asn1_pop_tag(&data);
104
105         asn1_pop_tag(&data);
106         asn1_pop_tag(&data);
107
108         asn1_pop_tag(&data);
109
110         if (data.has_error) {
111                 DEBUG(1,("Failed to build negTokenInit at offset %d\n", (int)data.ofs));
112                 asn1_free(&data);
113         }
114
115         ret = data_blob(data.data, data.length);
116         asn1_free(&data);
117
118         return ret;
119 }
120
121 /*
122   parse a negTokenInit packet giving a GUID, a list of supported
123   OIDs (the mechanisms) and a principal name string 
124 */
125 bool spnego_parse_negTokenInit(DATA_BLOB blob,
126                                char *OIDs[ASN1_MAX_OIDS], 
127                                char **principal)
128 {
129         int i;
130         bool ret;
131         ASN1_DATA data;
132
133         asn1_load(&data, blob);
134
135         asn1_start_tag(&data,ASN1_APPLICATION(0));
136         asn1_check_OID(&data,OID_SPNEGO);
137         asn1_start_tag(&data,ASN1_CONTEXT(0));
138         asn1_start_tag(&data,ASN1_SEQUENCE(0));
139
140         asn1_start_tag(&data,ASN1_CONTEXT(0));
141         asn1_start_tag(&data,ASN1_SEQUENCE(0));
142         for (i=0; asn1_tag_remaining(&data) > 0 && i < ASN1_MAX_OIDS-1; i++) {
143                 char *oid_str = NULL;
144                 asn1_read_OID(&data,&oid_str);
145                 OIDs[i] = oid_str;
146         }
147         OIDs[i] = NULL;
148         asn1_end_tag(&data);
149         asn1_end_tag(&data);
150
151         *principal = NULL;
152         if (asn1_tag_remaining(&data) > 0) {
153                 asn1_start_tag(&data, ASN1_CONTEXT(3));
154                 asn1_start_tag(&data, ASN1_SEQUENCE(0));
155                 asn1_start_tag(&data, ASN1_CONTEXT(0));
156                 asn1_read_GeneralString(&data,principal);
157                 asn1_end_tag(&data);
158                 asn1_end_tag(&data);
159                 asn1_end_tag(&data);
160         }
161
162         asn1_end_tag(&data);
163         asn1_end_tag(&data);
164
165         asn1_end_tag(&data);
166
167         ret = !data.has_error;
168         if (data.has_error) {
169                 int j;
170                 SAFE_FREE(*principal);
171                 for(j = 0; j < i && j < ASN1_MAX_OIDS-1; j++) {
172                         SAFE_FREE(OIDs[j]);
173                 }
174         }
175
176         asn1_free(&data);
177         return ret;
178 }
179
180 /*
181   generate a negTokenTarg packet given a list of OIDs and a security blob
182 */
183 DATA_BLOB gen_negTokenTarg(const char *OIDs[], DATA_BLOB blob)
184 {
185         int i;
186         ASN1_DATA data;
187         DATA_BLOB ret;
188
189         memset(&data, 0, sizeof(data));
190
191         asn1_push_tag(&data, ASN1_APPLICATION(0));
192         asn1_write_OID(&data,OID_SPNEGO);
193         asn1_push_tag(&data, ASN1_CONTEXT(0));
194         asn1_push_tag(&data, ASN1_SEQUENCE(0));
195
196         asn1_push_tag(&data, ASN1_CONTEXT(0));
197         asn1_push_tag(&data, ASN1_SEQUENCE(0));
198         for (i=0; OIDs[i]; i++) {
199                 asn1_write_OID(&data,OIDs[i]);
200         }
201         asn1_pop_tag(&data);
202         asn1_pop_tag(&data);
203
204         asn1_push_tag(&data, ASN1_CONTEXT(2));
205         asn1_write_OctetString(&data,blob.data,blob.length);
206         asn1_pop_tag(&data);
207
208         asn1_pop_tag(&data);
209         asn1_pop_tag(&data);
210
211         asn1_pop_tag(&data);
212
213         if (data.has_error) {
214                 DEBUG(1,("Failed to build negTokenTarg at offset %d\n", (int)data.ofs));
215                 asn1_free(&data);
216         }
217
218         ret = data_blob(data.data, data.length);
219         asn1_free(&data);
220
221         return ret;
222 }
223
224 /*
225   parse a negTokenTarg packet giving a list of OIDs and a security blob
226 */
227 bool parse_negTokenTarg(DATA_BLOB blob, char *OIDs[ASN1_MAX_OIDS], DATA_BLOB *secblob)
228 {
229         int i;
230         ASN1_DATA data;
231
232         asn1_load(&data, blob);
233         asn1_start_tag(&data, ASN1_APPLICATION(0));
234         asn1_check_OID(&data,OID_SPNEGO);
235         asn1_start_tag(&data, ASN1_CONTEXT(0));
236         asn1_start_tag(&data, ASN1_SEQUENCE(0));
237
238         asn1_start_tag(&data, ASN1_CONTEXT(0));
239         asn1_start_tag(&data, ASN1_SEQUENCE(0));
240         for (i=0; asn1_tag_remaining(&data) > 0 && i < ASN1_MAX_OIDS-1; i++) {
241                 char *oid_str = NULL;
242                 asn1_read_OID(&data,&oid_str);
243                 OIDs[i] = oid_str;
244         }
245         OIDs[i] = NULL;
246         asn1_end_tag(&data);
247         asn1_end_tag(&data);
248
249         asn1_start_tag(&data, ASN1_CONTEXT(2));
250         asn1_read_OctetString(&data,secblob);
251         asn1_end_tag(&data);
252
253         asn1_end_tag(&data);
254         asn1_end_tag(&data);
255
256         asn1_end_tag(&data);
257
258         if (data.has_error) {
259                 int j;
260                 data_blob_free(secblob);
261                 for(j = 0; j < i && j < ASN1_MAX_OIDS-1; j++) {
262                         SAFE_FREE(OIDs[j]);
263                 }
264                 DEBUG(1,("Failed to parse negTokenTarg at offset %d\n", (int)data.ofs));
265                 asn1_free(&data);
266                 return False;
267         }
268
269         asn1_free(&data);
270         return True;
271 }
272
273 /*
274   generate a krb5 GSS-API wrapper packet given a ticket
275 */
276 DATA_BLOB spnego_gen_krb5_wrap(const DATA_BLOB ticket, const uint8 tok_id[2])
277 {
278         ASN1_DATA data;
279         DATA_BLOB ret;
280
281         memset(&data, 0, sizeof(data));
282
283         asn1_push_tag(&data, ASN1_APPLICATION(0));
284         asn1_write_OID(&data, OID_KERBEROS5);
285
286         asn1_write(&data, tok_id, 2);
287         asn1_write(&data, ticket.data, ticket.length);
288         asn1_pop_tag(&data);
289
290         if (data.has_error) {
291                 DEBUG(1,("Failed to build krb5 wrapper at offset %d\n", (int)data.ofs));
292                 asn1_free(&data);
293         }
294
295         ret = data_blob(data.data, data.length);
296         asn1_free(&data);
297
298         return ret;
299 }
300
301 /*
302   parse a krb5 GSS-API wrapper packet giving a ticket
303 */
304 bool spnego_parse_krb5_wrap(DATA_BLOB blob, DATA_BLOB *ticket, uint8 tok_id[2])
305 {
306         bool ret;
307         ASN1_DATA data;
308         int data_remaining;
309
310         asn1_load(&data, blob);
311         asn1_start_tag(&data, ASN1_APPLICATION(0));
312         asn1_check_OID(&data, OID_KERBEROS5);
313
314         data_remaining = asn1_tag_remaining(&data);
315
316         if (data_remaining < 3) {
317                 data.has_error = True;
318         } else {
319                 asn1_read(&data, tok_id, 2);
320                 data_remaining -= 2;
321                 *ticket = data_blob(NULL, data_remaining);
322                 asn1_read(&data, ticket->data, ticket->length);
323         }
324
325         asn1_end_tag(&data);
326
327         ret = !data.has_error;
328
329         if (data.has_error) {
330                 data_blob_free(ticket);
331         }
332
333         asn1_free(&data);
334
335         return ret;
336 }
337
338
339 /* 
340    generate a SPNEGO negTokenTarg packet, ready for a EXTENDED_SECURITY
341    kerberos session setup 
342 */
343 int spnego_gen_negTokenTarg(const char *principal, int time_offset, 
344                             DATA_BLOB *targ, 
345                             DATA_BLOB *session_key_krb5, uint32 extra_ap_opts,
346                             time_t *expire_time)
347 {
348         int retval;
349         DATA_BLOB tkt, tkt_wrapped;
350         const char *krb_mechs[] = {OID_KERBEROS5_OLD, OID_KERBEROS5, OID_NTLMSSP, NULL};
351
352         /* get a kerberos ticket for the service and extract the session key */
353         retval = cli_krb5_get_ticket(principal, time_offset,
354                                         &tkt, session_key_krb5, extra_ap_opts, NULL, 
355                                         expire_time);
356
357         if (retval)
358                 return retval;
359
360         /* wrap that up in a nice GSS-API wrapping */
361         tkt_wrapped = spnego_gen_krb5_wrap(tkt, TOK_ID_KRB_AP_REQ);
362
363         /* and wrap that in a shiny SPNEGO wrapper */
364         *targ = gen_negTokenTarg(krb_mechs, tkt_wrapped);
365
366         data_blob_free(&tkt_wrapped);
367         data_blob_free(&tkt);
368
369         return retval;
370 }
371
372
373 /*
374   parse a spnego NTLMSSP challenge packet giving two security blobs
375 */
376 bool spnego_parse_challenge(const DATA_BLOB blob,
377                             DATA_BLOB *chal1, DATA_BLOB *chal2)
378 {
379         bool ret;
380         ASN1_DATA data;
381
382         ZERO_STRUCTP(chal1);
383         ZERO_STRUCTP(chal2);
384
385         asn1_load(&data, blob);
386         asn1_start_tag(&data,ASN1_CONTEXT(1));
387         asn1_start_tag(&data,ASN1_SEQUENCE(0));
388
389         asn1_start_tag(&data,ASN1_CONTEXT(0));
390         asn1_check_enumerated(&data,1);
391         asn1_end_tag(&data);
392
393         asn1_start_tag(&data,ASN1_CONTEXT(1));
394         asn1_check_OID(&data, OID_NTLMSSP);
395         asn1_end_tag(&data);
396
397         asn1_start_tag(&data,ASN1_CONTEXT(2));
398         asn1_read_OctetString(&data, chal1);
399         asn1_end_tag(&data);
400
401         /* the second challenge is optional (XP doesn't send it) */
402         if (asn1_tag_remaining(&data)) {
403                 asn1_start_tag(&data,ASN1_CONTEXT(3));
404                 asn1_read_OctetString(&data, chal2);
405                 asn1_end_tag(&data);
406         }
407
408         asn1_end_tag(&data);
409         asn1_end_tag(&data);
410
411         ret = !data.has_error;
412
413         if (data.has_error) {
414                 data_blob_free(chal1);
415                 data_blob_free(chal2);
416         }
417
418         asn1_free(&data);
419         return ret;
420 }
421
422
423 /*
424  generate a SPNEGO auth packet. This will contain the encrypted passwords
425 */
426 DATA_BLOB spnego_gen_auth(DATA_BLOB blob)
427 {
428         ASN1_DATA data;
429         DATA_BLOB ret;
430
431         memset(&data, 0, sizeof(data));
432
433         asn1_push_tag(&data, ASN1_CONTEXT(1));
434         asn1_push_tag(&data, ASN1_SEQUENCE(0));
435         asn1_push_tag(&data, ASN1_CONTEXT(2));
436         asn1_write_OctetString(&data,blob.data,blob.length);    
437         asn1_pop_tag(&data);
438         asn1_pop_tag(&data);
439         asn1_pop_tag(&data);
440
441         ret = data_blob(data.data, data.length);
442
443         asn1_free(&data);
444
445         return ret;
446 }
447
448 /*
449  parse a SPNEGO auth packet. This contains the encrypted passwords
450 */
451 bool spnego_parse_auth(DATA_BLOB blob, DATA_BLOB *auth)
452 {
453         ASN1_DATA data;
454
455         asn1_load(&data, blob);
456         asn1_start_tag(&data, ASN1_CONTEXT(1));
457         asn1_start_tag(&data, ASN1_SEQUENCE(0));
458         asn1_start_tag(&data, ASN1_CONTEXT(2));
459         asn1_read_OctetString(&data,auth);
460         asn1_end_tag(&data);
461         asn1_end_tag(&data);
462         asn1_end_tag(&data);
463
464         if (data.has_error) {
465                 DEBUG(3,("spnego_parse_auth failed at %d\n", (int)data.ofs));
466                 data_blob_free(auth);
467                 asn1_free(&data);
468                 return False;
469         }
470
471         asn1_free(&data);
472         return True;
473 }
474
475 /*
476   generate a minimal SPNEGO response packet.  Doesn't contain much.
477 */
478 DATA_BLOB spnego_gen_auth_response(DATA_BLOB *reply, NTSTATUS nt_status,
479                                    const char *mechOID)
480 {
481         ASN1_DATA data;
482         DATA_BLOB ret;
483         uint8 negResult;
484
485         if (NT_STATUS_IS_OK(nt_status)) {
486                 negResult = SPNEGO_NEG_RESULT_ACCEPT;
487         } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
488                 negResult = SPNEGO_NEG_RESULT_INCOMPLETE; 
489         } else {
490                 negResult = SPNEGO_NEG_RESULT_REJECT; 
491         }
492
493         ZERO_STRUCT(data);
494
495         asn1_push_tag(&data, ASN1_CONTEXT(1));
496         asn1_push_tag(&data, ASN1_SEQUENCE(0));
497         asn1_push_tag(&data, ASN1_CONTEXT(0));
498         asn1_write_enumerated(&data, negResult);
499         asn1_pop_tag(&data);
500
501         if (reply->data != NULL) {
502                 asn1_push_tag(&data,ASN1_CONTEXT(1));
503                 asn1_write_OID(&data, mechOID);
504                 asn1_pop_tag(&data);
505                 
506                 asn1_push_tag(&data,ASN1_CONTEXT(2));
507                 asn1_write_OctetString(&data, reply->data, reply->length);
508                 asn1_pop_tag(&data);
509         }
510
511         asn1_pop_tag(&data);
512         asn1_pop_tag(&data);
513
514         ret = data_blob(data.data, data.length);
515         asn1_free(&data);
516         return ret;
517 }
518
519 /*
520  parse a SPNEGO auth packet. This contains the encrypted passwords
521 */
522 bool spnego_parse_auth_response(DATA_BLOB blob, NTSTATUS nt_status,
523                                 const char *mechOID,
524                                 DATA_BLOB *auth)
525 {
526         ASN1_DATA data;
527         uint8 negResult;
528
529         if (NT_STATUS_IS_OK(nt_status)) {
530                 negResult = SPNEGO_NEG_RESULT_ACCEPT;
531         } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
532                 negResult = SPNEGO_NEG_RESULT_INCOMPLETE;
533         } else {
534                 negResult = SPNEGO_NEG_RESULT_REJECT;
535         }
536
537         asn1_load(&data, blob);
538         asn1_start_tag(&data, ASN1_CONTEXT(1));
539         asn1_start_tag(&data, ASN1_SEQUENCE(0));
540         asn1_start_tag(&data, ASN1_CONTEXT(0));
541         asn1_check_enumerated(&data, negResult);
542         asn1_end_tag(&data);
543
544         *auth = data_blob_null;
545
546         if (asn1_tag_remaining(&data)) {
547                 asn1_start_tag(&data,ASN1_CONTEXT(1));
548                 asn1_check_OID(&data, mechOID);
549                 asn1_end_tag(&data);
550
551                 if (asn1_tag_remaining(&data)) {
552                         asn1_start_tag(&data,ASN1_CONTEXT(2));
553                         asn1_read_OctetString(&data, auth);
554                         asn1_end_tag(&data);
555                 }
556         } else if (negResult == SPNEGO_NEG_RESULT_INCOMPLETE) {
557                 data.has_error = 1;
558         }
559
560         /* Binding against Win2K DC returns a duplicate of the responseToken in
561          * the optional mechListMIC field. This is a bug in Win2K. We ignore
562          * this field if it exists. Win2K8 may return a proper mechListMIC at
563          * which point we need to implement the integrity checking. */
564         if (asn1_tag_remaining(&data)) {
565                 DATA_BLOB mechList = data_blob_null;
566                 asn1_start_tag(&data, ASN1_CONTEXT(3));
567                 asn1_read_OctetString(&data, &mechList);
568                 asn1_end_tag(&data);
569                 data_blob_free(&mechList);
570                 DEBUG(5,("spnego_parse_auth_response received mechListMIC, "
571                     "ignoring.\n"));
572         }
573
574         asn1_end_tag(&data);
575         asn1_end_tag(&data);
576
577         if (data.has_error) {
578                 DEBUG(3,("spnego_parse_auth_response failed at %d\n", (int)data.ofs));
579                 asn1_free(&data);
580                 data_blob_free(auth);
581                 return False;
582         }
583
584         asn1_free(&data);
585         return True;
586 }