r8110: More PAC work. I still can't get WinXP to accept the PAC, but we are
[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
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 2 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, write to the Free Software
24    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 */
26
27 #include "includes.h"
28 #include "system/kerberos.h"
29 #include "system/time.h"
30 #include "system/network.h"
31 #include "auth/auth.h"
32 #include "auth/kerberos/kerberos.h"
33 #include "librpc/gen_ndr/ndr_krb5pac.h"
34 #include "auth/auth.h"
35
36 static NTSTATUS check_pac_checksum(TALLOC_CTX *mem_ctx, 
37                                    DATA_BLOB pac_data,
38                                    struct PAC_SIGNATURE_DATA *sig,
39                                    krb5_context context,
40                                    krb5_keyblock *keyblock)
41 {
42         krb5_error_code ret;
43         krb5_crypto crypto;
44         Checksum cksum;
45
46         cksum.cksumtype         = (CKSUMTYPE)sig->type;
47         cksum.checksum.length   = sizeof(sig->signature);
48         cksum.checksum.data     = sig->signature;
49
50
51         ret = krb5_crypto_init(context,
52                                keyblock,
53                                0,
54                                &crypto);
55         if (ret) {
56                 DEBUG(0,("krb5_crypto_init() failed\n"));
57                 return NT_STATUS_FOOBAR;
58         }
59         ret = krb5_verify_checksum(context,
60                                    crypto,
61                                    KRB5_KU_OTHER_CKSUM,
62                                    pac_data.data,
63                                    pac_data.length,
64                                    &cksum);
65         if (ret) {
66                 DEBUG(2, ("PAC Verification failed: %s\n", 
67                           smb_get_krb5_error_message(context, ret, mem_ctx)));
68         }
69
70         krb5_crypto_destroy(context, crypto);
71
72         if (ret) {
73                 return NT_STATUS_ACCESS_DENIED;
74         }
75
76         return NT_STATUS_OK;
77 }
78
79  NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx,
80                              struct PAC_LOGON_INFO **logon_info_out,
81                              DATA_BLOB blob,
82                              struct smb_krb5_context *smb_krb5_context,
83                              krb5_keyblock *keyblock)
84 {
85         NTSTATUS status;
86         struct PAC_SIGNATURE_DATA srv_sig;
87         struct PAC_SIGNATURE_DATA *srv_sig_ptr = NULL;
88         struct PAC_SIGNATURE_DATA kdc_sig;
89         struct PAC_SIGNATURE_DATA *kdc_sig_ptr = NULL;
90         struct PAC_LOGON_INFO *logon_info = NULL;
91         struct PAC_DATA pac_data;
92         DATA_BLOB modified_pac_blob = data_blob_talloc(mem_ctx, blob.data, blob.length);
93         int i;
94
95         status = ndr_pull_struct_blob(&blob, mem_ctx, &pac_data,
96                                         (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA);
97         if (!NT_STATUS_IS_OK(status)) {
98                 DEBUG(0,("can't parse the PAC\n"));
99                 return status;
100         }
101
102         if (pac_data.num_buffers < 3) {
103                 /* we need logon_ingo, service_key and kdc_key */
104                 DEBUG(0,("less than 3 PAC buffers\n"));
105                 return NT_STATUS_FOOBAR;
106         }
107
108         for (i=0; i < pac_data.num_buffers; i++) {
109                 switch (pac_data.buffers[i].type) {
110                         case PAC_TYPE_LOGON_INFO:
111                                 if (!pac_data.buffers[i].info) {
112                                         break;
113                                 }
114                                 logon_info = &pac_data.buffers[i].info->logon_info;
115                                 break;
116                         case PAC_TYPE_SRV_CHECKSUM:
117                                 if (!pac_data.buffers[i].info) {
118                                         break;
119                                 }
120                                 srv_sig_ptr = &pac_data.buffers[i].info->srv_cksum;
121                                 srv_sig = pac_data.buffers[i].info->srv_cksum;
122                                 break;
123                         case PAC_TYPE_KDC_CHECKSUM:
124                                 if (!pac_data.buffers[i].info) {
125                                         break;
126                                 }
127                                 kdc_sig_ptr = &pac_data.buffers[i].info->kdc_cksum;
128                                 kdc_sig = pac_data.buffers[i].info->kdc_cksum;
129                                 break;
130                         case PAC_TYPE_LOGON_NAME:
131                                 break;
132                         default:
133                                 break;
134                 }
135         }
136
137         if (!logon_info) {
138                 DEBUG(0,("PAC no logon_info\n"));
139                 return NT_STATUS_FOOBAR;
140         }
141
142         if (!srv_sig_ptr) {
143                 DEBUG(0,("PAC no srv_key\n"));
144                 return NT_STATUS_FOOBAR;
145         }
146
147         if (!kdc_sig_ptr) {
148                 DEBUG(0,("PAC no kdc_key\n"));
149                 return NT_STATUS_FOOBAR;
150         }
151
152         memset(&modified_pac_blob.data[modified_pac_blob.length - 20],
153                '\0', 16);
154         memset(&modified_pac_blob.data[modified_pac_blob.length - 44],
155                '\0', 16);
156
157         /* verify by service_key */
158         status = check_pac_checksum(mem_ctx, 
159                                     modified_pac_blob, &srv_sig, 
160                                     smb_krb5_context->krb5_context, keyblock);
161         
162         if (!NT_STATUS_IS_OK(status)) {
163                 return status;
164         }
165         DEBUG(0,("account_name: %s [%s]\n",
166                  logon_info->info3.base.account_name.string, 
167                  logon_info->info3.base.full_name.string));
168         *logon_info_out = logon_info;
169
170         return status;
171 }
172
173 static krb5_error_code make_pac_checksum(TALLOC_CTX *mem_ctx, 
174                                          DATA_BLOB pac_data,
175                                          struct PAC_SIGNATURE_DATA *sig,
176                                          krb5_context context,
177                                          krb5_keyblock *keyblock)
178 {
179         krb5_error_code ret;
180         krb5_crypto crypto;
181         Checksum cksum;
182
183
184         ret = krb5_crypto_init(context,
185                                keyblock,
186                                0,
187                                &crypto);
188         if (ret) {
189                 DEBUG(0,("krb5_crypto_init() failed\n"));
190                 return ret;
191         }
192         ret = krb5_create_checksum(context,
193                                    crypto,
194                                    KRB5_KU_OTHER_CKSUM,
195                                    0,
196                                    pac_data.data,
197                                    pac_data.length,
198                                    &cksum);
199         if (ret) {
200                 DEBUG(2, ("PAC Verification failed: %s\n", 
201                           smb_get_krb5_error_message(context, ret, mem_ctx)));
202         }
203
204         krb5_crypto_destroy(context, crypto);
205
206         if (ret) {
207                 return ret;
208         }
209
210         sig->type = cksum.cksumtype;
211         if (cksum.checksum.length == sizeof(sig->signature)) {
212                 memcpy(sig->signature, cksum.checksum.data, sizeof(sig->signature));
213         }
214
215         return 0;
216 }
217
218  krb5_error_code kerberos_encode_pac(TALLOC_CTX *mem_ctx,
219                                      struct auth_serversupplied_info *server_info,
220                                      krb5_context context,
221                                      krb5_keyblock *krbtgt_keyblock,
222                                      krb5_keyblock *server_keyblock,
223                                      DATA_BLOB *pac)
224 {
225         NTSTATUS nt_status;
226         DATA_BLOB zero_blob = data_blob(NULL, 0);
227         DATA_BLOB tmp_blob = data_blob(NULL, 0);
228         DATA_BLOB server_checksum_blob;
229         krb5_error_code ret;
230         struct PAC_DATA *pac_data = talloc(mem_ctx, struct PAC_DATA);
231         struct netr_SamBaseInfo *sam;
232         struct timeval tv = timeval_current();
233
234         enum {
235                 PAC_BUF_LOGON_TYPE = 0,
236                 PAC_BUF_LOGON_NAME = 1,
237                 PAC_BUF_KDC_CHECKSUM = 2,
238                 PAC_BUF_SRV_CHECKSUM = 3,
239                 PAC_BUF_NUM_BUFFERS = 4
240         };
241
242         if (!pac_data) {
243                 return ENOMEM;
244         }
245
246         pac_data->num_buffers = PAC_BUF_NUM_BUFFERS;
247         pac_data->version = 0;
248
249         pac_data->buffers = talloc_array(pac_data, 
250                                          struct PAC_BUFFER,
251                                          pac_data->num_buffers);
252
253         if (!pac_data->buffers) {
254                 talloc_free(pac_data);
255                 return ENOMEM;
256         }
257
258         pac_data->buffers[PAC_BUF_LOGON_TYPE].type = PAC_TYPE_LOGON_INFO;
259         pac_data->buffers[PAC_BUF_LOGON_TYPE].info = talloc_zero(pac_data->buffers,
260                                                                  union PAC_INFO);
261
262         nt_status = auth_convert_server_info_sambaseinfo(pac_data->buffers[0].info,
263                                                          server_info, &sam);
264         if (!NT_STATUS_IS_OK(nt_status)) {
265                 DEBUG(1, ("Getting Samba info failed: %s\n", nt_errstr(nt_status)));
266                 talloc_free(pac_data);
267                 return EINVAL;
268         }
269
270         pac_data->buffers[PAC_BUF_LOGON_TYPE].info->logon_info.info3.base = *sam;
271         pac_data->buffers[PAC_BUF_LOGON_TYPE].size
272                 = ndr_size_PAC_INFO(pac_data->buffers[PAC_BUF_LOGON_TYPE].info,
273                                     pac_data->buffers[PAC_BUF_LOGON_TYPE].type, 
274                                     0);
275         pac_data->buffers[PAC_BUF_LOGON_TYPE]._pad = 0;
276         
277         pac_data->buffers[PAC_BUF_LOGON_NAME].type = PAC_TYPE_LOGON_NAME;
278         pac_data->buffers[PAC_BUF_LOGON_NAME].info = talloc_zero(pac_data->buffers,
279                                                 union PAC_INFO);
280         pac_data->buffers[PAC_BUF_LOGON_NAME].info->logon_name.account_name
281                 = server_info->account_name;
282         pac_data->buffers[PAC_BUF_LOGON_NAME].info->logon_name.logon_time
283                 = timeval_to_nttime(&tv);
284         pac_data->buffers[PAC_BUF_LOGON_NAME].size
285                 = ndr_size_PAC_INFO(pac_data->buffers[PAC_BUF_LOGON_NAME].info,
286                                     pac_data->buffers[PAC_BUF_LOGON_NAME].type, 
287                                     0);
288         pac_data->buffers[PAC_BUF_LOGON_NAME]._pad = 0;
289
290
291         pac_data->buffers[PAC_BUF_KDC_CHECKSUM].type = PAC_TYPE_KDC_CHECKSUM;
292         pac_data->buffers[PAC_BUF_KDC_CHECKSUM].info = talloc_zero(pac_data->buffers,
293                                                 union PAC_INFO);
294         /* First, just get the keytypes filled in (and lengths right, eventually) */
295         ret = make_pac_checksum(mem_ctx, zero_blob, 
296                                 &pac_data->buffers[PAC_BUF_KDC_CHECKSUM].info->kdc_cksum,
297                                 context, krbtgt_keyblock);
298         if (ret) {
299                 DEBUG(2, ("making krbtgt PAC checksum failed: %s\n", 
300                           smb_get_krb5_error_message(context, ret, mem_ctx)));
301                 talloc_free(pac_data);
302                 return ret;
303         }
304
305         pac_data->buffers[PAC_BUF_KDC_CHECKSUM].size
306                 = ndr_size_PAC_INFO(pac_data->buffers[PAC_BUF_KDC_CHECKSUM].info,
307                                     pac_data->buffers[PAC_BUF_KDC_CHECKSUM].type, 
308                                     0);
309         pac_data->buffers[PAC_BUF_KDC_CHECKSUM]._pad = 0;
310
311
312         pac_data->buffers[PAC_BUF_SRV_CHECKSUM].type = PAC_TYPE_SRV_CHECKSUM;
313         pac_data->buffers[PAC_BUF_SRV_CHECKSUM].info = talloc_zero(pac_data->buffers,
314                                                 union PAC_INFO);
315         ret = make_pac_checksum(mem_ctx, zero_blob, 
316                                 &pac_data->buffers[PAC_BUF_SRV_CHECKSUM].info->srv_cksum,
317                                 context, server_keyblock);
318         if (ret) {
319                 DEBUG(2, ("making server PAC checksum failed: %s\n", 
320                           smb_get_krb5_error_message(context, ret, mem_ctx)));
321                 talloc_free(pac_data);
322                 return ret;
323         }
324
325         pac_data->buffers[PAC_BUF_SRV_CHECKSUM].size
326                 = ndr_size_PAC_INFO(pac_data->buffers[PAC_BUF_SRV_CHECKSUM].info,
327                                     pac_data->buffers[PAC_BUF_SRV_CHECKSUM].type, 
328                                     0);
329         pac_data->buffers[PAC_BUF_SRV_CHECKSUM]._pad = 0;
330
331         /* But wipe out the actual signatures */
332         ZERO_STRUCT(pac_data->buffers[PAC_BUF_KDC_CHECKSUM].info->kdc_cksum.signature);
333         ZERO_STRUCT(pac_data->buffers[PAC_BUF_SRV_CHECKSUM].info->srv_cksum.signature);
334         
335         nt_status = ndr_push_struct_blob(&tmp_blob, mem_ctx, pac_data,
336                                          (ndr_push_flags_fn_t)ndr_push_PAC_DATA);
337         if (!NT_STATUS_IS_OK(nt_status)) {
338                 DEBUG(1, ("PAC (presig) push failed: %s\n", nt_errstr(nt_status)));
339                 talloc_free(pac_data);
340                 return EINVAL;
341         }
342
343         /* Then sign the result of the previous push, where the sig was zero'ed out */
344         ret = make_pac_checksum(mem_ctx, tmp_blob, &pac_data->buffers[3].info->srv_cksum,
345                                 context, server_keyblock);
346
347         /* Push the Server checksum out */
348         nt_status = ndr_push_struct_blob(&server_checksum_blob, mem_ctx, 
349                                          &pac_data->buffers[PAC_BUF_SRV_CHECKSUM].info->srv_cksum,
350                                          (ndr_push_flags_fn_t)ndr_push_PAC_SIGNATURE_DATA);
351         if (!NT_STATUS_IS_OK(nt_status)) {
352                 DEBUG(1, ("PAC_SIGNATURE push failed: %s\n", nt_errstr(nt_status)));
353                 talloc_free(pac_data);
354                 return EINVAL;
355         }
356
357         /* Then sign the result of the previous push, where the sig was zero'ed out */
358         ret = make_pac_checksum(mem_ctx, server_checksum_blob, 
359                                 &pac_data->buffers[PAC_BUF_KDC_CHECKSUM].info->kdc_cksum,
360                                 context, krbtgt_keyblock);
361
362         /* And push it out again, this time to the world.  This relies on determanistic pointer values */
363         nt_status = ndr_push_struct_blob(&tmp_blob, mem_ctx, pac_data,
364                                          (ndr_push_flags_fn_t)ndr_push_PAC_DATA);
365         if (!NT_STATUS_IS_OK(nt_status)) {
366                 DEBUG(1, ("PAC (final) push failed: %s\n", nt_errstr(nt_status)));
367                 talloc_free(pac_data);
368                 return EINVAL;
369         }
370
371         *pac = tmp_blob;
372
373         talloc_free(pac_data);
374         return ret;
375 }
376