r8156: I found out that the unknown[2] field of the unknown[4] array is a length...
[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         /* file_save("tmp_pac_data.dat",blob.data,blob.length); */
96
97         status = ndr_pull_struct_blob(&blob, mem_ctx, &pac_data,
98                                         (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA);
99         if (!NT_STATUS_IS_OK(status)) {
100                 DEBUG(0,("can't parse the PAC\n"));
101                 return status;
102         }
103
104         if (pac_data.num_buffers < 3) {
105                 /* we need logon_ingo, service_key and kdc_key */
106                 DEBUG(0,("less than 3 PAC buffers\n"));
107                 return NT_STATUS_FOOBAR;
108         }
109
110         for (i=0; i < pac_data.num_buffers; i++) {
111                 switch (pac_data.buffers[i].type) {
112                         case PAC_TYPE_LOGON_INFO:
113                                 if (!pac_data.buffers[i].info) {
114                                         break;
115                                 }
116                                 logon_info = pac_data.buffers[i].info->logon_info.info;
117                                 break;
118                         case PAC_TYPE_SRV_CHECKSUM:
119                                 if (!pac_data.buffers[i].info) {
120                                         break;
121                                 }
122                                 srv_sig_ptr = &pac_data.buffers[i].info->srv_cksum;
123                                 srv_sig = pac_data.buffers[i].info->srv_cksum;
124                                 break;
125                         case PAC_TYPE_KDC_CHECKSUM:
126                                 if (!pac_data.buffers[i].info) {
127                                         break;
128                                 }
129                                 kdc_sig_ptr = &pac_data.buffers[i].info->kdc_cksum;
130                                 kdc_sig = pac_data.buffers[i].info->kdc_cksum;
131                                 break;
132                         case PAC_TYPE_LOGON_NAME:
133                                 break;
134                         default:
135                                 break;
136                 }
137         }
138
139         if (!logon_info) {
140                 DEBUG(0,("PAC no logon_info\n"));
141                 return NT_STATUS_FOOBAR;
142         }
143
144         if (!srv_sig_ptr) {
145                 DEBUG(0,("PAC no srv_key\n"));
146                 return NT_STATUS_FOOBAR;
147         }
148
149         if (!kdc_sig_ptr) {
150                 DEBUG(0,("PAC no kdc_key\n"));
151                 return NT_STATUS_FOOBAR;
152         }
153
154         memset(&modified_pac_blob.data[modified_pac_blob.length - 20],
155                '\0', 16);
156         memset(&modified_pac_blob.data[modified_pac_blob.length - 44],
157                '\0', 16);
158
159         /* verify by service_key */
160         status = check_pac_checksum(mem_ctx, 
161                                     modified_pac_blob, &srv_sig, 
162                                     smb_krb5_context->krb5_context, keyblock);
163         
164         if (!NT_STATUS_IS_OK(status)) {
165                 return status;
166         }
167         DEBUG(0,("account_name: %s [%s]\n",
168                  logon_info->info3.base.account_name.string, 
169                  logon_info->info3.base.full_name.string));
170         *logon_info_out = logon_info;
171
172         return status;
173 }
174
175 static krb5_error_code make_pac_checksum(TALLOC_CTX *mem_ctx, 
176                                          DATA_BLOB pac_data,
177                                          struct PAC_SIGNATURE_DATA *sig,
178                                          krb5_context context,
179                                          krb5_keyblock *keyblock)
180 {
181         krb5_error_code ret;
182         krb5_crypto crypto;
183         Checksum cksum;
184
185
186         ret = krb5_crypto_init(context,
187                                keyblock,
188                                0,
189                                &crypto);
190         if (ret) {
191                 DEBUG(0,("krb5_crypto_init() failed\n"));
192                 return ret;
193         }
194         ret = krb5_create_checksum(context,
195                                    crypto,
196                                    KRB5_KU_OTHER_CKSUM,
197                                    0,
198                                    pac_data.data,
199                                    pac_data.length,
200                                    &cksum);
201         if (ret) {
202                 DEBUG(2, ("PAC Verification failed: %s\n", 
203                           smb_get_krb5_error_message(context, ret, mem_ctx)));
204         }
205
206         krb5_crypto_destroy(context, crypto);
207
208         if (ret) {
209                 return ret;
210         }
211
212         sig->type = cksum.cksumtype;
213         if (cksum.checksum.length == sizeof(sig->signature)) {
214                 memcpy(sig->signature, cksum.checksum.data, sizeof(sig->signature));
215         }
216
217         return 0;
218 }
219
220  krb5_error_code kerberos_encode_pac(TALLOC_CTX *mem_ctx,
221                                      struct auth_serversupplied_info *server_info,
222                                      krb5_context context,
223                                      krb5_keyblock *krbtgt_keyblock,
224                                      krb5_keyblock *server_keyblock,
225                                      DATA_BLOB *pac)
226 {
227         NTSTATUS nt_status;
228         DATA_BLOB zero_blob = data_blob(NULL, 0);
229         DATA_BLOB tmp_blob = data_blob(NULL, 0);
230         DATA_BLOB server_checksum_blob;
231         krb5_error_code ret;
232         struct PAC_DATA *pac_data = talloc(mem_ctx, struct PAC_DATA);
233         struct netr_SamBaseInfo *sam;
234         struct timeval tv = timeval_current();
235         union PAC_INFO *u_LOGON_INFO;
236         struct PAC_LOGON_INFO *LOGON_INFO;
237         union PAC_INFO *u_LOGON_NAME;
238         struct PAC_LOGON_NAME *LOGON_NAME;
239         union PAC_INFO *u_KDC_CHECKSUM;
240         struct PAC_SIGNATURE_DATA *KDC_CHECKSUM;
241         union PAC_INFO *u_SRV_CHECKSUM;
242         struct PAC_SIGNATURE_DATA *SRV_CHECKSUM;
243
244         enum {
245                 PAC_BUF_LOGON_INFO = 0,
246                 PAC_BUF_LOGON_NAME = 1,
247                 PAC_BUF_KDC_CHECKSUM = 2,
248                 PAC_BUF_SRV_CHECKSUM = 3,
249                 PAC_BUF_NUM_BUFFERS = 4
250         };
251
252         if (!pac_data) {
253                 return ENOMEM;
254         }
255
256         pac_data->num_buffers = PAC_BUF_NUM_BUFFERS;
257         pac_data->version = 0;
258
259         pac_data->buffers = talloc_array(pac_data, 
260                                          struct PAC_BUFFER,
261                                          pac_data->num_buffers);
262         if (!pac_data->buffers) {
263                 talloc_free(pac_data);
264                 return ENOMEM;
265         }
266
267         /* LOGON_INFO */
268         u_LOGON_INFO = talloc_zero(pac_data->buffers, union PAC_INFO);
269         if (!u_LOGON_INFO) {
270                 talloc_free(pac_data);
271                 return ENOMEM;
272         }
273         pac_data->buffers[PAC_BUF_LOGON_INFO].type = PAC_TYPE_LOGON_INFO;
274         pac_data->buffers[PAC_BUF_LOGON_INFO].info = u_LOGON_INFO;
275
276         /* LOGON_NAME */
277         u_LOGON_NAME = talloc_zero(pac_data->buffers, union PAC_INFO);
278         if (!u_LOGON_NAME) {
279                 talloc_free(pac_data);
280                 return ENOMEM;
281         }
282         pac_data->buffers[PAC_BUF_LOGON_NAME].type = PAC_TYPE_LOGON_NAME;
283         pac_data->buffers[PAC_BUF_LOGON_NAME].info = u_LOGON_NAME;
284         LOGON_NAME = &u_LOGON_NAME->logon_name;
285
286         /* KDC_CHECKSUM */
287         u_KDC_CHECKSUM = talloc_zero(pac_data->buffers, union PAC_INFO);
288         if (!u_KDC_CHECKSUM) {
289                 talloc_free(pac_data);
290                 return ENOMEM;
291         }
292         pac_data->buffers[PAC_BUF_KDC_CHECKSUM].type = PAC_TYPE_KDC_CHECKSUM;
293         pac_data->buffers[PAC_BUF_KDC_CHECKSUM].info = u_KDC_CHECKSUM;
294         KDC_CHECKSUM = &u_KDC_CHECKSUM->kdc_cksum;
295
296         /* SRV_CHECKSUM */
297         u_SRV_CHECKSUM = talloc_zero(pac_data->buffers, union PAC_INFO);
298         if (!u_SRV_CHECKSUM) {
299                 talloc_free(pac_data);
300                 return ENOMEM;
301         }
302         pac_data->buffers[PAC_BUF_SRV_CHECKSUM].type = PAC_TYPE_SRV_CHECKSUM;
303         pac_data->buffers[PAC_BUF_SRV_CHECKSUM].info = u_SRV_CHECKSUM;
304         SRV_CHECKSUM = &u_SRV_CHECKSUM->srv_cksum;
305
306         /* now the real work begins... */
307
308         LOGON_INFO = talloc_zero(u_LOGON_INFO, struct PAC_LOGON_INFO);
309         if (!LOGON_INFO) {
310                 talloc_free(pac_data);
311                 return ENOMEM;
312         }
313         nt_status = auth_convert_server_info_sambaseinfo(LOGON_INFO, server_info, &sam);
314         if (!NT_STATUS_IS_OK(nt_status)) {
315                 DEBUG(1, ("Getting Samba info failed: %s\n", nt_errstr(nt_status)));
316                 talloc_free(pac_data);
317                 return EINVAL;
318         }
319
320         u_LOGON_INFO->logon_info.info           = LOGON_INFO;
321         LOGON_INFO->info3.base = *sam;
322
323         LOGON_NAME->account_name        = server_info->account_name;
324         LOGON_NAME->logon_time          = timeval_to_nttime(&tv);
325
326
327
328         /* First, just get the keytypes filled in (and lengths right, eventually) */
329         ret = make_pac_checksum(mem_ctx, zero_blob, KDC_CHECKSUM, context, krbtgt_keyblock);
330         if (ret) {
331                 DEBUG(2, ("making krbtgt PAC checksum failed: %s\n", 
332                           smb_get_krb5_error_message(context, ret, mem_ctx)));
333                 talloc_free(pac_data);
334                 return ret;
335         }
336
337         ret = make_pac_checksum(mem_ctx, zero_blob, SRV_CHECKSUM, context, server_keyblock);
338         if (ret) {
339                 DEBUG(2, ("making server PAC checksum failed: %s\n", 
340                           smb_get_krb5_error_message(context, ret, mem_ctx)));
341                 talloc_free(pac_data);
342                 return ret;
343         }
344
345         /* But wipe out the actual signatures */
346         ZERO_STRUCT(KDC_CHECKSUM->signature);
347         ZERO_STRUCT(SRV_CHECKSUM->signature);
348
349         nt_status = ndr_push_struct_blob(&tmp_blob, mem_ctx, pac_data,
350                                          (ndr_push_flags_fn_t)ndr_push_PAC_DATA);
351         if (!NT_STATUS_IS_OK(nt_status)) {
352                 DEBUG(1, ("PAC (presig) 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, tmp_blob, SRV_CHECKSUM,
359                                 context, server_keyblock);
360
361         /* Push the Server checksum out */
362         nt_status = ndr_push_struct_blob(&server_checksum_blob, mem_ctx, SRV_CHECKSUM,
363                                          (ndr_push_flags_fn_t)ndr_push_PAC_SIGNATURE_DATA);
364         if (!NT_STATUS_IS_OK(nt_status)) {
365                 DEBUG(1, ("PAC_SIGNATURE push failed: %s\n", nt_errstr(nt_status)));
366                 talloc_free(pac_data);
367                 return EINVAL;
368         }
369
370         /* Then sign Server checksum */
371         ret = make_pac_checksum(mem_ctx, server_checksum_blob, KDC_CHECKSUM, context, krbtgt_keyblock);
372         if (ret) {
373                 DEBUG(2, ("making krbtgt PAC checksum failed: %s\n", 
374                           smb_get_krb5_error_message(context, ret, mem_ctx)));
375                 talloc_free(pac_data);
376                 return ret;
377         }
378
379         /* And push it out again, this time to the world.  This relies on determanistic pointer values */
380         nt_status = ndr_push_struct_blob(&tmp_blob, mem_ctx, pac_data,
381                                          (ndr_push_flags_fn_t)ndr_push_PAC_DATA);
382         if (!NT_STATUS_IS_OK(nt_status)) {
383                 DEBUG(1, ("PAC (final) push failed: %s\n", nt_errstr(nt_status)));
384                 talloc_free(pac_data);
385                 return EINVAL;
386         }
387
388         *pac = tmp_blob;
389
390         talloc_free(pac_data);
391         return ret;
392 }
393