Replace cli_rpc_pipe_close by a talloc destructor on rpc_pipe_struct
[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, 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         /* Skip any optional req_flags that are sent per RFC 4178 */
250         if (asn1_check_tag(&data, ASN1_CONTEXT(1))) {
251                 uint8 flags;
252
253                 asn1_start_tag(&data, ASN1_CONTEXT(1));
254                 asn1_start_tag(&data, ASN1_BITFIELD);
255                 while (asn1_tag_remaining(&data) > 0)
256                         asn1_read_uint8(&data, &flags);
257                 asn1_end_tag(&data);
258                 asn1_end_tag(&data);
259         }
260
261         asn1_start_tag(&data, ASN1_CONTEXT(2));
262         asn1_read_OctetString(&data,secblob);
263         asn1_end_tag(&data);
264
265         asn1_end_tag(&data);
266         asn1_end_tag(&data);
267
268         asn1_end_tag(&data);
269
270         if (data.has_error) {
271                 int j;
272                 data_blob_free(secblob);
273                 for(j = 0; j < i && j < ASN1_MAX_OIDS-1; j++) {
274                         SAFE_FREE(OIDs[j]);
275                 }
276                 DEBUG(1,("Failed to parse negTokenTarg at offset %d\n", (int)data.ofs));
277                 asn1_free(&data);
278                 return False;
279         }
280
281         asn1_free(&data);
282         return True;
283 }
284
285 /*
286   generate a krb5 GSS-API wrapper packet given a ticket
287 */
288 DATA_BLOB spnego_gen_krb5_wrap(const DATA_BLOB ticket, const uint8 tok_id[2])
289 {
290         ASN1_DATA data;
291         DATA_BLOB ret;
292
293         memset(&data, 0, sizeof(data));
294
295         asn1_push_tag(&data, ASN1_APPLICATION(0));
296         asn1_write_OID(&data, OID_KERBEROS5);
297
298         asn1_write(&data, tok_id, 2);
299         asn1_write(&data, ticket.data, ticket.length);
300         asn1_pop_tag(&data);
301
302         if (data.has_error) {
303                 DEBUG(1,("Failed to build krb5 wrapper at offset %d\n", (int)data.ofs));
304                 asn1_free(&data);
305         }
306
307         ret = data_blob(data.data, data.length);
308         asn1_free(&data);
309
310         return ret;
311 }
312
313 /*
314   parse a krb5 GSS-API wrapper packet giving a ticket
315 */
316 bool spnego_parse_krb5_wrap(DATA_BLOB blob, DATA_BLOB *ticket, uint8 tok_id[2])
317 {
318         bool ret;
319         ASN1_DATA data;
320         int data_remaining;
321
322         asn1_load(&data, blob);
323         asn1_start_tag(&data, ASN1_APPLICATION(0));
324         asn1_check_OID(&data, OID_KERBEROS5);
325
326         data_remaining = asn1_tag_remaining(&data);
327
328         if (data_remaining < 3) {
329                 data.has_error = True;
330         } else {
331                 asn1_read(&data, tok_id, 2);
332                 data_remaining -= 2;
333                 *ticket = data_blob(NULL, data_remaining);
334                 asn1_read(&data, ticket->data, ticket->length);
335         }
336
337         asn1_end_tag(&data);
338
339         ret = !data.has_error;
340
341         if (data.has_error) {
342                 data_blob_free(ticket);
343         }
344
345         asn1_free(&data);
346
347         return ret;
348 }
349
350
351 /* 
352    generate a SPNEGO negTokenTarg packet, ready for a EXTENDED_SECURITY
353    kerberos session setup 
354 */
355 int spnego_gen_negTokenTarg(const char *principal, int time_offset, 
356                             DATA_BLOB *targ, 
357                             DATA_BLOB *session_key_krb5, uint32 extra_ap_opts,
358                             time_t *expire_time)
359 {
360         int retval;
361         DATA_BLOB tkt, tkt_wrapped;
362         const char *krb_mechs[] = {OID_KERBEROS5_OLD, OID_KERBEROS5, OID_NTLMSSP, NULL};
363
364         /* get a kerberos ticket for the service and extract the session key */
365         retval = cli_krb5_get_ticket(principal, time_offset,
366                                         &tkt, session_key_krb5, extra_ap_opts, NULL, 
367                                         expire_time);
368
369         if (retval)
370                 return retval;
371
372         /* wrap that up in a nice GSS-API wrapping */
373         tkt_wrapped = spnego_gen_krb5_wrap(tkt, TOK_ID_KRB_AP_REQ);
374
375         /* and wrap that in a shiny SPNEGO wrapper */
376         *targ = gen_negTokenTarg(krb_mechs, tkt_wrapped);
377
378         data_blob_free(&tkt_wrapped);
379         data_blob_free(&tkt);
380
381         return retval;
382 }
383
384
385 /*
386   parse a spnego NTLMSSP challenge packet giving two security blobs
387 */
388 bool spnego_parse_challenge(const DATA_BLOB blob,
389                             DATA_BLOB *chal1, DATA_BLOB *chal2)
390 {
391         bool ret;
392         ASN1_DATA data;
393
394         ZERO_STRUCTP(chal1);
395         ZERO_STRUCTP(chal2);
396
397         asn1_load(&data, blob);
398         asn1_start_tag(&data,ASN1_CONTEXT(1));
399         asn1_start_tag(&data,ASN1_SEQUENCE(0));
400
401         asn1_start_tag(&data,ASN1_CONTEXT(0));
402         asn1_check_enumerated(&data,1);
403         asn1_end_tag(&data);
404
405         asn1_start_tag(&data,ASN1_CONTEXT(1));
406         asn1_check_OID(&data, OID_NTLMSSP);
407         asn1_end_tag(&data);
408
409         asn1_start_tag(&data,ASN1_CONTEXT(2));
410         asn1_read_OctetString(&data, chal1);
411         asn1_end_tag(&data);
412
413         /* the second challenge is optional (XP doesn't send it) */
414         if (asn1_tag_remaining(&data)) {
415                 asn1_start_tag(&data,ASN1_CONTEXT(3));
416                 asn1_read_OctetString(&data, chal2);
417                 asn1_end_tag(&data);
418         }
419
420         asn1_end_tag(&data);
421         asn1_end_tag(&data);
422
423         ret = !data.has_error;
424
425         if (data.has_error) {
426                 data_blob_free(chal1);
427                 data_blob_free(chal2);
428         }
429
430         asn1_free(&data);
431         return ret;
432 }
433
434
435 /*
436  generate a SPNEGO auth packet. This will contain the encrypted passwords
437 */
438 DATA_BLOB spnego_gen_auth(DATA_BLOB blob)
439 {
440         ASN1_DATA data;
441         DATA_BLOB ret;
442
443         memset(&data, 0, sizeof(data));
444
445         asn1_push_tag(&data, ASN1_CONTEXT(1));
446         asn1_push_tag(&data, ASN1_SEQUENCE(0));
447         asn1_push_tag(&data, ASN1_CONTEXT(2));
448         asn1_write_OctetString(&data,blob.data,blob.length);    
449         asn1_pop_tag(&data);
450         asn1_pop_tag(&data);
451         asn1_pop_tag(&data);
452
453         ret = data_blob(data.data, data.length);
454
455         asn1_free(&data);
456
457         return ret;
458 }
459
460 /*
461  parse a SPNEGO auth packet. This contains the encrypted passwords
462 */
463 bool spnego_parse_auth(DATA_BLOB blob, DATA_BLOB *auth)
464 {
465         ASN1_DATA data;
466
467         asn1_load(&data, blob);
468         asn1_start_tag(&data, ASN1_CONTEXT(1));
469         asn1_start_tag(&data, ASN1_SEQUENCE(0));
470         asn1_start_tag(&data, ASN1_CONTEXT(2));
471         asn1_read_OctetString(&data,auth);
472         asn1_end_tag(&data);
473         asn1_end_tag(&data);
474         asn1_end_tag(&data);
475
476         if (data.has_error) {
477                 DEBUG(3,("spnego_parse_auth failed at %d\n", (int)data.ofs));
478                 data_blob_free(auth);
479                 asn1_free(&data);
480                 return False;
481         }
482
483         asn1_free(&data);
484         return True;
485 }
486
487 /*
488   generate a minimal SPNEGO response packet.  Doesn't contain much.
489 */
490 DATA_BLOB spnego_gen_auth_response(DATA_BLOB *reply, NTSTATUS nt_status,
491                                    const char *mechOID)
492 {
493         ASN1_DATA data;
494         DATA_BLOB ret;
495         uint8 negResult;
496
497         if (NT_STATUS_IS_OK(nt_status)) {
498                 negResult = SPNEGO_NEG_RESULT_ACCEPT;
499         } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
500                 negResult = SPNEGO_NEG_RESULT_INCOMPLETE; 
501         } else {
502                 negResult = SPNEGO_NEG_RESULT_REJECT; 
503         }
504
505         ZERO_STRUCT(data);
506
507         asn1_push_tag(&data, ASN1_CONTEXT(1));
508         asn1_push_tag(&data, ASN1_SEQUENCE(0));
509         asn1_push_tag(&data, ASN1_CONTEXT(0));
510         asn1_write_enumerated(&data, negResult);
511         asn1_pop_tag(&data);
512
513         if (mechOID) {
514                 asn1_push_tag(&data,ASN1_CONTEXT(1));
515                 asn1_write_OID(&data, mechOID);
516                 asn1_pop_tag(&data);
517         }
518
519         if (reply && reply->data != NULL) {
520                 asn1_push_tag(&data,ASN1_CONTEXT(2));
521                 asn1_write_OctetString(&data, reply->data, reply->length);
522                 asn1_pop_tag(&data);
523         }
524
525         asn1_pop_tag(&data);
526         asn1_pop_tag(&data);
527
528         ret = data_blob(data.data, data.length);
529         asn1_free(&data);
530         return ret;
531 }
532
533 /*
534  parse a SPNEGO auth packet. This contains the encrypted passwords
535 */
536 bool spnego_parse_auth_response(DATA_BLOB blob, NTSTATUS nt_status,
537                                 const char *mechOID,
538                                 DATA_BLOB *auth)
539 {
540         ASN1_DATA data;
541         uint8 negResult;
542
543         if (NT_STATUS_IS_OK(nt_status)) {
544                 negResult = SPNEGO_NEG_RESULT_ACCEPT;
545         } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
546                 negResult = SPNEGO_NEG_RESULT_INCOMPLETE;
547         } else {
548                 negResult = SPNEGO_NEG_RESULT_REJECT;
549         }
550
551         asn1_load(&data, blob);
552         asn1_start_tag(&data, ASN1_CONTEXT(1));
553         asn1_start_tag(&data, ASN1_SEQUENCE(0));
554         asn1_start_tag(&data, ASN1_CONTEXT(0));
555         asn1_check_enumerated(&data, negResult);
556         asn1_end_tag(&data);
557
558         *auth = data_blob_null;
559
560         if (asn1_tag_remaining(&data)) {
561                 asn1_start_tag(&data,ASN1_CONTEXT(1));
562                 asn1_check_OID(&data, mechOID);
563                 asn1_end_tag(&data);
564
565                 if (asn1_tag_remaining(&data)) {
566                         asn1_start_tag(&data,ASN1_CONTEXT(2));
567                         asn1_read_OctetString(&data, auth);
568                         asn1_end_tag(&data);
569                 }
570         } else if (negResult == SPNEGO_NEG_RESULT_INCOMPLETE) {
571                 data.has_error = 1;
572         }
573
574         /* Binding against Win2K DC returns a duplicate of the responseToken in
575          * the optional mechListMIC field. This is a bug in Win2K. We ignore
576          * this field if it exists. Win2K8 may return a proper mechListMIC at
577          * which point we need to implement the integrity checking. */
578         if (asn1_tag_remaining(&data)) {
579                 DATA_BLOB mechList = data_blob_null;
580                 asn1_start_tag(&data, ASN1_CONTEXT(3));
581                 asn1_read_OctetString(&data, &mechList);
582                 asn1_end_tag(&data);
583                 data_blob_free(&mechList);
584                 DEBUG(5,("spnego_parse_auth_response received mechListMIC, "
585                     "ignoring.\n"));
586         }
587
588         asn1_end_tag(&data);
589         asn1_end_tag(&data);
590
591         if (data.has_error) {
592                 DEBUG(3,("spnego_parse_auth_response failed at %d\n", (int)data.ofs));
593                 asn1_free(&data);
594                 data_blob_free(auth);
595                 return False;
596         }
597
598         asn1_free(&data);
599         return True;
600 }