40f0cf7cf85a980c2a19bf852cce9f50221a857b
[kai/samba-autobuild/.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         int 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                 return NT_STATUS_ACCESS_DENIED;
320         }
321
322 #if 0
323         if (strcasecmp(logon_info->info3.base.account_name.string,
324                        "Administrator")== 0) {
325                 file_save("tmp_pac_data-admin.dat",blob.data,blob.length);
326         }
327 #endif
328
329         DEBUG(3,("Found account name from PAC: %s [%s]\n",
330                  logon_info->info3.base.account_name.string,
331                  logon_info->info3.base.full_name.string));
332         *pac_data_out = pac_data;
333
334         return NT_STATUS_OK;
335 }
336
337 _PUBLIC_  NTSTATUS kerberos_pac_logon_info(TALLOC_CTX *mem_ctx,
338                                   struct PAC_LOGON_INFO **logon_info,
339                                   DATA_BLOB blob,
340                                   krb5_context context,
341                                   const krb5_keyblock *krbtgt_keyblock,
342                                   const krb5_keyblock *service_keyblock,
343                                   krb5_const_principal client_principal,
344                                   time_t tgs_authtime,
345                                   krb5_error_code *k5ret)
346 {
347         NTSTATUS nt_status;
348         struct PAC_DATA *pac_data;
349         int i;
350         nt_status = kerberos_decode_pac(mem_ctx,
351                                         &pac_data,
352                                         blob,
353                                         context,
354                                         krbtgt_keyblock,
355                                         service_keyblock,
356                                         client_principal,
357                                         tgs_authtime,
358                                         k5ret);
359         if (!NT_STATUS_IS_OK(nt_status)) {
360                 return nt_status;
361         }
362
363         *logon_info = NULL;
364         for (i=0; i < pac_data->num_buffers; i++) {
365                 if (pac_data->buffers[i].type != PAC_TYPE_LOGON_INFO) {
366                         continue;
367                 }
368                 *logon_info = pac_data->buffers[i].info->logon_info.info;
369         }
370         if (!*logon_info) {
371                 return NT_STATUS_INVALID_PARAMETER;
372         }
373         return NT_STATUS_OK;
374 }
375
376 static krb5_error_code make_pac_checksum(TALLOC_CTX *mem_ctx,
377                                          DATA_BLOB *pac_data,
378                                          struct PAC_SIGNATURE_DATA *sig,
379                                          krb5_context context,
380                                          const krb5_keyblock *keyblock)
381 {
382         krb5_error_code ret;
383         krb5_crypto crypto;
384         Checksum cksum;
385
386
387         ret = krb5_crypto_init(context,
388                                keyblock,
389                                0,
390                                &crypto);
391         if (ret) {
392                 DEBUG(0,("krb5_crypto_init() failed: %s\n",
393                           smb_get_krb5_error_message(context, ret, mem_ctx)));
394                 return ret;
395         }
396         ret = krb5_create_checksum(context,
397                                    crypto,
398                                    KRB5_KU_OTHER_CKSUM,
399                                    0,
400                                    pac_data->data,
401                                    pac_data->length,
402                                    &cksum);
403         if (ret) {
404                 DEBUG(2, ("PAC Verification failed: %s\n",
405                           smb_get_krb5_error_message(context, ret, mem_ctx)));
406         }
407
408         krb5_crypto_destroy(context, crypto);
409
410         if (ret) {
411                 return ret;
412         }
413
414         sig->type = cksum.cksumtype;
415         sig->signature = data_blob_talloc(mem_ctx, cksum.checksum.data, cksum.checksum.length);
416         free_Checksum(&cksum);
417
418         return 0;
419 }
420
421  krb5_error_code kerberos_encode_pac(TALLOC_CTX *mem_ctx,
422                                     struct PAC_DATA *pac_data,
423                                     krb5_context context,
424                                     const krb5_keyblock *krbtgt_keyblock,
425                                     const krb5_keyblock *service_keyblock,
426                                     DATA_BLOB *pac)
427 {
428         NTSTATUS nt_status;
429         krb5_error_code ret;
430         enum ndr_err_code ndr_err;
431         DATA_BLOB zero_blob = data_blob(NULL, 0);
432         DATA_BLOB tmp_blob = data_blob(NULL, 0);
433         struct PAC_SIGNATURE_DATA *kdc_checksum = NULL;
434         struct PAC_SIGNATURE_DATA *srv_checksum = NULL;
435         int i;
436
437         /* First, just get the keytypes filled in (and lengths right, eventually) */
438         for (i=0; i < pac_data->num_buffers; i++) {
439                 if (pac_data->buffers[i].type != PAC_TYPE_KDC_CHECKSUM) {
440                         continue;
441                 }
442                 kdc_checksum = &pac_data->buffers[i].info->kdc_cksum,
443                 ret = make_pac_checksum(mem_ctx, &zero_blob,
444                                         kdc_checksum,
445                                         context, krbtgt_keyblock);
446                 if (ret) {
447                         DEBUG(2, ("making krbtgt PAC checksum failed: %s\n",
448                                   smb_get_krb5_error_message(context, ret, mem_ctx)));
449                         talloc_free(pac_data);
450                         return ret;
451                 }
452         }
453
454         for (i=0; i < pac_data->num_buffers; i++) {
455                 if (pac_data->buffers[i].type != PAC_TYPE_SRV_CHECKSUM) {
456                         continue;
457                 }
458                 srv_checksum = &pac_data->buffers[i].info->srv_cksum;
459                 ret = make_pac_checksum(mem_ctx, &zero_blob,
460                                         srv_checksum,
461                                         context, service_keyblock);
462                 if (ret) {
463                         DEBUG(2, ("making service PAC checksum failed: %s\n",
464                                   smb_get_krb5_error_message(context, ret, mem_ctx)));
465                         talloc_free(pac_data);
466                         return ret;
467                 }
468         }
469
470         if (!kdc_checksum) {
471                 DEBUG(2, ("Invalid PAC constructed for signing, no KDC checksum present!"));
472                 return EINVAL;
473         }
474         if (!srv_checksum) {
475                 DEBUG(2, ("Invalid PAC constructed for signing, no SRV checksum present!"));
476                 return EINVAL;
477         }
478
479         /* But wipe out the actual signatures */
480         memset(kdc_checksum->signature.data, '\0', kdc_checksum->signature.length);
481         memset(srv_checksum->signature.data, '\0', srv_checksum->signature.length);
482
483         ndr_err = ndr_push_struct_blob(&tmp_blob, mem_ctx,
484                                        pac_data,
485                                        (ndr_push_flags_fn_t)ndr_push_PAC_DATA);
486         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
487                 nt_status = ndr_map_error2ntstatus(ndr_err);
488                 DEBUG(1, ("PAC (presig) push failed: %s\n", nt_errstr(nt_status)));
489                 talloc_free(pac_data);
490                 return EINVAL;
491         }
492
493         /* Then sign the result of the previous push, where the sig was zero'ed out */
494         ret = make_pac_checksum(mem_ctx, &tmp_blob, srv_checksum,
495                                 context, service_keyblock);
496
497         /* Then sign Server checksum */
498         ret = make_pac_checksum(mem_ctx, &srv_checksum->signature, kdc_checksum, context, krbtgt_keyblock);
499         if (ret) {
500                 DEBUG(2, ("making krbtgt PAC checksum failed: %s\n",
501                           smb_get_krb5_error_message(context, ret, mem_ctx)));
502                 talloc_free(pac_data);
503                 return ret;
504         }
505
506         /* And push it out again, this time to the world.  This relies on determanistic pointer values */
507         ndr_err = ndr_push_struct_blob(&tmp_blob, mem_ctx,
508                                        pac_data,
509                                        (ndr_push_flags_fn_t)ndr_push_PAC_DATA);
510         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
511                 nt_status = ndr_map_error2ntstatus(ndr_err);
512                 DEBUG(1, ("PAC (final) push failed: %s\n", nt_errstr(nt_status)));
513                 talloc_free(pac_data);
514                 return EINVAL;
515         }
516
517         *pac = tmp_blob;
518
519         return ret;
520 }
521
522
523  krb5_error_code kerberos_create_pac(TALLOC_CTX *mem_ctx,
524                                      struct auth_serversupplied_info *server_info,
525                                      krb5_context context,
526                                      const krb5_keyblock *krbtgt_keyblock,
527                                      const krb5_keyblock *service_keyblock,
528                                      krb5_principal client_principal,
529                                      time_t tgs_authtime,
530                                      DATA_BLOB *pac)
531 {
532         NTSTATUS nt_status;
533         krb5_error_code ret;
534         struct PAC_DATA *pac_data = talloc(mem_ctx, struct PAC_DATA);
535         struct netr_SamInfo3 *sam3;
536         union PAC_INFO *u_LOGON_INFO;
537         struct PAC_LOGON_INFO *LOGON_INFO;
538         union PAC_INFO *u_LOGON_NAME;
539         struct PAC_LOGON_NAME *LOGON_NAME;
540         union PAC_INFO *u_KDC_CHECKSUM;
541         union PAC_INFO *u_SRV_CHECKSUM;
542
543         char *name;
544
545         enum {
546                 PAC_BUF_LOGON_INFO = 0,
547                 PAC_BUF_LOGON_NAME = 1,
548                 PAC_BUF_SRV_CHECKSUM = 2,
549                 PAC_BUF_KDC_CHECKSUM = 3,
550                 PAC_BUF_NUM_BUFFERS = 4
551         };
552
553         if (!pac_data) {
554                 return ENOMEM;
555         }
556
557         pac_data->num_buffers = PAC_BUF_NUM_BUFFERS;
558         pac_data->version = 0;
559
560         pac_data->buffers = talloc_array(pac_data,
561                                          struct PAC_BUFFER,
562                                          pac_data->num_buffers);
563         if (!pac_data->buffers) {
564                 talloc_free(pac_data);
565                 return ENOMEM;
566         }
567
568         /* LOGON_INFO */
569         u_LOGON_INFO = talloc_zero(pac_data->buffers, union PAC_INFO);
570         if (!u_LOGON_INFO) {
571                 talloc_free(pac_data);
572                 return ENOMEM;
573         }
574         pac_data->buffers[PAC_BUF_LOGON_INFO].type = PAC_TYPE_LOGON_INFO;
575         pac_data->buffers[PAC_BUF_LOGON_INFO].info = u_LOGON_INFO;
576
577         /* LOGON_NAME */
578         u_LOGON_NAME = talloc_zero(pac_data->buffers, union PAC_INFO);
579         if (!u_LOGON_NAME) {
580                 talloc_free(pac_data);
581                 return ENOMEM;
582         }
583         pac_data->buffers[PAC_BUF_LOGON_NAME].type = PAC_TYPE_LOGON_NAME;
584         pac_data->buffers[PAC_BUF_LOGON_NAME].info = u_LOGON_NAME;
585         LOGON_NAME = &u_LOGON_NAME->logon_name;
586
587         /* SRV_CHECKSUM */
588         u_SRV_CHECKSUM = talloc_zero(pac_data->buffers, union PAC_INFO);
589         if (!u_SRV_CHECKSUM) {
590                 talloc_free(pac_data);
591                 return ENOMEM;
592         }
593         pac_data->buffers[PAC_BUF_SRV_CHECKSUM].type = PAC_TYPE_SRV_CHECKSUM;
594         pac_data->buffers[PAC_BUF_SRV_CHECKSUM].info = u_SRV_CHECKSUM;
595
596         /* KDC_CHECKSUM */
597         u_KDC_CHECKSUM = talloc_zero(pac_data->buffers, union PAC_INFO);
598         if (!u_KDC_CHECKSUM) {
599                 talloc_free(pac_data);
600                 return ENOMEM;
601         }
602         pac_data->buffers[PAC_BUF_KDC_CHECKSUM].type = PAC_TYPE_KDC_CHECKSUM;
603         pac_data->buffers[PAC_BUF_KDC_CHECKSUM].info = u_KDC_CHECKSUM;
604
605         /* now the real work begins... */
606
607         LOGON_INFO = talloc_zero(u_LOGON_INFO, struct PAC_LOGON_INFO);
608         if (!LOGON_INFO) {
609                 talloc_free(pac_data);
610                 return ENOMEM;
611         }
612         nt_status = auth_convert_server_info_saminfo3(LOGON_INFO, server_info, &sam3);
613         if (!NT_STATUS_IS_OK(nt_status)) {
614                 DEBUG(1, ("Getting Samba info failed: %s\n", nt_errstr(nt_status)));
615                 talloc_free(pac_data);
616                 return EINVAL;
617         }
618
619         u_LOGON_INFO->logon_info.info           = LOGON_INFO;
620         LOGON_INFO->info3 = *sam3;
621
622         ret = krb5_unparse_name_flags(context, client_principal,
623                                       KRB5_PRINCIPAL_UNPARSE_NO_REALM, &name);
624         if (ret) {
625                 return ret;
626         }
627         LOGON_NAME->account_name        = talloc_strdup(LOGON_NAME, name);
628         free(name);
629         /*
630           this logon_time field is absolutely critical. This is what
631           caused all our PAC troubles :-)
632         */
633         unix_to_nt_time(&LOGON_NAME->logon_time, tgs_authtime);
634
635         ret = kerberos_encode_pac(mem_ctx,
636                                   pac_data,
637                                   context,
638                                   krbtgt_keyblock,
639                                   service_keyblock,
640                                   pac);
641         talloc_free(pac_data);
642         return ret;
643 }
644
645 krb5_error_code kerberos_pac_to_server_info(TALLOC_CTX *mem_ctx,
646                                                 krb5_pac pac,
647                                                 krb5_context context,
648                                                 struct auth_serversupplied_info **server_info)
649 {
650         NTSTATUS nt_status;
651         enum ndr_err_code ndr_err;
652         krb5_error_code ret;
653
654         DATA_BLOB pac_logon_info_in, pac_srv_checksum_in, pac_kdc_checksum_in;
655         krb5_data k5pac_logon_info_in, k5pac_srv_checksum_in, k5pac_kdc_checksum_in;
656
657         union PAC_INFO info;
658         union netr_Validation validation;
659         struct auth_serversupplied_info *server_info_out;
660
661         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
662
663         if (!tmp_ctx) {
664                 return ENOMEM;
665         }
666
667         ret = krb5_pac_get_buffer(context, pac, PAC_TYPE_LOGON_INFO, &k5pac_logon_info_in);
668         if (ret != 0) {
669                 talloc_free(tmp_ctx);
670                 return EINVAL;
671         }
672
673         pac_logon_info_in = data_blob_const(k5pac_logon_info_in.data, k5pac_logon_info_in.length);
674
675         ndr_err = ndr_pull_union_blob(&pac_logon_info_in, tmp_ctx, &info,
676                                       PAC_TYPE_LOGON_INFO,
677                                       (ndr_pull_flags_fn_t)ndr_pull_PAC_INFO);
678         krb5_data_free(&k5pac_logon_info_in);
679         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err) || !info.logon_info.info) {
680                 nt_status = ndr_map_error2ntstatus(ndr_err);
681                 DEBUG(0,("can't parse the PAC LOGON_INFO: %s\n", nt_errstr(nt_status)));
682                 talloc_free(tmp_ctx);
683                 return EINVAL;
684         }
685
686         /* Pull this right into the normal auth sysstem structures */
687         nt_status = make_server_info_pac(mem_ctx,
688                                          info.logon_info.info,
689                                          &server_info_out);
690         if (!NT_STATUS_IS_OK(nt_status)) {
691                 talloc_free(tmp_ctx);
692                 return EINVAL;
693         }
694
695         ret = krb5_pac_get_buffer(context, pac, PAC_TYPE_SRV_CHECKSUM, &k5pac_srv_checksum_in);
696         if (ret != 0) {
697                 talloc_free(tmp_ctx);
698                 return ret;
699         }
700
701         pac_srv_checksum_in = data_blob_const(k5pac_srv_checksum_in.data, k5pac_srv_checksum_in.length);
702
703         ndr_err = ndr_pull_struct_blob(&pac_srv_checksum_in, server_info_out,
704                                        &server_info_out->pac_srv_sig,
705                                        (ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA);
706         krb5_data_free(&k5pac_srv_checksum_in);
707         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
708                 nt_status = ndr_map_error2ntstatus(ndr_err);
709                 DEBUG(0,("can't parse the KDC signature: %s\n",
710                         nt_errstr(nt_status)));
711                 return EINVAL;
712         }
713
714         ret = krb5_pac_get_buffer(context, pac, PAC_TYPE_KDC_CHECKSUM, &k5pac_kdc_checksum_in);
715         if (ret != 0) {
716                 talloc_free(tmp_ctx);
717                 return ret;
718         }
719
720         pac_kdc_checksum_in = data_blob_const(k5pac_kdc_checksum_in.data, k5pac_kdc_checksum_in.length);
721
722         ndr_err = ndr_pull_struct_blob(&pac_kdc_checksum_in, server_info_out,
723                                        &server_info_out->pac_kdc_sig,
724                                        (ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA);
725         krb5_data_free(&k5pac_kdc_checksum_in);
726         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
727                 nt_status = ndr_map_error2ntstatus(ndr_err);
728                 DEBUG(0,("can't parse the KDC signature: %s\n",
729                         nt_errstr(nt_status)));
730                 return EINVAL;
731         }
732
733         *server_info = server_info_out;
734
735         return 0;
736 }
737
738
739 NTSTATUS kerberos_pac_blob_to_server_info(TALLOC_CTX *mem_ctx,
740                                                      DATA_BLOB pac_blob,
741                                                      krb5_context context,
742                                                      struct auth_serversupplied_info **server_info)
743 {
744         krb5_error_code ret;
745         krb5_pac pac;
746         ret = krb5_pac_parse(context,
747                              pac_blob.data, pac_blob.length,
748                              &pac);
749         if (ret) {
750                 return map_nt_error_from_unix(ret);
751         }
752
753
754         ret = kerberos_pac_to_server_info(mem_ctx, pac, context, server_info);
755         krb5_pac_free(context, pac);
756         if (ret) {
757                 return map_nt_error_from_unix(ret);
758         }
759         return NT_STATUS_OK;
760 }