r23969: Some helper routines to retrieve a PAC and PAC elements.
[ira/wip.git] / source3 / libads / authdata.c
1 /* 
2    Unix SMB/CIFS implementation.
3    kerberos authorization data (PAC) utility library
4    Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003   
5    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
6    Copyright (C) Andrew Tridgell 2001
7    Copyright (C) Luke Howard 2002-2003
8    Copyright (C) Stefan Metzmacher 2004-2005
9    Copyright (C) Guenther Deschner 2005
10    
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 3 of the License, or
14    (at your option) any later version.
15    
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20    
21    You should have received a copy of the GNU General Public License
22    along with this program.  If not, see <http://www.gnu.org/licenses/>.
23 */
24
25 #include "includes.h"
26
27 #ifdef HAVE_KRB5
28
29 static BOOL pac_io_logon_name(const char *desc, PAC_LOGON_NAME *logon_name,
30                               prs_struct *ps, int depth)
31 {
32         if (NULL == logon_name)
33                 return False;
34
35         prs_debug(ps, depth, desc, "pac_io_logon_name");
36         depth++;
37
38         if (!smb_io_time("logon_time", &logon_name->logon_time, ps, depth))
39                 return False;
40
41         if (!prs_uint16("len", ps, depth, &logon_name->len))
42                 return False;
43
44         /* The following string is always in little endian 16 bit values,
45            copy as 8 bits to avoid endian reversal on big-endian machines.
46            len is the length in bytes. */
47
48         if (UNMARSHALLING(ps) && logon_name->len) {
49                 logon_name->username = PRS_ALLOC_MEM(ps, uint8, logon_name->len);
50                 if (!logon_name->username) {
51                         DEBUG(3, ("No memory available\n"));
52                         return False;
53                 }
54         }
55
56         if (!prs_uint8s(True, "name", ps, depth, logon_name->username, logon_name->len))
57                 return False;
58
59         return True;
60 }
61
62 #if 0 /* Unused (handled now in net_io_user_info3()) - Guenther */
63 static BOOL pac_io_krb_sids(const char *desc, KRB_SID_AND_ATTRS *sid_and_attr,
64                             prs_struct *ps, int depth)
65 {
66         if (NULL == sid_and_attr)
67                 return False;
68
69         prs_debug(ps, depth, desc, "pac_io_krb_sids");
70         depth++;
71
72         if (UNMARSHALLING(ps)) {
73                 sid_and_attr->sid = PRS_ALLOC_MEM(ps, DOM_SID2, 1);
74                 if (!sid_and_attr->sid) {
75                         DEBUG(3, ("No memory available\n"));
76                         return False;
77                 }
78         }
79
80         if(!smb_io_dom_sid2("sid", sid_and_attr->sid, ps, depth))
81                 return False;
82
83         return True;
84 }
85
86
87 static BOOL pac_io_krb_attrs(const char *desc, KRB_SID_AND_ATTRS *sid_and_attr,
88                              prs_struct *ps, int depth)
89 {
90         if (NULL == sid_and_attr)
91                 return False;
92
93         prs_debug(ps, depth, desc, "pac_io_krb_attrs");
94         depth++;
95
96         if (!prs_uint32("sid_ptr", ps, depth, &sid_and_attr->sid_ptr))
97                 return False;
98         if (!prs_uint32("attrs", ps, depth, &sid_and_attr->attrs))
99                 return False;
100
101         return True;
102 }
103
104 static BOOL pac_io_krb_sid_and_attr_array(const char *desc, 
105                                           KRB_SID_AND_ATTR_ARRAY *array,
106                                           uint32 num,
107                                           prs_struct *ps, int depth)
108 {
109         int i;
110
111         if (NULL == array)
112                 return False;
113
114         prs_debug(ps, depth, desc, "pac_io_krb_sid_and_attr_array");
115         depth++;
116
117
118         if (!prs_uint32("count", ps, depth, &array->count))
119                 return False;
120
121         if (UNMARSHALLING(ps)) {
122                 if (num) {
123                         array->krb_sid_and_attrs = PRS_ALLOC_MEM(ps, KRB_SID_AND_ATTRS, num);
124                         if (!array->krb_sid_and_attrs) {
125                                 DEBUG(3, ("No memory available\n"));
126                                 return False;
127                         }
128                 } else {
129                         array->krb_sid_and_attrs = NULL;
130                 }
131         }
132
133         for (i=0; i<num; i++) {
134                 if (!pac_io_krb_attrs(desc, 
135                                       &array->krb_sid_and_attrs[i],
136                                       ps, depth))
137                         return False;
138
139         }
140         for (i=0; i<num; i++) {
141                 if (!pac_io_krb_sids(desc, 
142                                      &array->krb_sid_and_attrs[i],
143                                      ps, depth))
144                         return False;
145
146         }
147
148         return True;
149
150 }
151 #endif
152
153 static BOOL pac_io_group_membership(const char *desc, 
154                                     GROUP_MEMBERSHIP *membership,
155                                     prs_struct *ps, int depth)
156 {
157         if (NULL == membership)
158                 return False;
159
160         prs_debug(ps, depth, desc, "pac_io_group_membership");
161         depth++;
162
163         if (!prs_uint32("rid", ps, depth, &membership->rid))
164                 return False;
165         if (!prs_uint32("attrs", ps, depth, &membership->attrs))
166                 return False;
167
168         return True;
169 }
170
171
172 static BOOL pac_io_group_membership_array(const char *desc, 
173                                           GROUP_MEMBERSHIP_ARRAY *array,
174                                           uint32 num,
175                                           prs_struct *ps, int depth)
176 {
177         int i;
178
179         if (NULL == array)
180                 return False;
181
182         prs_debug(ps, depth, desc, "pac_io_group_membership_array");
183         depth++;
184
185
186         if (!prs_uint32("count", ps, depth, &array->count))
187                 return False;
188
189         if (UNMARSHALLING(ps)) {
190                 if (num) {
191                         array->group_membership = PRS_ALLOC_MEM(ps, GROUP_MEMBERSHIP, num);
192                         if (!array->group_membership) {
193                                 DEBUG(3, ("No memory available\n"));
194                                 return False;
195                         }
196                 } else {
197                         array->group_membership = NULL;
198                 }
199         }
200
201         for (i=0; i<num; i++) {
202                 if (!pac_io_group_membership(desc, 
203                                              &array->group_membership[i],
204                                              ps, depth))
205                         return False;
206
207         }
208
209         return True;
210
211 }
212
213 #if 0 /* Unused, replaced using an expanded net_io_user_info3() now - Guenther */
214 static BOOL pac_io_pac_logon_info(const char *desc, PAC_LOGON_INFO *info, 
215                                   prs_struct *ps, int depth)
216 {
217         uint32 garbage, i;
218
219         if (NULL == info)
220                 return False;
221
222         prs_debug(ps, depth, desc, "pac_io_pac_logon_info");
223         depth++;
224
225         if (!prs_align(ps))
226                 return False;
227         if (!prs_uint32("unknown", ps, depth, &garbage)) /* 00081001 */
228                 return False;
229         if (!prs_uint32("unknown", ps, depth, &garbage)) /* cccccccc */
230                 return False;
231         if (!prs_uint32("bufferlen", ps, depth, &garbage))
232                 return False;
233         if (!prs_uint32("bufferlenhi", ps, depth, &garbage)) /* 00000000 */
234                 return False;
235
236         if (!prs_uint32("pointer", ps, depth, &garbage))
237                 return False;
238
239         if (!prs_align(ps))
240                 return False;
241         if (!smb_io_time("logon_time", &info->logon_time, ps, depth))
242                 return False;
243         if (!smb_io_time("logoff_time", &info->logoff_time, ps, depth))
244                 return False;
245         if (!smb_io_time("kickoff_time", &info->kickoff_time, ps, depth))
246                 return False;
247         if (!smb_io_time("pass_last_set_time", &info->pass_last_set_time, 
248                          ps, depth))
249                 return False;
250         if (!smb_io_time("pass_can_change_time", &info->pass_can_change_time, 
251                          ps, depth))
252                 return False;
253         if (!smb_io_time("pass_must_change_time", &info->pass_must_change_time,
254                          ps, depth))
255                 return False;
256
257         if (!smb_io_unihdr("hdr_user_name", &info->hdr_user_name, ps, depth))
258                 return False;
259         if (!smb_io_unihdr("hdr_full_name", &info->hdr_full_name, ps, depth))
260                 return False;
261         if (!smb_io_unihdr("hdr_logon_script", &info->hdr_logon_script, 
262                            ps, depth))
263                 return False;
264         if (!smb_io_unihdr("hdr_profile_path", &info->hdr_profile_path, 
265                            ps, depth))
266                 return False;
267         if (!smb_io_unihdr("hdr_home_dir", &info->hdr_home_dir, ps, depth))
268                 return False;
269         if (!smb_io_unihdr("hdr_dir_drive", &info->hdr_dir_drive, ps, depth))
270                 return False;
271
272         if (!prs_uint16("logon_count", ps, depth, &info->logon_count))
273                 return False;
274         if (!prs_uint16("bad_password_count", ps, depth, &info->bad_password_count))
275                 return False;
276         if (!prs_uint32("user_rid", ps, depth, &info->user_rid))
277                 return False;
278         if (!prs_uint32("group_rid", ps, depth, &info->group_rid))
279                 return False;
280         if (!prs_uint32("group_count", ps, depth, &info->group_count))
281                 return False;
282         /* I haven't seen this contain anything yet, but when it does
283            we will have to make sure we decode the contents in the middle
284            all the unistr2s ... */
285         if (!prs_uint32("group_mem_ptr", ps, depth, 
286                         &info->group_membership_ptr))
287                 return False;
288         if (!prs_uint32("user_flags", ps, depth, &info->user_flags))
289                 return False;
290
291         if (!prs_uint8s(False, "session_key", ps, depth, info->session_key, 16)) 
292                 return False;
293         
294         if (!smb_io_unihdr("hdr_dom_controller", 
295                            &info->hdr_dom_controller, ps, depth))
296                 return False;
297         if (!smb_io_unihdr("hdr_dom_name", &info->hdr_dom_name, ps, depth))
298                 return False;
299
300         /* this should be followed, but just get ptr for now */
301         if (!prs_uint32("ptr_dom_sid", ps, depth, &info->ptr_dom_sid))
302                 return False;
303
304         if (!prs_uint8s(False, "lm_session_key", ps, depth, info->lm_session_key, 8)) 
305                 return False;
306
307         if (!prs_uint32("acct_flags", ps, depth, &info->acct_flags))
308                 return False;
309
310         for (i = 0; i < 7; i++)
311         {
312                 if (!prs_uint32("unkown", ps, depth, &info->unknown[i])) /* unknown */
313                         return False;
314         }
315
316         if (!prs_uint32("sid_count", ps, depth, &info->sid_count))
317                 return False;
318         if (!prs_uint32("ptr_extra_sids", ps, depth, &info->ptr_extra_sids))
319                 return False;
320         if (!prs_uint32("ptr_res_group_dom_sid", ps, depth, 
321                         &info->ptr_res_group_dom_sid))
322                 return False;
323         if (!prs_uint32("res_group_count", ps, depth, &info->res_group_count))
324                 return False;
325         if (!prs_uint32("ptr_res_groups", ps, depth, &info->ptr_res_groups))
326                 return False;
327
328         if(!smb_io_unistr2("uni_user_name", &info->uni_user_name, 
329                            info->hdr_user_name.buffer, ps, depth))
330                 return False;
331         if(!smb_io_unistr2("uni_full_name", &info->uni_full_name, 
332                            info->hdr_full_name.buffer, ps, depth))
333                 return False;
334         if(!smb_io_unistr2("uni_logon_script", &info->uni_logon_script, 
335                            info->hdr_logon_script.buffer, ps, depth))
336                 return False;
337         if(!smb_io_unistr2("uni_profile_path", &info->uni_profile_path,
338                            info->hdr_profile_path.buffer, ps, depth))
339                 return False;
340         if(!smb_io_unistr2("uni_home_dir", &info->uni_home_dir,
341                            info->hdr_home_dir.buffer, ps, depth))
342                 return False;
343         if(!smb_io_unistr2("uni_dir_drive", &info->uni_dir_drive,
344                            info->hdr_dir_drive.buffer, ps, depth))
345                 return False;
346
347         if (info->group_membership_ptr) {
348                 if (!pac_io_group_membership_array("group membership",
349                                                    &info->groups,
350                                                    info->group_count,
351                                                    ps, depth))
352                         return False;
353         }
354
355
356         if(!smb_io_unistr2("uni_dom_controller", &info->uni_dom_controller,
357                            info->hdr_dom_controller.buffer, ps, depth))
358                 return False;
359         if(!smb_io_unistr2("uni_dom_name", &info->uni_dom_name, 
360                            info->hdr_dom_name.buffer, ps, depth))
361                 return False;
362
363         if(info->ptr_dom_sid)
364                 if(!smb_io_dom_sid2("dom_sid", &info->dom_sid, ps, depth))
365                         return False;
366
367         
368         if (info->sid_count && info->ptr_extra_sids)
369                 if (!pac_io_krb_sid_and_attr_array("extra_sids", 
370                                                    &info->extra_sids,
371                                                    info->sid_count,
372                                                    ps, depth))
373                         return False;
374
375         if (info->ptr_res_group_dom_sid)
376                 if (!smb_io_dom_sid2("res_group_dom_sid", 
377                                      &info->res_group_dom_sid, ps, depth))
378                         return False;
379
380         if (info->ptr_res_groups) {
381
382                 if (!(info->user_flgs & LOGON_RESOURCE_GROUPS)) {
383                         DEBUG(0,("user_flgs attribute does not have LOGON_RESOURCE_GROUPS\n"));
384                         /* return False; */
385                 }
386
387                 if (!pac_io_group_membership_array("res group membership",
388                                                    &info->res_groups,
389                                                    info->res_group_count,
390                                                    ps, depth))
391                         return False;
392         }
393
394         return True;
395 }
396 #endif
397
398 static BOOL pac_io_pac_logon_info(const char *desc, PAC_LOGON_INFO *info, 
399                                   prs_struct *ps, int depth)
400 {
401         uint32 garbage;
402         BOOL kerb_validation_info = True;
403
404         if (NULL == info)
405                 return False;
406
407         prs_debug(ps, depth, desc, "pac_io_pac_logon_info");
408         depth++;
409
410         if (!prs_align(ps))
411                 return False;
412         if (!prs_uint32("unknown", ps, depth, &garbage)) /* 00081001 */
413                 return False;
414         if (!prs_uint32("unknown", ps, depth, &garbage)) /* cccccccc */
415                 return False;
416         if (!prs_uint32("bufferlen", ps, depth, &garbage))
417                 return False;
418         if (!prs_uint32("bufferlenhi", ps, depth, &garbage)) /* 00000000 */
419                 return False;
420
421         if(!net_io_user_info3("", &info->info3, ps, depth, 3, kerb_validation_info))
422                 return False;
423
424         if (info->info3.ptr_res_group_dom_sid) {
425                 if (!smb_io_dom_sid2("res_group_dom_sid", 
426                                      &info->res_group_dom_sid, ps, depth))
427                         return False;
428         }
429
430         if (info->info3.ptr_res_groups) {
431
432                 if (!(info->info3.user_flgs & LOGON_RESOURCE_GROUPS)) {
433                         DEBUG(0,("user_flgs attribute does not have LOGON_RESOURCE_GROUPS\n"));
434                         /* return False; */
435                 }
436
437                 if (!pac_io_group_membership_array("res group membership",
438                                                    &info->res_groups,
439                                                    info->info3.res_group_count,
440                                                    ps, depth))
441                         return False;
442         }
443
444         return True;
445 }
446
447
448
449 static BOOL pac_io_pac_signature_data(const char *desc, 
450                                       PAC_SIGNATURE_DATA *data, uint32 length,
451                                       prs_struct *ps, int depth)
452 {
453         uint32 siglen = 0;
454
455         prs_debug(ps, depth, desc, "pac_io_pac_signature_data");
456         depth++;
457
458         if (data == NULL)
459                 return False;
460
461         if (!prs_align(ps))
462                 return False;
463         if (!prs_uint32("type", ps, depth, &data->type))
464                 return False;
465
466         if ( length > sizeof(uint32) )
467                 siglen = length - sizeof(uint32);       
468
469         if (UNMARSHALLING(ps) && length) {
470                 if (siglen) {
471                         data->signature.buffer = PRS_ALLOC_MEM(ps, uint8, siglen);
472                         if (!data->signature.buffer) {
473                                 DEBUG(3, ("No memory available\n"));
474                                 return False;
475                         }
476                 } else {
477                         data->signature.buffer = NULL;
478                 }
479         }
480
481         data->signature.buf_len = siglen;
482
483         if (!prs_uint8s(False, "signature", ps, depth, data->signature.buffer, data->signature.buf_len))
484                 return False;
485
486
487         return True;
488 }
489
490 static BOOL pac_io_pac_info_hdr_ctr(const char *desc, PAC_BUFFER *hdr,
491                                     prs_struct *ps, int depth)
492 {
493         if (NULL == hdr)
494                 return False;
495
496         prs_debug(ps, depth, desc, "pac_io_pac_info_hdr_ctr");
497         depth++;
498
499         if (!prs_align(ps))
500                 return False;
501
502         if (hdr->offset != prs_offset(ps)) {
503                 DEBUG(5,("offset in header(x%x) and data(x%x) do not match, correcting\n",
504                          hdr->offset, prs_offset(ps)));
505                 prs_set_offset(ps, hdr->offset);
506         }
507
508         if (UNMARSHALLING(ps) && hdr->size > 0) {
509                 hdr->ctr = PRS_ALLOC_MEM(ps, PAC_INFO_CTR, 1);
510                 if (!hdr->ctr) {
511                         DEBUG(3, ("No memory available\n"));
512                         return False;
513                 }
514         }
515
516         switch(hdr->type) {
517         case PAC_TYPE_LOGON_INFO:
518                 DEBUG(5, ("PAC_TYPE_LOGON_INFO\n"));
519                 if (UNMARSHALLING(ps))
520                         hdr->ctr->pac.logon_info = PRS_ALLOC_MEM(ps, PAC_LOGON_INFO, 1);
521                 if (!hdr->ctr->pac.logon_info) {
522                         DEBUG(3, ("No memory available\n"));
523                         return False;
524                 }
525                 if (!pac_io_pac_logon_info(desc, hdr->ctr->pac.logon_info,
526                                            ps, depth))
527                         return False;
528                 break;
529
530         case PAC_TYPE_SERVER_CHECKSUM:
531                 DEBUG(5, ("PAC_TYPE_SERVER_CHECKSUM\n"));
532                 if (UNMARSHALLING(ps))
533                         hdr->ctr->pac.srv_cksum = PRS_ALLOC_MEM(ps, PAC_SIGNATURE_DATA, 1);
534                 if (!hdr->ctr->pac.srv_cksum) {
535                         DEBUG(3, ("No memory available\n"));
536                         return False;
537                 }
538                 if (!pac_io_pac_signature_data(desc, hdr->ctr->pac.srv_cksum,
539                                                hdr->size, ps, depth))
540                         return False;
541                 break;
542
543         case PAC_TYPE_PRIVSVR_CHECKSUM:
544                 DEBUG(5, ("PAC_TYPE_PRIVSVR_CHECKSUM\n"));
545                 if (UNMARSHALLING(ps))
546                         hdr->ctr->pac.privsrv_cksum = PRS_ALLOC_MEM(ps, PAC_SIGNATURE_DATA, 1);
547                 if (!hdr->ctr->pac.privsrv_cksum) {
548                         DEBUG(3, ("No memory available\n"));
549                         return False;
550                 }
551                 if (!pac_io_pac_signature_data(desc, 
552                                                hdr->ctr->pac.privsrv_cksum,
553                                                hdr->size, ps, depth))
554                         return False;
555                 break;
556
557         case PAC_TYPE_LOGON_NAME:
558                 DEBUG(5, ("PAC_TYPE_LOGON_NAME\n"));
559                 if (UNMARSHALLING(ps))
560                         hdr->ctr->pac.logon_name = PRS_ALLOC_MEM(ps, PAC_LOGON_NAME, 1);
561                 if (!hdr->ctr->pac.logon_name) {
562                         DEBUG(3, ("No memory available\n"));
563                         return False;
564                 }
565                 if (!pac_io_logon_name(desc, hdr->ctr->pac.logon_name,
566                                             ps, depth))
567                         return False;
568                 break;
569
570         default:
571                 /* dont' know, so we need to skip it */
572                 DEBUG(3, ("unknown PAC type %d\n", hdr->type));
573                 prs_set_offset(ps, prs_offset(ps) + hdr->size);
574         }
575
576 #if 0
577         /* obscure pad */
578         if (!prs_uint32("pad", ps, depth, &hdr->pad))
579                 return False;
580 #endif
581         return True;
582 }
583
584 static BOOL pac_io_pac_info_hdr(const char *desc, PAC_BUFFER *hdr, 
585                                 prs_struct *ps, int depth)
586 {
587         if (NULL == hdr)
588                 return False;
589
590         prs_debug(ps, depth, desc, "pac_io_pac_info_hdr");
591         depth++;
592
593         if (!prs_align(ps))
594                 return False;
595         if (!prs_uint32("type", ps, depth, &hdr->type))
596                 return False;
597         if (!prs_uint32("size", ps, depth, &hdr->size))
598                 return False;
599         if (!prs_uint32("offset", ps, depth, &hdr->offset))
600                 return False;
601         if (!prs_uint32("offsethi", ps, depth, &hdr->offsethi))
602                 return False;
603
604         return True;
605 }
606
607 static BOOL pac_io_pac_data(const char *desc, PAC_DATA *data, 
608                             prs_struct *ps, int depth)
609 {
610         int i;
611
612         if (NULL == data)
613                 return False;
614
615         prs_debug(ps, depth, desc, "pac_io_pac_data");
616         depth++;
617
618         if (!prs_align(ps))
619                 return False;
620         if (!prs_uint32("num_buffers", ps, depth, &data->num_buffers))
621                 return False;
622         if (!prs_uint32("version", ps, depth, &data->version))
623                 return False;
624
625         if (UNMARSHALLING(ps) && data->num_buffers > 0) {
626                 if ((data->pac_buffer = PRS_ALLOC_MEM(ps, PAC_BUFFER, data->num_buffers)) == NULL) {
627                         return False;
628                 }
629         }
630
631         for (i=0; i<data->num_buffers; i++) {
632                 if (!pac_io_pac_info_hdr(desc, &data->pac_buffer[i], ps, 
633                                          depth))
634                         return False;
635         }
636
637         for (i=0; i<data->num_buffers; i++) {
638                 if (!pac_io_pac_info_hdr_ctr(desc, &data->pac_buffer[i],
639                                              ps, depth))
640                         return False;
641         }
642
643         return True;
644 }
645
646 static NTSTATUS check_pac_checksum(TALLOC_CTX *mem_ctx, 
647                                    DATA_BLOB pac_data,
648                                    PAC_SIGNATURE_DATA *sig,
649                                    krb5_context context,
650                                    krb5_keyblock *keyblock)
651 {
652         krb5_error_code ret;
653         krb5_checksum cksum;
654         krb5_keyusage usage = 0;
655
656         smb_krb5_checksum_from_pac_sig(&cksum, sig);
657
658 #ifdef HAVE_KRB5_KU_OTHER_CKSUM /* Heimdal */
659         usage = KRB5_KU_OTHER_CKSUM;
660 #elif defined(HAVE_KRB5_KEYUSAGE_APP_DATA_CKSUM) /* MIT */
661         usage = KRB5_KEYUSAGE_APP_DATA_CKSUM;
662 #else
663 #error UNKNOWN_KRB5_KEYUSAGE
664 #endif
665
666         ret = smb_krb5_verify_checksum(context, 
667                                        keyblock, 
668                                        usage, 
669                                        &cksum,
670                                        pac_data.data, 
671                                        pac_data.length);
672
673         if (ret) {
674                 DEBUG(2,("check_pac_checksum: PAC Verification failed: %s (%d)\n", 
675                         error_message(ret), ret));
676                 return NT_STATUS_ACCESS_DENIED;
677         }
678
679         return NT_STATUS_OK;
680 }
681
682 static NTSTATUS parse_pac_data(TALLOC_CTX *mem_ctx, DATA_BLOB *pac_data_blob, PAC_DATA *pac_data)
683 {
684         prs_struct ps;
685         PAC_DATA *my_pac;
686
687         if (!prs_init(&ps, pac_data_blob->length, mem_ctx, UNMARSHALL))
688                 return NT_STATUS_NO_MEMORY;
689
690         if (!prs_copy_data_in(&ps, (char *)pac_data_blob->data, pac_data_blob->length))
691                 return NT_STATUS_INVALID_PARAMETER;
692
693         prs_set_offset(&ps, 0);
694
695         my_pac = TALLOC_ZERO_P(mem_ctx, PAC_DATA);
696         if (!pac_io_pac_data("pac data", my_pac, &ps, 0))
697                 return NT_STATUS_INVALID_PARAMETER;
698
699         prs_mem_free(&ps);
700
701         *pac_data = *my_pac;
702
703         return NT_STATUS_OK;
704 }
705
706 /* just for debugging, will be removed later - Guenther */
707 char *pac_group_attr_string(uint32 attr)
708 {
709         fstring name = "";
710
711         if (!attr)
712                 return NULL;
713
714         if (attr & SE_GROUP_MANDATORY)                  fstrcat(name, "SE_GROUP_MANDATORY ");
715         if (attr & SE_GROUP_ENABLED_BY_DEFAULT)         fstrcat(name, "SE_GROUP_ENABLED_BY_DEFAULT ");
716         if (attr & SE_GROUP_ENABLED)                    fstrcat(name, "SE_GROUP_ENABLED ");
717         if (attr & SE_GROUP_OWNER)                      fstrcat(name, "SE_GROUP_OWNER ");
718         if (attr & SE_GROUP_USE_FOR_DENY_ONLY)          fstrcat(name, "SE_GROUP_USE_FOR_DENY_ONLY ");
719         if (attr & SE_GROUP_LOGON_ID)                   fstrcat(name, "SE_GROUP_LOGON_ID ");
720         if (attr & SE_GROUP_RESOURCE)                   fstrcat(name, "SE_GROUP_RESOURCE ");
721
722         return SMB_STRDUP(name);
723 }
724
725 /* just for debugging, will be removed later - Guenther */
726 static void dump_pac_logon_info(PAC_LOGON_INFO *logon_info) {
727
728         DOM_SID dom_sid, res_group_dom_sid;
729         int i;
730         char *attr_string;
731         uint32 user_flgs = logon_info->info3.user_flgs;
732
733         if (logon_info->info3.ptr_res_group_dom_sid) {
734                 sid_copy(&res_group_dom_sid, &logon_info->res_group_dom_sid.sid);
735         }
736         sid_copy(&dom_sid, &logon_info->info3.dom_sid.sid);
737         
738         DEBUG(10,("The PAC:\n"));
739         
740         DEBUGADD(10,("\tUser Flags: 0x%x (%d)\n", user_flgs, user_flgs));
741         if (user_flgs & LOGON_EXTRA_SIDS)
742                 DEBUGADD(10,("\tUser Flags: LOGON_EXTRA_SIDS 0x%x (%d)\n", LOGON_EXTRA_SIDS, LOGON_EXTRA_SIDS));
743         if (user_flgs & LOGON_RESOURCE_GROUPS)
744                 DEBUGADD(10,("\tUser Flags: LOGON_RESOURCE_GROUPS 0x%x (%d)\n", LOGON_RESOURCE_GROUPS, LOGON_RESOURCE_GROUPS));
745         DEBUGADD(10,("\tUser SID: %s-%d\n", sid_string_static(&dom_sid), logon_info->info3.user_rid));
746         DEBUGADD(10,("\tGroup SID: %s-%d\n", sid_string_static(&dom_sid), logon_info->info3.group_rid));
747
748         DEBUGADD(10,("\tGroup Membership (Global and Universal Groups of own domain):\n"));
749         for (i = 0; i < logon_info->info3.num_groups; i++) {
750                 attr_string = pac_group_attr_string(logon_info->info3.gids[i].attr);
751                 DEBUGADD(10,("\t\t%d: sid: %s-%d\n\t\t   attr: 0x%x == %s\n", 
752                         i, sid_string_static(&dom_sid), 
753                         logon_info->info3.gids[i].g_rid,
754                         logon_info->info3.gids[i].attr,
755                         attr_string));
756                 SAFE_FREE(attr_string);
757         }
758
759         DEBUGADD(10,("\tGroup Membership (Domain Local Groups and Groups from Trusted Domains):\n"));
760         for (i = 0; i < logon_info->info3.num_other_sids; i++) {
761                 attr_string = pac_group_attr_string(logon_info->info3.other_sids_attrib[i]);
762                 DEBUGADD(10,("\t\t%d: sid: %s\n\t\t   attr: 0x%x == %s\n", 
763                         i, sid_string_static(&logon_info->info3.other_sids[i].sid), 
764                         logon_info->info3.other_sids_attrib[i],
765                         attr_string));
766                 SAFE_FREE(attr_string);
767         }
768
769         DEBUGADD(10,("\tGroup Membership (Ressource Groups (SID History ?)):\n"));
770         for (i = 0; i < logon_info->info3.res_group_count; i++) {
771                 attr_string = pac_group_attr_string(logon_info->res_groups.group_membership[i].attrs);
772                 DEBUGADD(10,("\t\t%d: sid: %s-%d\n\t\t   attr: 0x%x == %s\n", 
773                         i, sid_string_static(&res_group_dom_sid),
774                         logon_info->res_groups.group_membership[i].rid,
775                         logon_info->res_groups.group_membership[i].attrs,
776                         attr_string));
777                 SAFE_FREE(attr_string);
778         }
779 }
780
781  NTSTATUS decode_pac_data(TALLOC_CTX *mem_ctx,
782                          DATA_BLOB *pac_data_blob,
783                          krb5_context context, 
784                          krb5_keyblock *service_keyblock,
785                          krb5_const_principal client_principal,
786                          time_t tgs_authtime,
787                          PAC_DATA **pac_data)
788                          
789 {
790         DATA_BLOB modified_pac_blob;
791         PAC_DATA *my_pac;
792         NTSTATUS nt_status;
793         krb5_error_code ret;
794         PAC_SIGNATURE_DATA *srv_sig = NULL;
795         PAC_SIGNATURE_DATA *kdc_sig = NULL;
796         PAC_LOGON_NAME *logon_name = NULL;
797         PAC_LOGON_INFO *logon_info = NULL;
798         krb5_principal client_principal_pac = NULL;
799         NTTIME tgs_authtime_nttime;
800         int i, srv_sig_pos = 0, kdc_sig_pos = 0;
801         fstring username;
802
803         *pac_data = NULL;
804
805         my_pac = talloc(mem_ctx, PAC_DATA);
806         if (!my_pac) {
807                 return NT_STATUS_NO_MEMORY;
808         }
809
810         nt_status = parse_pac_data(mem_ctx, pac_data_blob, my_pac);
811         if (!NT_STATUS_IS_OK(nt_status)) {
812                 DEBUG(0,("decode_pac_data: failed to parse PAC\n"));
813                 return nt_status;
814         }
815
816         modified_pac_blob = data_blob_talloc(mem_ctx, pac_data_blob->data, pac_data_blob->length);
817
818         if (my_pac->num_buffers < 4) {
819                 nt_status = NT_STATUS_INVALID_PARAMETER;
820                 goto out;
821         }
822
823         /* store signatures */
824         for (i=0; i < my_pac->num_buffers; i++) {
825         
826                 switch (my_pac->pac_buffer[i].type) {
827                 
828                         case PAC_TYPE_SERVER_CHECKSUM:
829                                 if (!my_pac->pac_buffer[i].ctr->pac.srv_cksum) {
830                                         break;
831                                 }
832                                 
833                                 srv_sig = my_pac->pac_buffer[i].ctr->pac.srv_cksum;
834                                 
835                                 /* get position of signature buffer */
836                                 srv_sig_pos = my_pac->pac_buffer[i].offset;
837                                 srv_sig_pos += sizeof(uint32);
838                                 
839                                 break;
840                                 
841                         case PAC_TYPE_PRIVSVR_CHECKSUM:
842                                 if (!my_pac->pac_buffer[i].ctr->pac.privsrv_cksum) {
843                                         break;
844                                 }
845
846                                 kdc_sig = my_pac->pac_buffer[i].ctr->pac.privsrv_cksum;
847                                 
848                                 /* get position of signature buffer */
849                                 kdc_sig_pos = my_pac->pac_buffer[i].offset;
850                                 kdc_sig_pos += sizeof(uint32);
851                                 
852                                 break;
853                                 
854                         case PAC_TYPE_LOGON_NAME:
855                                 if (!my_pac->pac_buffer[i].ctr->pac.logon_name) {
856                                         break;
857                                 }
858
859                                 logon_name = my_pac->pac_buffer[i].ctr->pac.logon_name;
860                                 break;
861
862                         case PAC_TYPE_LOGON_INFO:
863                                 if (!my_pac->pac_buffer[i].ctr->pac.logon_info) {
864                                         break;
865                                 }
866
867                                 logon_info = my_pac->pac_buffer[i].ctr->pac.logon_info;
868                                 break;
869                         }
870
871         }
872
873         if (!srv_sig || !kdc_sig || !logon_name || !logon_info) {
874                 nt_status = NT_STATUS_INVALID_PARAMETER;
875                 goto out;
876         }
877
878         /* zero PAC_SIGNATURE_DATA signature buffer */
879         memset(&modified_pac_blob.data[srv_sig_pos], '\0', srv_sig->signature.buf_len);
880         memset(&modified_pac_blob.data[kdc_sig_pos], '\0', kdc_sig->signature.buf_len);
881
882         /* check server signature */
883         nt_status = check_pac_checksum(mem_ctx, modified_pac_blob, srv_sig, context, service_keyblock);
884         if (!NT_STATUS_IS_OK(nt_status)) {
885                 DEBUG(0,("decode_pac_data: failed to verify PAC server signature\n"));
886                 goto out;
887         }
888
889         /* Convert to NT time, so as not to loose accuracy in comparison */
890         unix_to_nt_time(&tgs_authtime_nttime, tgs_authtime);
891
892         if (!nt_time_equals(&tgs_authtime_nttime, &logon_name->logon_time)) {
893         
894                 DEBUG(2,("decode_pac_data: Logon time mismatch between ticket and PAC!\n"));
895                 DEBUGADD(2, ("decode_pac_data: PAC: %s\n", 
896                         http_timestring(nt_time_to_unix(logon_name->logon_time))));
897                 DEBUGADD(2, ("decode_pac_data: Ticket: %s\n", 
898                         http_timestring(nt_time_to_unix(tgs_authtime_nttime))));
899                 
900                 nt_status = NT_STATUS_ACCESS_DENIED;
901                 goto out;
902         }
903
904         if (!logon_name->len) {
905                 DEBUG(2,("decode_pac_data: No Logon Name available\n"));
906                 nt_status = NT_STATUS_INVALID_PARAMETER;
907                 goto out;
908         }
909         rpcstr_pull(username, logon_name->username, sizeof(username), logon_name->len, 0);
910
911         ret = smb_krb5_parse_name_norealm(context, username, &client_principal_pac);
912         if (ret) {
913                 DEBUG(2,("decode_pac_data: Could not parse name from incoming PAC: [%s]: %s\n", 
914                         username, error_message(ret)));
915                 nt_status = NT_STATUS_INVALID_PARAMETER;
916                 goto out;
917         }
918
919         if (!smb_krb5_principal_compare_any_realm(context, client_principal, client_principal_pac)) {
920                 DEBUG(2,("decode_pac_data: Name in PAC [%s] does not match principal name in ticket\n", 
921                         username));
922                 nt_status = NT_STATUS_ACCESS_DENIED;
923                 goto out;
924         }
925
926         DEBUG(10,("Successfully validated Kerberos PAC\n"));
927
928         dump_pac_logon_info(logon_info);
929
930         *pac_data = my_pac;
931
932         nt_status = NT_STATUS_OK;
933
934 out:
935         if (client_principal_pac) {
936                 krb5_free_principal(context, client_principal_pac);
937         }
938
939         return nt_status;
940 }
941
942  PAC_LOGON_INFO *get_logon_info_from_pac(PAC_DATA *pac_data) 
943 {
944         PAC_LOGON_INFO *logon_info = NULL;
945         int i;
946         
947         for (i=0; i < pac_data->num_buffers; i++) {
948
949                 if (pac_data->pac_buffer[i].type != PAC_TYPE_LOGON_INFO)
950                         continue;
951
952                 logon_info = pac_data->pac_buffer[i].ctr->pac.logon_info;
953                 break;
954         }
955         return logon_info;
956 }
957
958 /****************************************************************
959 ****************************************************************/
960
961  NTSTATUS kerberos_return_pac(TALLOC_CTX *mem_ctx,
962                               const char *name,
963                               const char *pass,
964                               time_t time_offset,
965                               PAC_DATA **pac_ret)
966 {
967         krb5_error_code ret;
968         NTSTATUS status = NT_STATUS_INVALID_PARAMETER;
969         DATA_BLOB tkt, ap_rep, sesskey1, sesskey2;
970         PAC_DATA *pac_data = NULL;
971         char *client_princ_out = NULL;
972         const char *auth_princ = NULL;
973         const char *local_service = NULL;
974         const char *cc = "MEMORY:kerberos_return_pac";
975
976         ZERO_STRUCT(tkt);
977         ZERO_STRUCT(ap_rep);
978         ZERO_STRUCT(sesskey1);
979         ZERO_STRUCT(sesskey2);
980
981         if (!name || !pass) {
982                 return NT_STATUS_INVALID_PARAMETER;
983         }
984
985         if (!strchr_m(name, '@')) {
986                 auth_princ = talloc_asprintf(mem_ctx, "%s@%s", name,
987                         lp_realm());
988         } else {
989                 auth_princ = name;
990         }
991         NT_STATUS_HAVE_NO_MEMORY(auth_princ);
992
993         local_service = talloc_asprintf(mem_ctx, "%s$@%s",
994                                         global_myname(), lp_realm());
995         NT_STATUS_HAVE_NO_MEMORY(local_service);
996
997         ret = kerberos_kinit_password_ext(auth_princ,
998                                           pass,
999                                           time_offset,
1000                                           NULL,
1001                                           NULL,
1002                                           cc,
1003                                           True,
1004                                           True,
1005                                           0,
1006                                           &status);
1007         if (ret) {
1008                 /* status already set */
1009                 goto out;
1010         }
1011
1012         ret = cli_krb5_get_ticket(local_service,
1013                                   time_offset,
1014                                   &tkt,
1015                                   &sesskey1,
1016                                   0,
1017                                   cc,
1018                                   NULL);
1019         if (ret) {
1020                 status = krb5_to_nt_status(ret);
1021                 goto out;
1022         }
1023
1024         status = ads_verify_ticket(mem_ctx,
1025                                    lp_realm(),
1026                                    time_offset,
1027                                    &tkt,
1028                                    &client_princ_out,
1029                                    &pac_data,
1030                                    &ap_rep,
1031                                    &sesskey2,
1032                                    False);
1033         if (!NT_STATUS_IS_OK(status)) {
1034                 goto out;
1035         }
1036
1037         if (!pac_data) {
1038                 status = NT_STATUS_INVALID_PARAMETER;
1039                 goto out;
1040         }
1041
1042         *pac_ret = pac_data;
1043
1044 out:
1045         ads_kdestroy(cc);
1046
1047         data_blob_free(&tkt);
1048         data_blob_free(&ap_rep);
1049         data_blob_free(&sesskey1);
1050         data_blob_free(&sesskey2);
1051
1052         SAFE_FREE(client_princ_out);
1053
1054         return status;
1055 }
1056
1057 /****************************************************************
1058 ****************************************************************/
1059
1060 static NTSTATUS kerberos_return_pac_logon_info(TALLOC_CTX *mem_ctx,
1061                                                const char *name,
1062                                                const char *pass,
1063                                                time_t time_offset,
1064                                                PAC_LOGON_INFO **logon_info)
1065 {
1066         NTSTATUS status;
1067         PAC_DATA *pac_data = NULL;
1068         PAC_LOGON_INFO *info = NULL;
1069
1070         status = kerberos_return_pac(mem_ctx,
1071                                      name,
1072                                      pass,
1073                                      time_offset,
1074                                      &pac_data);
1075         if (!NT_STATUS_IS_OK(status)) {
1076                 return status;
1077         }
1078
1079         if (!pac_data) {
1080                 return NT_STATUS_INVALID_USER_BUFFER;
1081         }
1082
1083         info = get_logon_info_from_pac(pac_data);
1084         if (!info) {
1085                 return NT_STATUS_INVALID_USER_BUFFER;
1086         }
1087
1088         *logon_info = info;
1089
1090         return NT_STATUS_OK;
1091 }
1092
1093 /****************************************************************
1094 ****************************************************************/
1095
1096 NTSTATUS kerberos_return_info3_from_pac(TALLOC_CTX *mem_ctx,
1097                                         const char *name,
1098                                         const char *pass,
1099                                         time_t time_offset,
1100                                         NET_USER_INFO_3 **info3)
1101 {
1102         NTSTATUS status;
1103         PAC_LOGON_INFO *logon_info = NULL;
1104
1105         status = kerberos_return_pac_logon_info(mem_ctx,
1106                                                 name,
1107                                                 pass,
1108                                                 time_offset,
1109                                                 &logon_info);
1110         if (!NT_STATUS_IS_OK(status)) {
1111                 return status;
1112         }
1113
1114         *info3 = &logon_info->info3;
1115
1116         return NT_STATUS_OK;
1117 }
1118 #endif