Heimdal provides Kerberos PAC parsing routines. Use them.
[kai/samba.git] / source4 / auth / kerberos / kerberos_pac.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Create and parse the krb5 PAC
5    
6    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005,2008
7    Copyright (C) Andrew Tridgell 2001
8    Copyright (C) Luke Howard 2002-2003
9    Copyright (C) Stefan Metzmacher 2004-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    
22    You should have received a copy of the GNU General Public License
23    along with this program.  If not, see <http://www.gnu.org/licenses/>.
24 */
25
26 #include "includes.h"
27 #include "system/kerberos.h"
28 #include "auth/auth.h"
29 #include "auth/kerberos/kerberos.h"
30 #include "librpc/gen_ndr/ndr_krb5pac.h"
31 #include "lib/ldb/include/ldb.h"
32 #include "auth/auth_sam_reply.h"
33 #include "param/param.h"
34
35 static krb5_error_code check_pac_checksum(TALLOC_CTX *mem_ctx, 
36                                           DATA_BLOB pac_data,
37                                           struct PAC_SIGNATURE_DATA *sig,
38                                           krb5_context context,
39                                           const krb5_keyblock *keyblock)
40 {
41         krb5_error_code ret;
42         krb5_crypto crypto;
43         Checksum cksum;
44
45         cksum.cksumtype         = (CKSUMTYPE)sig->type;
46         cksum.checksum.length   = sig->signature.length;
47         cksum.checksum.data     = sig->signature.data;
48
49         ret = krb5_crypto_init(context,
50                                keyblock,
51                                0,
52                                &crypto);
53         if (ret) {
54                 DEBUG(0,("krb5_crypto_init() failed: %s\n", 
55                           smb_get_krb5_error_message(context, ret, mem_ctx)));
56                 return ret;
57         }
58         ret = krb5_verify_checksum(context,
59                                    crypto,
60                                    KRB5_KU_OTHER_CKSUM,
61                                    pac_data.data,
62                                    pac_data.length,
63                                    &cksum);
64         krb5_crypto_destroy(context, crypto);
65
66         return ret;
67 }
68
69  NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx,
70                               struct smb_iconv_convenience *iconv_convenience,
71                               struct PAC_DATA **pac_data_out,
72                               DATA_BLOB blob,
73                               krb5_context context,
74                               const krb5_keyblock *krbtgt_keyblock,
75                               const krb5_keyblock *service_keyblock,
76                               krb5_const_principal client_principal,
77                               time_t tgs_authtime,
78                               krb5_error_code *k5ret)
79 {
80         krb5_error_code ret;
81         NTSTATUS status;
82         enum ndr_err_code ndr_err;
83         struct PAC_SIGNATURE_DATA *srv_sig_ptr = NULL;
84         struct PAC_SIGNATURE_DATA *kdc_sig_ptr = NULL;
85         struct PAC_SIGNATURE_DATA *srv_sig_wipe = NULL;
86         struct PAC_SIGNATURE_DATA *kdc_sig_wipe = NULL;
87         struct PAC_LOGON_INFO *logon_info = NULL;
88         struct PAC_LOGON_NAME *logon_name = NULL;
89         struct PAC_DATA *pac_data;
90         struct PAC_DATA_RAW *pac_data_raw;
91
92         DATA_BLOB *srv_sig_blob = NULL;
93         DATA_BLOB *kdc_sig_blob = NULL;
94
95         DATA_BLOB modified_pac_blob;
96         NTTIME tgs_authtime_nttime;
97         krb5_principal client_principal_pac;
98         int i;
99
100         krb5_clear_error_string(context);
101
102         if (k5ret) {
103                 *k5ret = KRB5_PARSE_MALFORMED;
104         }
105
106         pac_data = talloc(mem_ctx, struct PAC_DATA);
107         pac_data_raw = talloc(mem_ctx, struct PAC_DATA_RAW);
108         kdc_sig_wipe = talloc(mem_ctx, struct PAC_SIGNATURE_DATA);
109         srv_sig_wipe = talloc(mem_ctx, struct PAC_SIGNATURE_DATA);
110         if (!pac_data_raw || !pac_data || !kdc_sig_wipe || !srv_sig_wipe) {
111                 if (k5ret) {
112                         *k5ret = ENOMEM;
113                 }
114                 return NT_STATUS_NO_MEMORY;
115         }
116
117         ndr_err = ndr_pull_struct_blob(&blob, pac_data, 
118                         iconv_convenience, pac_data,
119                        (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA);
120         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
121                 status = ndr_map_error2ntstatus(ndr_err);
122                 DEBUG(0,("can't parse the PAC: %s\n",
123                         nt_errstr(status)));
124                 return status;
125         }
126
127         if (pac_data->num_buffers < 4) {
128                 /* we need logon_ingo, service_key and kdc_key */
129                 DEBUG(0,("less than 4 PAC buffers\n"));
130                 return NT_STATUS_INVALID_PARAMETER;
131         }
132
133         ndr_err = ndr_pull_struct_blob(&blob, pac_data_raw, 
134                                        iconv_convenience, pac_data_raw,
135                                        (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA_RAW);
136         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
137                 status = ndr_map_error2ntstatus(ndr_err);
138                 DEBUG(0,("can't parse the PAC: %s\n",
139                         nt_errstr(status)));
140                 return status;
141         }
142
143         if (pac_data_raw->num_buffers < 4) {
144                 /* we need logon_ingo, service_key and kdc_key */
145                 DEBUG(0,("less than 4 PAC buffers\n"));
146                 return NT_STATUS_INVALID_PARAMETER;
147         }
148
149         if (pac_data->num_buffers != pac_data_raw->num_buffers) {
150                 /* we need logon_ingo, service_key and kdc_key */
151                 DEBUG(0,("misparse!  PAC_DATA has %d buffers while PAC_DATA_RAW has %d\n",
152                          pac_data->num_buffers, pac_data_raw->num_buffers));
153                 return NT_STATUS_INVALID_PARAMETER;
154         }
155
156         for (i=0; i < pac_data->num_buffers; i++) {
157                 if (pac_data->buffers[i].type != pac_data_raw->buffers[i].type) {
158                         DEBUG(0,("misparse!  PAC_DATA buffer %d has type %d while PAC_DATA_RAW has %d\n",
159                                  i, pac_data->buffers[i].type, pac_data->buffers[i].type));
160                         return NT_STATUS_INVALID_PARAMETER;
161                 }
162                 switch (pac_data->buffers[i].type) {
163                         case PAC_TYPE_LOGON_INFO:
164                                 if (!pac_data->buffers[i].info) {
165                                         break;
166                                 }
167                                 logon_info = pac_data->buffers[i].info->logon_info.info;
168                                 break;
169                         case PAC_TYPE_SRV_CHECKSUM:
170                                 if (!pac_data->buffers[i].info) {
171                                         break;
172                                 }
173                                 srv_sig_ptr = &pac_data->buffers[i].info->srv_cksum;
174                                 srv_sig_blob = &pac_data_raw->buffers[i].info->remaining;
175                                 break;
176                         case PAC_TYPE_KDC_CHECKSUM:
177                                 if (!pac_data->buffers[i].info) {
178                                         break;
179                                 }
180                                 kdc_sig_ptr = &pac_data->buffers[i].info->kdc_cksum;
181                                 kdc_sig_blob = &pac_data_raw->buffers[i].info->remaining;
182                                 break;
183                         case PAC_TYPE_LOGON_NAME:
184                                 logon_name = &pac_data->buffers[i].info->logon_name;
185                                 break;
186                         default:
187                                 break;
188                 }
189         }
190
191         if (!logon_info) {
192                 DEBUG(0,("PAC no logon_info\n"));
193                 return NT_STATUS_INVALID_PARAMETER;
194         }
195
196         if (!logon_name) {
197                 DEBUG(0,("PAC no logon_name\n"));
198                 return NT_STATUS_INVALID_PARAMETER;
199         }
200
201         if (!srv_sig_ptr || !srv_sig_blob) {
202                 DEBUG(0,("PAC no srv_key\n"));
203                 return NT_STATUS_INVALID_PARAMETER;
204         }
205
206         if (!kdc_sig_ptr || !kdc_sig_blob) {
207                 DEBUG(0,("PAC no kdc_key\n"));
208                 return NT_STATUS_INVALID_PARAMETER;
209         }
210
211         /* Find and zero out the signatures, as required by the signing algorithm */
212
213         /* We find the data blobs above, now we parse them to get at the exact portion we should zero */
214         ndr_err = ndr_pull_struct_blob(kdc_sig_blob, kdc_sig_wipe, 
215                                        iconv_convenience, kdc_sig_wipe,
216                                        (ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA);
217         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
218                 status = ndr_map_error2ntstatus(ndr_err);
219                 DEBUG(0,("can't parse the KDC signature: %s\n",
220                         nt_errstr(status)));
221                 return status;
222         }
223         
224         ndr_err = ndr_pull_struct_blob(srv_sig_blob, srv_sig_wipe, 
225                                        iconv_convenience, srv_sig_wipe,
226                                        (ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA);
227         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
228                 status = ndr_map_error2ntstatus(ndr_err);
229                 DEBUG(0,("can't parse the SRV signature: %s\n",
230                         nt_errstr(status)));
231                 return status;
232         }
233
234         /* Now zero the decoded structure */
235         memset(kdc_sig_wipe->signature.data, '\0', kdc_sig_wipe->signature.length);
236         memset(srv_sig_wipe->signature.data, '\0', srv_sig_wipe->signature.length);
237         
238         /* and reencode, back into the same place it came from */
239         ndr_err = ndr_push_struct_blob(kdc_sig_blob, pac_data_raw, 
240                                        iconv_convenience,
241                                        kdc_sig_wipe,
242                                        (ndr_push_flags_fn_t)ndr_push_PAC_SIGNATURE_DATA);
243         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
244                 status = ndr_map_error2ntstatus(ndr_err);
245                 DEBUG(0,("can't repack the KDC signature: %s\n",
246                         nt_errstr(status)));
247                 return status;
248         }
249         ndr_err = ndr_push_struct_blob(srv_sig_blob, pac_data_raw, 
250                                        iconv_convenience,
251                                        srv_sig_wipe,
252                                        (ndr_push_flags_fn_t)ndr_push_PAC_SIGNATURE_DATA);
253         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
254                 status = ndr_map_error2ntstatus(ndr_err);
255                 DEBUG(0,("can't repack the SRV signature: %s\n",
256                         nt_errstr(status)));
257                 return status;
258         }
259
260         /* push out the whole structure, but now with zero'ed signatures */
261         ndr_err = ndr_push_struct_blob(&modified_pac_blob, pac_data_raw, 
262                                        iconv_convenience,
263                                        pac_data_raw,
264                                        (ndr_push_flags_fn_t)ndr_push_PAC_DATA_RAW);
265         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
266                 status = ndr_map_error2ntstatus(ndr_err);
267                 DEBUG(0,("can't repack the RAW PAC: %s\n",
268                         nt_errstr(status)));
269                 return status;
270         }
271
272         /* verify by service_key */
273         ret = check_pac_checksum(mem_ctx, 
274                                  modified_pac_blob, srv_sig_ptr, 
275                                  context, 
276                                  service_keyblock);
277         if (ret) {
278                 DEBUG(1, ("PAC Decode: Failed to verify the service signature: %s\n",
279                           smb_get_krb5_error_message(context, ret, mem_ctx)));
280                 if (k5ret) {
281                         *k5ret = ret;
282                 }
283                 return NT_STATUS_ACCESS_DENIED;
284         }
285
286         if (krbtgt_keyblock) {
287                 ret = check_pac_checksum(mem_ctx, 
288                                             srv_sig_ptr->signature, kdc_sig_ptr, 
289                                             context, krbtgt_keyblock);
290                 if (ret) {
291                         DEBUG(1, ("PAC Decode: Failed to verify the KDC signature: %s\n",
292                                   smb_get_krb5_error_message(context, ret, mem_ctx)));
293                         if (k5ret) {
294                                 *k5ret = ret;
295                         }
296                         return NT_STATUS_ACCESS_DENIED;
297                 }
298         }
299
300         /* Convert to NT time, so as not to loose accuracy in comparison */
301         unix_to_nt_time(&tgs_authtime_nttime, tgs_authtime);
302
303         if (tgs_authtime_nttime != logon_name->logon_time) {
304                 DEBUG(2, ("PAC Decode: Logon time mismatch between ticket and PAC!\n"));
305                 DEBUG(2, ("PAC Decode: PAC: %s\n", nt_time_string(mem_ctx, logon_name->logon_time)));
306                 DEBUG(2, ("PAC Decode: Ticket: %s\n", nt_time_string(mem_ctx, tgs_authtime_nttime)));
307                 return NT_STATUS_ACCESS_DENIED;
308         }
309
310         ret = krb5_parse_name_flags(context, logon_name->account_name, KRB5_PRINCIPAL_PARSE_NO_REALM, 
311                                     &client_principal_pac);
312         if (ret) {
313                 DEBUG(2, ("Could not parse name from incoming PAC: [%s]: %s\n", 
314                           logon_name->account_name, 
315                           smb_get_krb5_error_message(context, ret, mem_ctx)));
316                 if (k5ret) {
317                         *k5ret = ret;
318                 }
319                 return NT_STATUS_INVALID_PARAMETER;
320         }
321
322         if (!krb5_principal_compare_any_realm(context, client_principal, client_principal_pac)) {
323                 DEBUG(2, ("Name in PAC [%s] does not match principal name in ticket\n", 
324                           logon_name->account_name));
325                 return NT_STATUS_ACCESS_DENIED;
326         }
327         
328 #if 0
329         if (strcasecmp(logon_info->info3.base.account_name.string, 
330                        "Administrator")== 0) {
331                 file_save("tmp_pac_data-admin.dat",blob.data,blob.length);
332         }
333 #endif
334
335         DEBUG(3,("Found account name from PAC: %s [%s]\n",
336                  logon_info->info3.base.account_name.string, 
337                  logon_info->info3.base.full_name.string));
338         *pac_data_out = pac_data;
339
340         return NT_STATUS_OK;
341 }
342
343 _PUBLIC_  NTSTATUS kerberos_pac_logon_info(TALLOC_CTX *mem_ctx,
344                                   struct smb_iconv_convenience *iconv_convenience,
345                                   struct PAC_LOGON_INFO **logon_info,
346                                   DATA_BLOB blob,
347                                   krb5_context context,
348                                   const krb5_keyblock *krbtgt_keyblock,
349                                   const krb5_keyblock *service_keyblock,
350                                   krb5_const_principal client_principal,
351                                   time_t tgs_authtime, 
352                                   krb5_error_code *k5ret)
353 {
354         NTSTATUS nt_status;
355         struct PAC_DATA *pac_data;
356         int i;
357         nt_status = kerberos_decode_pac(mem_ctx, 
358                                         iconv_convenience,
359                                         &pac_data,
360                                         blob,
361                                         context,
362                                         krbtgt_keyblock,
363                                         service_keyblock,
364                                         client_principal, 
365                                         tgs_authtime,
366                                         k5ret);
367         if (!NT_STATUS_IS_OK(nt_status)) {
368                 return nt_status;
369         }
370
371         *logon_info = NULL;
372         for (i=0; i < pac_data->num_buffers; i++) {
373                 if (pac_data->buffers[i].type != PAC_TYPE_LOGON_INFO) {
374                         continue;
375                 }
376                 *logon_info = pac_data->buffers[i].info->logon_info.info; 
377         }
378         if (!*logon_info) {
379                 return NT_STATUS_INVALID_PARAMETER;
380         }
381         return NT_STATUS_OK;
382 }
383
384 static krb5_error_code make_pac_checksum(TALLOC_CTX *mem_ctx, 
385                                          DATA_BLOB *pac_data,
386                                          struct PAC_SIGNATURE_DATA *sig,
387                                          krb5_context context,
388                                          const krb5_keyblock *keyblock)
389 {
390         krb5_error_code ret;
391         krb5_crypto crypto;
392         Checksum cksum;
393
394
395         ret = krb5_crypto_init(context,
396                                keyblock,
397                                0,
398                                &crypto);
399         if (ret) {
400                 DEBUG(0,("krb5_crypto_init() failed: %s\n",
401                           smb_get_krb5_error_message(context, ret, mem_ctx)));
402                 return ret;
403         }
404         ret = krb5_create_checksum(context,
405                                    crypto,
406                                    KRB5_KU_OTHER_CKSUM,
407                                    0,
408                                    pac_data->data,
409                                    pac_data->length,
410                                    &cksum);
411         if (ret) {
412                 DEBUG(2, ("PAC Verification failed: %s\n", 
413                           smb_get_krb5_error_message(context, ret, mem_ctx)));
414         }
415
416         krb5_crypto_destroy(context, crypto);
417
418         if (ret) {
419                 return ret;
420         }
421
422         sig->type = cksum.cksumtype;
423         sig->signature = data_blob_talloc(mem_ctx, cksum.checksum.data, cksum.checksum.length);
424         free_Checksum(&cksum);
425
426         return 0;
427 }
428
429  krb5_error_code kerberos_encode_pac(TALLOC_CTX *mem_ctx,
430                                      struct smb_iconv_convenience *iconv_convenience,
431                                     struct PAC_DATA *pac_data,
432                                     krb5_context context,
433                                     const krb5_keyblock *krbtgt_keyblock,
434                                     const krb5_keyblock *service_keyblock,
435                                     DATA_BLOB *pac) 
436 {
437         NTSTATUS nt_status;
438         krb5_error_code ret;
439         enum ndr_err_code ndr_err;
440         DATA_BLOB zero_blob = data_blob(NULL, 0);
441         DATA_BLOB tmp_blob = data_blob(NULL, 0);
442         struct PAC_SIGNATURE_DATA *kdc_checksum = NULL;
443         struct PAC_SIGNATURE_DATA *srv_checksum = NULL;
444         int i;
445
446         /* First, just get the keytypes filled in (and lengths right, eventually) */
447         for (i=0; i < pac_data->num_buffers; i++) {
448                 if (pac_data->buffers[i].type != PAC_TYPE_KDC_CHECKSUM) {
449                         continue;
450                 }
451                 kdc_checksum = &pac_data->buffers[i].info->kdc_cksum, 
452                 ret = make_pac_checksum(mem_ctx, &zero_blob,
453                                         kdc_checksum, 
454                                         context, krbtgt_keyblock);
455                 if (ret) {
456                         DEBUG(2, ("making krbtgt PAC checksum failed: %s\n", 
457                                   smb_get_krb5_error_message(context, ret, mem_ctx)));
458                         talloc_free(pac_data);
459                         return ret;
460                 }
461         }
462         
463         for (i=0; i < pac_data->num_buffers; i++) {
464                 if (pac_data->buffers[i].type != PAC_TYPE_SRV_CHECKSUM) {
465                         continue;
466                 }
467                 srv_checksum = &pac_data->buffers[i].info->srv_cksum; 
468                 ret = make_pac_checksum(mem_ctx, &zero_blob, 
469                                         srv_checksum, 
470                                         context, service_keyblock);
471                 if (ret) {
472                         DEBUG(2, ("making service PAC checksum failed: %s\n", 
473                                   smb_get_krb5_error_message(context, ret, mem_ctx)));
474                         talloc_free(pac_data);
475                         return ret;
476                 }
477         }
478
479         if (!kdc_checksum) {
480                 DEBUG(2, ("Invalid PAC constructed for signing, no KDC checksum present!"));
481                 return EINVAL;
482         }
483         if (!srv_checksum) {
484                 DEBUG(2, ("Invalid PAC constructed for signing, no SRV checksum present!"));
485                 return EINVAL;
486         }
487
488         /* But wipe out the actual signatures */
489         memset(kdc_checksum->signature.data, '\0', kdc_checksum->signature.length);
490         memset(srv_checksum->signature.data, '\0', srv_checksum->signature.length);
491
492         ndr_err = ndr_push_struct_blob(&tmp_blob, mem_ctx, 
493                                        iconv_convenience,
494                                        pac_data,
495                                        (ndr_push_flags_fn_t)ndr_push_PAC_DATA);
496         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
497                 nt_status = ndr_map_error2ntstatus(ndr_err);
498                 DEBUG(1, ("PAC (presig) push failed: %s\n", nt_errstr(nt_status)));
499                 talloc_free(pac_data);
500                 return EINVAL;
501         }
502
503         /* Then sign the result of the previous push, where the sig was zero'ed out */
504         ret = make_pac_checksum(mem_ctx, &tmp_blob, srv_checksum,
505                                 context, service_keyblock);
506
507         /* Then sign Server checksum */
508         ret = make_pac_checksum(mem_ctx, &srv_checksum->signature, kdc_checksum, context, krbtgt_keyblock);
509         if (ret) {
510                 DEBUG(2, ("making krbtgt PAC checksum failed: %s\n", 
511                           smb_get_krb5_error_message(context, ret, mem_ctx)));
512                 talloc_free(pac_data);
513                 return ret;
514         }
515
516         /* And push it out again, this time to the world.  This relies on determanistic pointer values */
517         ndr_err = ndr_push_struct_blob(&tmp_blob, mem_ctx, 
518                                        iconv_convenience,
519                                        pac_data,
520                                        (ndr_push_flags_fn_t)ndr_push_PAC_DATA);
521         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
522                 nt_status = ndr_map_error2ntstatus(ndr_err);
523                 DEBUG(1, ("PAC (final) push failed: %s\n", nt_errstr(nt_status)));
524                 talloc_free(pac_data);
525                 return EINVAL;
526         }
527
528         *pac = tmp_blob;
529
530         return ret;
531 }
532
533
534  krb5_error_code kerberos_create_pac(TALLOC_CTX *mem_ctx,
535                                      struct smb_iconv_convenience *iconv_convenience,
536                                      struct auth_serversupplied_info *server_info,
537                                      krb5_context context,
538                                      const krb5_keyblock *krbtgt_keyblock,
539                                      const krb5_keyblock *service_keyblock,
540                                      krb5_principal client_principal,
541                                      time_t tgs_authtime,
542                                      DATA_BLOB *pac)
543 {
544         NTSTATUS nt_status;
545         krb5_error_code ret;
546         struct PAC_DATA *pac_data = talloc(mem_ctx, struct PAC_DATA);
547         struct netr_SamInfo3 *sam3;
548         union PAC_INFO *u_LOGON_INFO;
549         struct PAC_LOGON_INFO *LOGON_INFO;
550         union PAC_INFO *u_LOGON_NAME;
551         struct PAC_LOGON_NAME *LOGON_NAME;
552         union PAC_INFO *u_KDC_CHECKSUM;
553         union PAC_INFO *u_SRV_CHECKSUM;
554
555         char *name;
556                 
557         enum {
558                 PAC_BUF_LOGON_INFO = 0,
559                 PAC_BUF_LOGON_NAME = 1,
560                 PAC_BUF_SRV_CHECKSUM = 2,
561                 PAC_BUF_KDC_CHECKSUM = 3,
562                 PAC_BUF_NUM_BUFFERS = 4
563         };
564
565         if (!pac_data) {
566                 return ENOMEM;
567         }
568
569         pac_data->num_buffers = PAC_BUF_NUM_BUFFERS;
570         pac_data->version = 0;
571
572         pac_data->buffers = talloc_array(pac_data, 
573                                          struct PAC_BUFFER,
574                                          pac_data->num_buffers);
575         if (!pac_data->buffers) {
576                 talloc_free(pac_data);
577                 return ENOMEM;
578         }
579
580         /* LOGON_INFO */
581         u_LOGON_INFO = talloc_zero(pac_data->buffers, union PAC_INFO);
582         if (!u_LOGON_INFO) {
583                 talloc_free(pac_data);
584                 return ENOMEM;
585         }
586         pac_data->buffers[PAC_BUF_LOGON_INFO].type = PAC_TYPE_LOGON_INFO;
587         pac_data->buffers[PAC_BUF_LOGON_INFO].info = u_LOGON_INFO;
588
589         /* LOGON_NAME */
590         u_LOGON_NAME = talloc_zero(pac_data->buffers, union PAC_INFO);
591         if (!u_LOGON_NAME) {
592                 talloc_free(pac_data);
593                 return ENOMEM;
594         }
595         pac_data->buffers[PAC_BUF_LOGON_NAME].type = PAC_TYPE_LOGON_NAME;
596         pac_data->buffers[PAC_BUF_LOGON_NAME].info = u_LOGON_NAME;
597         LOGON_NAME = &u_LOGON_NAME->logon_name;
598
599         /* SRV_CHECKSUM */
600         u_SRV_CHECKSUM = talloc_zero(pac_data->buffers, union PAC_INFO);
601         if (!u_SRV_CHECKSUM) {
602                 talloc_free(pac_data);
603                 return ENOMEM;
604         }
605         pac_data->buffers[PAC_BUF_SRV_CHECKSUM].type = PAC_TYPE_SRV_CHECKSUM;
606         pac_data->buffers[PAC_BUF_SRV_CHECKSUM].info = u_SRV_CHECKSUM;
607
608         /* KDC_CHECKSUM */
609         u_KDC_CHECKSUM = talloc_zero(pac_data->buffers, union PAC_INFO);
610         if (!u_KDC_CHECKSUM) {
611                 talloc_free(pac_data);
612                 return ENOMEM;
613         }
614         pac_data->buffers[PAC_BUF_KDC_CHECKSUM].type = PAC_TYPE_KDC_CHECKSUM;
615         pac_data->buffers[PAC_BUF_KDC_CHECKSUM].info = u_KDC_CHECKSUM;
616
617         /* now the real work begins... */
618
619         LOGON_INFO = talloc_zero(u_LOGON_INFO, struct PAC_LOGON_INFO);
620         if (!LOGON_INFO) {
621                 talloc_free(pac_data);
622                 return ENOMEM;
623         }
624         nt_status = auth_convert_server_info_saminfo3(LOGON_INFO, server_info, &sam3);
625         if (!NT_STATUS_IS_OK(nt_status)) {
626                 DEBUG(1, ("Getting Samba info failed: %s\n", nt_errstr(nt_status)));
627                 talloc_free(pac_data);
628                 return EINVAL;
629         }
630
631         u_LOGON_INFO->logon_info.info           = LOGON_INFO;
632         LOGON_INFO->info3 = *sam3;
633
634         ret = krb5_unparse_name_flags(context, client_principal, 
635                                       KRB5_PRINCIPAL_UNPARSE_NO_REALM, &name);
636         if (ret) {
637                 return ret;
638         }
639         LOGON_NAME->account_name        = talloc_strdup(LOGON_NAME, name);
640         free(name);
641         /*
642           this logon_time field is absolutely critical. This is what
643           caused all our PAC troubles :-)
644         */
645         unix_to_nt_time(&LOGON_NAME->logon_time, tgs_authtime);
646
647         ret = kerberos_encode_pac(mem_ctx, 
648                                   iconv_convenience,
649                                   pac_data, 
650                                   context,
651                                   krbtgt_keyblock,
652                                   service_keyblock,
653                                   pac);
654         talloc_free(pac_data);
655         return ret;
656 }
657
658 krb5_error_code kerberos_pac_to_server_info(TALLOC_CTX *mem_ctx,
659                                                 struct smb_iconv_convenience *iconv_convenience,
660                                                 krb5_pac pac,
661                                                 krb5_context context,
662                                                 struct auth_serversupplied_info **server_info) 
663 {
664         NTSTATUS nt_status;
665         enum ndr_err_code ndr_err;
666         krb5_error_code ret;
667
668         DATA_BLOB pac_logon_info_in, pac_srv_checksum_in, pac_kdc_checksum_in;
669         krb5_data k5pac_logon_info_in, k5pac_srv_checksum_in, k5pac_kdc_checksum_in;
670
671         union PAC_INFO info;
672         union netr_Validation validation;
673         struct auth_serversupplied_info *server_info_out;
674
675         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
676
677         if (!tmp_ctx) {
678                 return ENOMEM;
679         }
680
681         ret = krb5_pac_get_buffer(context, pac, PAC_TYPE_LOGON_INFO, &k5pac_logon_info_in);
682         if (ret != 0) {
683                 talloc_free(tmp_ctx);
684                 return EINVAL;
685         }
686
687         pac_logon_info_in = data_blob_const(k5pac_logon_info_in.data, k5pac_logon_info_in.length);
688
689         ndr_err = ndr_pull_union_blob(&pac_logon_info_in, tmp_ctx, iconv_convenience, &info,
690                                       PAC_TYPE_LOGON_INFO,
691                                       (ndr_pull_flags_fn_t)ndr_pull_PAC_INFO);
692         krb5_data_free(&k5pac_logon_info_in);
693         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err) || !info.logon_info.info) {
694                 nt_status = ndr_map_error2ntstatus(ndr_err);
695                 DEBUG(0,("can't parse the PAC LOGON_INFO: %s\n", nt_errstr(nt_status)));
696                 talloc_free(tmp_ctx);
697                 return EINVAL;
698         }
699
700         /* Pull this right into the normal auth sysstem structures */
701         validation.sam3 = &info.logon_info.info->info3;
702         nt_status = make_server_info_netlogon_validation(mem_ctx,
703                                                          "",
704                                                          3, &validation,
705                                                          &server_info_out); 
706         if (!NT_STATUS_IS_OK(nt_status)) {
707                 talloc_free(tmp_ctx);
708                 return EINVAL;
709         }
710         
711         ret = krb5_pac_get_buffer(context, pac, PAC_TYPE_SRV_CHECKSUM, &k5pac_srv_checksum_in);
712         if (ret != 0) {
713                 talloc_free(tmp_ctx);
714                 return ret;
715         }
716
717         pac_srv_checksum_in = data_blob_const(k5pac_srv_checksum_in.data, k5pac_srv_checksum_in.length);
718                 
719         ndr_err = ndr_pull_struct_blob(&pac_srv_checksum_in, server_info_out, 
720                                        iconv_convenience, &server_info_out->pac_srv_sig,
721                                        (ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA);
722         krb5_data_free(&k5pac_srv_checksum_in);
723         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
724                 nt_status = ndr_map_error2ntstatus(ndr_err);
725                 DEBUG(0,("can't parse the KDC signature: %s\n",
726                         nt_errstr(nt_status)));
727                 return EINVAL;
728         }
729
730         ret = krb5_pac_get_buffer(context, pac, PAC_TYPE_KDC_CHECKSUM, &k5pac_kdc_checksum_in);
731         if (ret != 0) {
732                 talloc_free(tmp_ctx);
733                 return ret;
734         }
735
736         pac_kdc_checksum_in = data_blob_const(k5pac_kdc_checksum_in.data, k5pac_kdc_checksum_in.length);
737                 
738         ndr_err = ndr_pull_struct_blob(&pac_kdc_checksum_in, server_info_out, 
739                                        iconv_convenience, &server_info_out->pac_kdc_sig,
740                                        (ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA);
741         krb5_data_free(&k5pac_kdc_checksum_in);
742         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
743                 nt_status = ndr_map_error2ntstatus(ndr_err);
744                 DEBUG(0,("can't parse the KDC signature: %s\n",
745                         nt_errstr(nt_status)));
746                 return EINVAL;
747         }
748
749         *server_info = server_info_out;
750         
751         return 0;
752 }
753
754
755 NTSTATUS kerberos_pac_blob_to_server_info(TALLOC_CTX *mem_ctx,
756                                                      struct smb_iconv_convenience *iconv_convenience,
757                                                      DATA_BLOB pac_blob, 
758                                                      krb5_context context,
759                                                      struct auth_serversupplied_info **server_info) 
760 {
761         krb5_error_code ret;
762         krb5_pac pac;
763         ret = krb5_pac_parse(context, 
764                              pac_blob.data, pac_blob.length, 
765                              &pac);
766         if (ret) {
767                 return map_nt_error_from_unix(ret);
768         }
769
770
771         ret = kerberos_pac_to_server_info(mem_ctx, iconv_convenience, pac, context, server_info);
772         krb5_pac_free(context, pac);
773         if (ret) {
774                 return map_nt_error_from_unix(ret);
775         }
776         return NT_STATUS_OK;
777 }