r6149: Fixes bugs #2498 and 2484.
[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 <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 2 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, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24
25 /*
26   generate a negTokenInit packet given a GUID, a list of supported
27   OIDs (the mechanisms) and a principal name string 
28 */
29 DATA_BLOB spnego_gen_negTokenInit(uint8 guid[16], 
30                                   const char *OIDs[], 
31                                   const char *principal)
32 {
33         int i;
34         ASN1_DATA data;
35         DATA_BLOB ret;
36
37         memset(&data, 0, sizeof(data));
38
39         asn1_write(&data, guid, 16);
40         asn1_push_tag(&data,ASN1_APPLICATION(0));
41         asn1_write_OID(&data,OID_SPNEGO);
42         asn1_push_tag(&data,ASN1_CONTEXT(0));
43         asn1_push_tag(&data,ASN1_SEQUENCE(0));
44
45         asn1_push_tag(&data,ASN1_CONTEXT(0));
46         asn1_push_tag(&data,ASN1_SEQUENCE(0));
47         for (i=0; OIDs[i]; i++) {
48                 asn1_write_OID(&data,OIDs[i]);
49         }
50         asn1_pop_tag(&data);
51         asn1_pop_tag(&data);
52
53         asn1_push_tag(&data, ASN1_CONTEXT(3));
54         asn1_push_tag(&data, ASN1_SEQUENCE(0));
55         asn1_push_tag(&data, ASN1_CONTEXT(0));
56         asn1_write_GeneralString(&data,principal);
57         asn1_pop_tag(&data);
58         asn1_pop_tag(&data);
59         asn1_pop_tag(&data);
60
61         asn1_pop_tag(&data);
62         asn1_pop_tag(&data);
63
64         asn1_pop_tag(&data);
65
66         if (data.has_error) {
67                 DEBUG(1,("Failed to build negTokenInit at offset %d\n", (int)data.ofs));
68                 asn1_free(&data);
69         }
70
71         ret = data_blob(data.data, data.length);
72         asn1_free(&data);
73
74         return ret;
75 }
76
77 /*
78   Generate a negTokenInit as used by the client side ... It has a mechType
79   (OID), and a mechToken (a security blob) ... 
80
81   Really, we need to break out the NTLMSSP stuff as well, because it could be
82   raw in the packets!
83 */
84 DATA_BLOB gen_negTokenInit(const char *OID, DATA_BLOB blob)
85 {
86         ASN1_DATA data;
87         DATA_BLOB ret;
88
89         memset(&data, 0, sizeof(data));
90
91         asn1_push_tag(&data, ASN1_APPLICATION(0));
92         asn1_write_OID(&data,OID_SPNEGO);
93         asn1_push_tag(&data, ASN1_CONTEXT(0));
94         asn1_push_tag(&data, ASN1_SEQUENCE(0));
95
96         asn1_push_tag(&data, ASN1_CONTEXT(0));
97         asn1_push_tag(&data, ASN1_SEQUENCE(0));
98         asn1_write_OID(&data, OID);
99         asn1_pop_tag(&data);
100         asn1_pop_tag(&data);
101
102         asn1_push_tag(&data, ASN1_CONTEXT(2));
103         asn1_write_OctetString(&data,blob.data,blob.length);
104         asn1_pop_tag(&data);
105
106         asn1_pop_tag(&data);
107         asn1_pop_tag(&data);
108
109         asn1_pop_tag(&data);
110
111         if (data.has_error) {
112                 DEBUG(1,("Failed to build negTokenInit at offset %d\n", (int)data.ofs));
113                 asn1_free(&data);
114         }
115
116         ret = data_blob(data.data, data.length);
117         asn1_free(&data);
118
119         return ret;
120 }
121
122 /*
123   parse a negTokenInit packet giving a GUID, a list of supported
124   OIDs (the mechanisms) and a principal name string 
125 */
126 BOOL spnego_parse_negTokenInit(DATA_BLOB blob,
127                                char *OIDs[ASN1_MAX_OIDS], 
128                                char **principal)
129 {
130         int i;
131         BOOL ret;
132         ASN1_DATA data;
133
134         asn1_load(&data, blob);
135
136         asn1_start_tag(&data,ASN1_APPLICATION(0));
137         asn1_check_OID(&data,OID_SPNEGO);
138         asn1_start_tag(&data,ASN1_CONTEXT(0));
139         asn1_start_tag(&data,ASN1_SEQUENCE(0));
140
141         asn1_start_tag(&data,ASN1_CONTEXT(0));
142         asn1_start_tag(&data,ASN1_SEQUENCE(0));
143         for (i=0; asn1_tag_remaining(&data) > 0 && i < ASN1_MAX_OIDS; i++) {
144                 char *oid_str = NULL;
145                 asn1_read_OID(&data,&oid_str);
146                 OIDs[i] = oid_str;
147         }
148         OIDs[i] = NULL;
149         asn1_end_tag(&data);
150         asn1_end_tag(&data);
151
152         asn1_start_tag(&data, ASN1_CONTEXT(3));
153         asn1_start_tag(&data, ASN1_SEQUENCE(0));
154         asn1_start_tag(&data, ASN1_CONTEXT(0));
155         asn1_read_GeneralString(&data,principal);
156         asn1_end_tag(&data);
157         asn1_end_tag(&data);
158         asn1_end_tag(&data);
159
160         asn1_end_tag(&data);
161         asn1_end_tag(&data);
162
163         asn1_end_tag(&data);
164
165         ret = !data.has_error;
166         asn1_free(&data);
167         return ret;
168 }
169
170
171 /*
172   generate a negTokenTarg packet given a list of OIDs and a security blob
173 */
174 DATA_BLOB gen_negTokenTarg(const char *OIDs[], DATA_BLOB blob)
175 {
176         int i;
177         ASN1_DATA data;
178         DATA_BLOB ret;
179
180         memset(&data, 0, sizeof(data));
181
182         asn1_push_tag(&data, ASN1_APPLICATION(0));
183         asn1_write_OID(&data,OID_SPNEGO);
184         asn1_push_tag(&data, ASN1_CONTEXT(0));
185         asn1_push_tag(&data, ASN1_SEQUENCE(0));
186
187         asn1_push_tag(&data, ASN1_CONTEXT(0));
188         asn1_push_tag(&data, ASN1_SEQUENCE(0));
189         for (i=0; OIDs[i]; i++) {
190                 asn1_write_OID(&data,OIDs[i]);
191         }
192         asn1_pop_tag(&data);
193         asn1_pop_tag(&data);
194
195         asn1_push_tag(&data, ASN1_CONTEXT(2));
196         asn1_write_OctetString(&data,blob.data,blob.length);
197         asn1_pop_tag(&data);
198
199         asn1_pop_tag(&data);
200         asn1_pop_tag(&data);
201
202         asn1_pop_tag(&data);
203
204         if (data.has_error) {
205                 DEBUG(1,("Failed to build negTokenTarg at offset %d\n", (int)data.ofs));
206                 asn1_free(&data);
207         }
208
209         ret = data_blob(data.data, data.length);
210         asn1_free(&data);
211
212         return ret;
213 }
214
215
216 /*
217   parse a negTokenTarg packet giving a list of OIDs and a security blob
218 */
219 BOOL parse_negTokenTarg(DATA_BLOB blob, char *OIDs[ASN1_MAX_OIDS], DATA_BLOB *secblob)
220 {
221         int i;
222         ASN1_DATA data;
223
224         asn1_load(&data, blob);
225         asn1_start_tag(&data, ASN1_APPLICATION(0));
226         asn1_check_OID(&data,OID_SPNEGO);
227         asn1_start_tag(&data, ASN1_CONTEXT(0));
228         asn1_start_tag(&data, ASN1_SEQUENCE(0));
229
230         asn1_start_tag(&data, ASN1_CONTEXT(0));
231         asn1_start_tag(&data, ASN1_SEQUENCE(0));
232         for (i=0; asn1_tag_remaining(&data) > 0 && i < ASN1_MAX_OIDS; i++) {
233                 char *oid_str = NULL;
234                 asn1_read_OID(&data,&oid_str);
235                 OIDs[i] = oid_str;
236         }
237         OIDs[i] = NULL;
238         asn1_end_tag(&data);
239         asn1_end_tag(&data);
240
241         asn1_start_tag(&data, ASN1_CONTEXT(2));
242         asn1_read_OctetString(&data,secblob);
243         asn1_end_tag(&data);
244
245         asn1_end_tag(&data);
246         asn1_end_tag(&data);
247
248         asn1_end_tag(&data);
249
250         if (data.has_error) {
251                 DEBUG(1,("Failed to parse negTokenTarg at offset %d\n", (int)data.ofs));
252                 asn1_free(&data);
253                 return False;
254         }
255
256         asn1_free(&data);
257         return True;
258 }
259
260 /*
261   generate a krb5 GSS-API wrapper packet given a ticket
262 */
263 DATA_BLOB spnego_gen_krb5_wrap(DATA_BLOB ticket, const uint8 tok_id[2])
264 {
265         ASN1_DATA data;
266         DATA_BLOB ret;
267
268         memset(&data, 0, sizeof(data));
269
270         asn1_push_tag(&data, ASN1_APPLICATION(0));
271         asn1_write_OID(&data, OID_KERBEROS5);
272
273         asn1_write(&data, tok_id, 2);
274         asn1_write(&data, ticket.data, ticket.length);
275         asn1_pop_tag(&data);
276
277         if (data.has_error) {
278                 DEBUG(1,("Failed to build krb5 wrapper at offset %d\n", (int)data.ofs));
279                 asn1_free(&data);
280         }
281
282         ret = data_blob(data.data, data.length);
283         asn1_free(&data);
284
285         return ret;
286 }
287
288 /*
289   parse a krb5 GSS-API wrapper packet giving a ticket
290 */
291 BOOL spnego_parse_krb5_wrap(DATA_BLOB blob, DATA_BLOB *ticket, uint8 tok_id[2])
292 {
293         BOOL ret;
294         ASN1_DATA data;
295         int data_remaining;
296
297         asn1_load(&data, blob);
298         asn1_start_tag(&data, ASN1_APPLICATION(0));
299         asn1_check_OID(&data, OID_KERBEROS5);
300
301         data_remaining = asn1_tag_remaining(&data);
302
303         if (data_remaining < 3) {
304                 data.has_error = True;
305         } else {
306                 asn1_read(&data, tok_id, 2);
307                 data_remaining -= 2;
308                 *ticket = data_blob(NULL, data_remaining);
309                 asn1_read(&data, ticket->data, ticket->length);
310         }
311
312         asn1_end_tag(&data);
313
314         ret = !data.has_error;
315
316         asn1_free(&data);
317
318         return ret;
319 }
320
321
322 /* 
323    generate a SPNEGO negTokenTarg packet, ready for a EXTENDED_SECURITY
324    kerberos session setup 
325 */
326 int spnego_gen_negTokenTarg(const char *principal, int time_offset, 
327                             DATA_BLOB *targ, 
328                             DATA_BLOB *session_key_krb5)
329 {
330         int retval;
331         DATA_BLOB tkt, tkt_wrapped;
332         const char *krb_mechs[] = {OID_KERBEROS5_OLD, OID_NTLMSSP, NULL};
333
334         /* get a kerberos ticket for the service and extract the session key */
335         retval = cli_krb5_get_ticket(principal, time_offset, &tkt, session_key_krb5);
336
337         if (retval)
338                 return retval;
339
340         /* wrap that up in a nice GSS-API wrapping */
341         tkt_wrapped = spnego_gen_krb5_wrap(
342             tkt, CONST_ADD(const uint8 *, TOK_ID_KRB_AP_REQ));
343
344         /* and wrap that in a shiny SPNEGO wrapper */
345         *targ = gen_negTokenTarg(krb_mechs, tkt_wrapped);
346
347         data_blob_free(&tkt_wrapped);
348         data_blob_free(&tkt);
349
350         return retval;
351 }
352
353
354 /*
355   parse a spnego NTLMSSP challenge packet giving two security blobs
356 */
357 BOOL spnego_parse_challenge(const DATA_BLOB blob,
358                             DATA_BLOB *chal1, DATA_BLOB *chal2)
359 {
360         BOOL ret;
361         ASN1_DATA data;
362
363         ZERO_STRUCTP(chal1);
364         ZERO_STRUCTP(chal2);
365
366         asn1_load(&data, blob);
367         asn1_start_tag(&data,ASN1_CONTEXT(1));
368         asn1_start_tag(&data,ASN1_SEQUENCE(0));
369
370         asn1_start_tag(&data,ASN1_CONTEXT(0));
371         asn1_check_enumerated(&data,1);
372         asn1_end_tag(&data);
373
374         asn1_start_tag(&data,ASN1_CONTEXT(1));
375         asn1_check_OID(&data, OID_NTLMSSP);
376         asn1_end_tag(&data);
377
378         asn1_start_tag(&data,ASN1_CONTEXT(2));
379         asn1_read_OctetString(&data, chal1);
380         asn1_end_tag(&data);
381
382         /* the second challenge is optional (XP doesn't send it) */
383         if (asn1_tag_remaining(&data)) {
384                 asn1_start_tag(&data,ASN1_CONTEXT(3));
385                 asn1_read_OctetString(&data, chal2);
386                 asn1_end_tag(&data);
387         }
388
389         asn1_end_tag(&data);
390         asn1_end_tag(&data);
391
392         ret = !data.has_error;
393         asn1_free(&data);
394         return ret;
395 }
396
397
398 /*
399  generate a SPNEGO auth packet. This will contain the encrypted passwords
400 */
401 DATA_BLOB spnego_gen_auth(DATA_BLOB blob)
402 {
403         ASN1_DATA data;
404         DATA_BLOB ret;
405
406         memset(&data, 0, sizeof(data));
407
408         asn1_push_tag(&data, ASN1_CONTEXT(1));
409         asn1_push_tag(&data, ASN1_SEQUENCE(0));
410         asn1_push_tag(&data, ASN1_CONTEXT(2));
411         asn1_write_OctetString(&data,blob.data,blob.length);    
412         asn1_pop_tag(&data);
413         asn1_pop_tag(&data);
414         asn1_pop_tag(&data);
415
416         ret = data_blob(data.data, data.length);
417
418         asn1_free(&data);
419
420         return ret;
421 }
422
423 /*
424  parse a SPNEGO auth packet. This contains the encrypted passwords
425 */
426 BOOL spnego_parse_auth(DATA_BLOB blob, DATA_BLOB *auth)
427 {
428         ASN1_DATA data;
429
430         asn1_load(&data, blob);
431         asn1_start_tag(&data, ASN1_CONTEXT(1));
432         asn1_start_tag(&data, ASN1_SEQUENCE(0));
433         asn1_start_tag(&data, ASN1_CONTEXT(2));
434         asn1_read_OctetString(&data,auth);
435         asn1_end_tag(&data);
436         asn1_end_tag(&data);
437         asn1_end_tag(&data);
438
439         if (data.has_error) {
440                 DEBUG(3,("spnego_parse_auth failed at %d\n", (int)data.ofs));
441                 asn1_free(&data);
442                 return False;
443         }
444
445         asn1_free(&data);
446         return True;
447 }
448
449 /*
450   generate a minimal SPNEGO response packet.  Doesn't contain much.
451 */
452 DATA_BLOB spnego_gen_auth_response(DATA_BLOB *reply, NTSTATUS nt_status,
453                                    const char *mechOID)
454 {
455         ASN1_DATA data;
456         DATA_BLOB ret;
457         uint8 negResult;
458
459         if (NT_STATUS_IS_OK(nt_status)) {
460                 negResult = SPNEGO_NEG_RESULT_ACCEPT;
461         } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
462                 negResult = SPNEGO_NEG_RESULT_INCOMPLETE; 
463         } else {
464                 negResult = SPNEGO_NEG_RESULT_REJECT; 
465         }
466
467         ZERO_STRUCT(data);
468
469         asn1_push_tag(&data, ASN1_CONTEXT(1));
470         asn1_push_tag(&data, ASN1_SEQUENCE(0));
471         asn1_push_tag(&data, ASN1_CONTEXT(0));
472         asn1_write_enumerated(&data, negResult);
473         asn1_pop_tag(&data);
474
475         if (reply->data != NULL) {
476                 asn1_push_tag(&data,ASN1_CONTEXT(1));
477                 asn1_write_OID(&data, mechOID);
478                 asn1_pop_tag(&data);
479                 
480                 asn1_push_tag(&data,ASN1_CONTEXT(2));
481                 asn1_write_OctetString(&data, reply->data, reply->length);
482                 asn1_pop_tag(&data);
483         }
484
485         asn1_pop_tag(&data);
486         asn1_pop_tag(&data);
487
488         ret = data_blob(data.data, data.length);
489         asn1_free(&data);
490         return ret;
491 }
492
493 /*
494  parse a SPNEGO NTLMSSP auth packet. This contains the encrypted passwords
495 */
496 BOOL spnego_parse_auth_response(DATA_BLOB blob, NTSTATUS nt_status, 
497                                 DATA_BLOB *auth)
498 {
499         ASN1_DATA data;
500         uint8 negResult;
501
502         if (NT_STATUS_IS_OK(nt_status)) {
503                 negResult = SPNEGO_NEG_RESULT_ACCEPT;
504         } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
505                 negResult = SPNEGO_NEG_RESULT_INCOMPLETE;
506         } else {
507                 negResult = SPNEGO_NEG_RESULT_REJECT;
508         }
509
510         asn1_load(&data, blob);
511         asn1_start_tag(&data, ASN1_CONTEXT(1));
512         asn1_start_tag(&data, ASN1_SEQUENCE(0));
513         asn1_start_tag(&data, ASN1_CONTEXT(0));
514         asn1_check_enumerated(&data, negResult);
515         asn1_end_tag(&data);
516
517         if (negResult == SPNEGO_NEG_RESULT_INCOMPLETE) {
518                 asn1_start_tag(&data,ASN1_CONTEXT(1));
519                 asn1_check_OID(&data, OID_NTLMSSP);
520                 asn1_end_tag(&data);
521                 
522                 asn1_start_tag(&data,ASN1_CONTEXT(2));
523                 asn1_read_OctetString(&data, auth);
524                 asn1_end_tag(&data);
525         }
526
527         asn1_end_tag(&data);
528         asn1_end_tag(&data);
529
530         if (data.has_error) {
531                 DEBUG(3,("spnego_parse_auth_response failed at %d\n", (int)data.ofs));
532                 asn1_free(&data);
533                 data_blob_free(auth);
534                 return False;
535         }
536
537         asn1_free(&data);
538         return True;
539 }
540