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