r23779: Change from v2 or later to v3 or later.
[kai/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 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, 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(char 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-1; 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         *principal = NULL;
153         if (asn1_tag_remaining(&data) > 0) {
154                 asn1_start_tag(&data, ASN1_CONTEXT(3));
155                 asn1_start_tag(&data, ASN1_SEQUENCE(0));
156                 asn1_start_tag(&data, ASN1_CONTEXT(0));
157                 asn1_read_GeneralString(&data,principal);
158                 asn1_end_tag(&data);
159                 asn1_end_tag(&data);
160                 asn1_end_tag(&data);
161         }
162
163         asn1_end_tag(&data);
164         asn1_end_tag(&data);
165
166         asn1_end_tag(&data);
167
168         ret = !data.has_error;
169         if (data.has_error) {
170                 int j;
171                 SAFE_FREE(*principal);
172                 for(j = 0; j < i && j < ASN1_MAX_OIDS-1; j++) {
173                         SAFE_FREE(OIDs[j]);
174                 }
175         }
176
177         asn1_free(&data);
178         return ret;
179 }
180
181 /*
182   generate a negTokenTarg packet given a list of OIDs and a security blob
183 */
184 DATA_BLOB gen_negTokenTarg(const char *OIDs[], DATA_BLOB blob)
185 {
186         int i;
187         ASN1_DATA data;
188         DATA_BLOB ret;
189
190         memset(&data, 0, sizeof(data));
191
192         asn1_push_tag(&data, ASN1_APPLICATION(0));
193         asn1_write_OID(&data,OID_SPNEGO);
194         asn1_push_tag(&data, ASN1_CONTEXT(0));
195         asn1_push_tag(&data, ASN1_SEQUENCE(0));
196
197         asn1_push_tag(&data, ASN1_CONTEXT(0));
198         asn1_push_tag(&data, ASN1_SEQUENCE(0));
199         for (i=0; OIDs[i]; i++) {
200                 asn1_write_OID(&data,OIDs[i]);
201         }
202         asn1_pop_tag(&data);
203         asn1_pop_tag(&data);
204
205         asn1_push_tag(&data, ASN1_CONTEXT(2));
206         asn1_write_OctetString(&data,blob.data,blob.length);
207         asn1_pop_tag(&data);
208
209         asn1_pop_tag(&data);
210         asn1_pop_tag(&data);
211
212         asn1_pop_tag(&data);
213
214         if (data.has_error) {
215                 DEBUG(1,("Failed to build negTokenTarg at offset %d\n", (int)data.ofs));
216                 asn1_free(&data);
217         }
218
219         ret = data_blob(data.data, data.length);
220         asn1_free(&data);
221
222         return ret;
223 }
224
225 /*
226   parse a negTokenTarg packet giving a list of OIDs and a security blob
227 */
228 BOOL parse_negTokenTarg(DATA_BLOB blob, char *OIDs[ASN1_MAX_OIDS], DATA_BLOB *secblob)
229 {
230         int i;
231         ASN1_DATA data;
232
233         asn1_load(&data, blob);
234         asn1_start_tag(&data, ASN1_APPLICATION(0));
235         asn1_check_OID(&data,OID_SPNEGO);
236         asn1_start_tag(&data, ASN1_CONTEXT(0));
237         asn1_start_tag(&data, ASN1_SEQUENCE(0));
238
239         asn1_start_tag(&data, ASN1_CONTEXT(0));
240         asn1_start_tag(&data, ASN1_SEQUENCE(0));
241         for (i=0; asn1_tag_remaining(&data) > 0 && i < ASN1_MAX_OIDS-1; i++) {
242                 char *oid_str = NULL;
243                 asn1_read_OID(&data,&oid_str);
244                 OIDs[i] = oid_str;
245         }
246         OIDs[i] = NULL;
247         asn1_end_tag(&data);
248         asn1_end_tag(&data);
249
250         asn1_start_tag(&data, ASN1_CONTEXT(2));
251         asn1_read_OctetString(&data,secblob);
252         asn1_end_tag(&data);
253
254         asn1_end_tag(&data);
255         asn1_end_tag(&data);
256
257         asn1_end_tag(&data);
258
259         if (data.has_error) {
260                 int j;
261                 data_blob_free(secblob);
262                 for(j = 0; j < i && j < ASN1_MAX_OIDS-1; j++) {
263                         SAFE_FREE(OIDs[j]);
264                 }
265                 DEBUG(1,("Failed to parse negTokenTarg at offset %d\n", (int)data.ofs));
266                 asn1_free(&data);
267                 return False;
268         }
269
270         asn1_free(&data);
271         return True;
272 }
273
274 /*
275   generate a krb5 GSS-API wrapper packet given a ticket
276 */
277 DATA_BLOB spnego_gen_krb5_wrap(const DATA_BLOB ticket, const uint8 tok_id[2])
278 {
279         ASN1_DATA data;
280         DATA_BLOB ret;
281
282         memset(&data, 0, sizeof(data));
283
284         asn1_push_tag(&data, ASN1_APPLICATION(0));
285         asn1_write_OID(&data, OID_KERBEROS5);
286
287         asn1_write(&data, tok_id, 2);
288         asn1_write(&data, ticket.data, ticket.length);
289         asn1_pop_tag(&data);
290
291         if (data.has_error) {
292                 DEBUG(1,("Failed to build krb5 wrapper at offset %d\n", (int)data.ofs));
293                 asn1_free(&data);
294         }
295
296         ret = data_blob(data.data, data.length);
297         asn1_free(&data);
298
299         return ret;
300 }
301
302 /*
303   parse a krb5 GSS-API wrapper packet giving a ticket
304 */
305 BOOL spnego_parse_krb5_wrap(DATA_BLOB blob, DATA_BLOB *ticket, uint8 tok_id[2])
306 {
307         BOOL ret;
308         ASN1_DATA data;
309         int data_remaining;
310
311         asn1_load(&data, blob);
312         asn1_start_tag(&data, ASN1_APPLICATION(0));
313         asn1_check_OID(&data, OID_KERBEROS5);
314
315         data_remaining = asn1_tag_remaining(&data);
316
317         if (data_remaining < 3) {
318                 data.has_error = True;
319         } else {
320                 asn1_read(&data, tok_id, 2);
321                 data_remaining -= 2;
322                 *ticket = data_blob(NULL, data_remaining);
323                 asn1_read(&data, ticket->data, ticket->length);
324         }
325
326         asn1_end_tag(&data);
327
328         ret = !data.has_error;
329
330         if (data.has_error) {
331                 data_blob_free(ticket);
332         }
333
334         asn1_free(&data);
335
336         return ret;
337 }
338
339
340 /* 
341    generate a SPNEGO negTokenTarg packet, ready for a EXTENDED_SECURITY
342    kerberos session setup 
343 */
344 int spnego_gen_negTokenTarg(const char *principal, int time_offset, 
345                             DATA_BLOB *targ, 
346                             DATA_BLOB *session_key_krb5, uint32 extra_ap_opts,
347                             time_t *expire_time)
348 {
349         int retval;
350         DATA_BLOB tkt, tkt_wrapped;
351         const char *krb_mechs[] = {OID_KERBEROS5_OLD, OID_KERBEROS5, OID_NTLMSSP, NULL};
352
353         /* get a kerberos ticket for the service and extract the session key */
354         retval = cli_krb5_get_ticket(principal, time_offset,
355                                         &tkt, session_key_krb5, extra_ap_opts, NULL, 
356                                         expire_time);
357
358         if (retval)
359                 return retval;
360
361         /* wrap that up in a nice GSS-API wrapping */
362         tkt_wrapped = spnego_gen_krb5_wrap(tkt, TOK_ID_KRB_AP_REQ);
363
364         /* and wrap that in a shiny SPNEGO wrapper */
365         *targ = gen_negTokenTarg(krb_mechs, tkt_wrapped);
366
367         data_blob_free(&tkt_wrapped);
368         data_blob_free(&tkt);
369
370         return retval;
371 }
372
373
374 /*
375   parse a spnego NTLMSSP challenge packet giving two security blobs
376 */
377 BOOL spnego_parse_challenge(const DATA_BLOB blob,
378                             DATA_BLOB *chal1, DATA_BLOB *chal2)
379 {
380         BOOL ret;
381         ASN1_DATA data;
382
383         ZERO_STRUCTP(chal1);
384         ZERO_STRUCTP(chal2);
385
386         asn1_load(&data, blob);
387         asn1_start_tag(&data,ASN1_CONTEXT(1));
388         asn1_start_tag(&data,ASN1_SEQUENCE(0));
389
390         asn1_start_tag(&data,ASN1_CONTEXT(0));
391         asn1_check_enumerated(&data,1);
392         asn1_end_tag(&data);
393
394         asn1_start_tag(&data,ASN1_CONTEXT(1));
395         asn1_check_OID(&data, OID_NTLMSSP);
396         asn1_end_tag(&data);
397
398         asn1_start_tag(&data,ASN1_CONTEXT(2));
399         asn1_read_OctetString(&data, chal1);
400         asn1_end_tag(&data);
401
402         /* the second challenge is optional (XP doesn't send it) */
403         if (asn1_tag_remaining(&data)) {
404                 asn1_start_tag(&data,ASN1_CONTEXT(3));
405                 asn1_read_OctetString(&data, chal2);
406                 asn1_end_tag(&data);
407         }
408
409         asn1_end_tag(&data);
410         asn1_end_tag(&data);
411
412         ret = !data.has_error;
413
414         if (data.has_error) {
415                 data_blob_free(chal1);
416                 data_blob_free(chal2);
417         }
418
419         asn1_free(&data);
420         return ret;
421 }
422
423
424 /*
425  generate a SPNEGO auth packet. This will contain the encrypted passwords
426 */
427 DATA_BLOB spnego_gen_auth(DATA_BLOB blob)
428 {
429         ASN1_DATA data;
430         DATA_BLOB ret;
431
432         memset(&data, 0, sizeof(data));
433
434         asn1_push_tag(&data, ASN1_CONTEXT(1));
435         asn1_push_tag(&data, ASN1_SEQUENCE(0));
436         asn1_push_tag(&data, ASN1_CONTEXT(2));
437         asn1_write_OctetString(&data,blob.data,blob.length);    
438         asn1_pop_tag(&data);
439         asn1_pop_tag(&data);
440         asn1_pop_tag(&data);
441
442         ret = data_blob(data.data, data.length);
443
444         asn1_free(&data);
445
446         return ret;
447 }
448
449 /*
450  parse a SPNEGO auth packet. This contains the encrypted passwords
451 */
452 BOOL spnego_parse_auth(DATA_BLOB blob, DATA_BLOB *auth)
453 {
454         ASN1_DATA data;
455
456         asn1_load(&data, blob);
457         asn1_start_tag(&data, ASN1_CONTEXT(1));
458         asn1_start_tag(&data, ASN1_SEQUENCE(0));
459         asn1_start_tag(&data, ASN1_CONTEXT(2));
460         asn1_read_OctetString(&data,auth);
461         asn1_end_tag(&data);
462         asn1_end_tag(&data);
463         asn1_end_tag(&data);
464
465         if (data.has_error) {
466                 DEBUG(3,("spnego_parse_auth failed at %d\n", (int)data.ofs));
467                 data_blob_free(auth);
468                 asn1_free(&data);
469                 return False;
470         }
471
472         asn1_free(&data);
473         return True;
474 }
475
476 /*
477   generate a minimal SPNEGO response packet.  Doesn't contain much.
478 */
479 DATA_BLOB spnego_gen_auth_response(DATA_BLOB *reply, NTSTATUS nt_status,
480                                    const char *mechOID)
481 {
482         ASN1_DATA data;
483         DATA_BLOB ret;
484         uint8 negResult;
485
486         if (NT_STATUS_IS_OK(nt_status)) {
487                 negResult = SPNEGO_NEG_RESULT_ACCEPT;
488         } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
489                 negResult = SPNEGO_NEG_RESULT_INCOMPLETE; 
490         } else {
491                 negResult = SPNEGO_NEG_RESULT_REJECT; 
492         }
493
494         ZERO_STRUCT(data);
495
496         asn1_push_tag(&data, ASN1_CONTEXT(1));
497         asn1_push_tag(&data, ASN1_SEQUENCE(0));
498         asn1_push_tag(&data, ASN1_CONTEXT(0));
499         asn1_write_enumerated(&data, negResult);
500         asn1_pop_tag(&data);
501
502         if (reply->data != NULL) {
503                 asn1_push_tag(&data,ASN1_CONTEXT(1));
504                 asn1_write_OID(&data, mechOID);
505                 asn1_pop_tag(&data);
506                 
507                 asn1_push_tag(&data,ASN1_CONTEXT(2));
508                 asn1_write_OctetString(&data, reply->data, reply->length);
509                 asn1_pop_tag(&data);
510         }
511
512         asn1_pop_tag(&data);
513         asn1_pop_tag(&data);
514
515         ret = data_blob(data.data, data.length);
516         asn1_free(&data);
517         return ret;
518 }
519
520 /*
521  parse a SPNEGO auth packet. This contains the encrypted passwords
522 */
523 BOOL spnego_parse_auth_response(DATA_BLOB blob, NTSTATUS nt_status,
524                                 const char *mechOID,
525                                 DATA_BLOB *auth)
526 {
527         ASN1_DATA data;
528         uint8 negResult;
529
530         if (NT_STATUS_IS_OK(nt_status)) {
531                 negResult = SPNEGO_NEG_RESULT_ACCEPT;
532         } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
533                 negResult = SPNEGO_NEG_RESULT_INCOMPLETE;
534         } else {
535                 negResult = SPNEGO_NEG_RESULT_REJECT;
536         }
537
538         asn1_load(&data, blob);
539         asn1_start_tag(&data, ASN1_CONTEXT(1));
540         asn1_start_tag(&data, ASN1_SEQUENCE(0));
541         asn1_start_tag(&data, ASN1_CONTEXT(0));
542         asn1_check_enumerated(&data, negResult);
543         asn1_end_tag(&data);
544
545         *auth = data_blob_null;
546
547         if (asn1_tag_remaining(&data)) {
548                 asn1_start_tag(&data,ASN1_CONTEXT(1));
549                 asn1_check_OID(&data, mechOID);
550                 asn1_end_tag(&data);
551
552                 if (asn1_tag_remaining(&data)) {
553                         asn1_start_tag(&data,ASN1_CONTEXT(2));
554                         asn1_read_OctetString(&data, auth);
555                         asn1_end_tag(&data);
556                 }
557         } else if (negResult == SPNEGO_NEG_RESULT_INCOMPLETE) {
558                 data.has_error = 1;
559         }
560
561         asn1_end_tag(&data);
562         asn1_end_tag(&data);
563
564         if (data.has_error) {
565                 DEBUG(3,("spnego_parse_auth_response failed at %d\n", (int)data.ofs));
566                 asn1_free(&data);
567                 data_blob_free(auth);
568                 return False;
569         }
570
571         asn1_free(&data);
572         return True;
573 }