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