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