2 Unix SMB/CIFS implementation.
4 Create and parse the krb5 PAC
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
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.
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.
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.
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"
36 static NTSTATUS check_pac_checksum(TALLOC_CTX *mem_ctx,
38 struct PAC_SIGNATURE_DATA *sig,
40 krb5_keyblock *keyblock)
46 cksum.cksumtype = (CKSUMTYPE)sig->type;
47 cksum.checksum.length = sizeof(sig->signature);
48 cksum.checksum.data = sig->signature;
51 ret = krb5_crypto_init(context,
56 DEBUG(0,("krb5_crypto_init() failed\n"));
57 return NT_STATUS_FOOBAR;
59 ret = krb5_verify_checksum(context,
66 DEBUG(2, ("PAC Verification failed: %s\n",
67 smb_get_krb5_error_message(context, ret, mem_ctx)));
70 krb5_crypto_destroy(context, crypto);
73 return NT_STATUS_ACCESS_DENIED;
79 NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx,
80 struct PAC_LOGON_INFO **logon_info_out,
82 struct smb_krb5_context *smb_krb5_context,
83 krb5_keyblock *keyblock)
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);
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"));
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;
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) {
114 logon_info = &pac_data.buffers[i].info->logon_info;
116 case PAC_TYPE_SRV_CHECKSUM:
117 if (!pac_data.buffers[i].info) {
120 srv_sig_ptr = &pac_data.buffers[i].info->srv_cksum;
121 srv_sig = pac_data.buffers[i].info->srv_cksum;
123 case PAC_TYPE_KDC_CHECKSUM:
124 if (!pac_data.buffers[i].info) {
127 kdc_sig_ptr = &pac_data.buffers[i].info->kdc_cksum;
128 kdc_sig = pac_data.buffers[i].info->kdc_cksum;
130 case PAC_TYPE_LOGON_NAME:
138 DEBUG(0,("PAC no logon_info\n"));
139 return NT_STATUS_FOOBAR;
143 DEBUG(0,("PAC no srv_key\n"));
144 return NT_STATUS_FOOBAR;
148 DEBUG(0,("PAC no kdc_key\n"));
149 return NT_STATUS_FOOBAR;
152 memset(&modified_pac_blob.data[modified_pac_blob.length - 20],
154 memset(&modified_pac_blob.data[modified_pac_blob.length - 44],
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);
162 if (!NT_STATUS_IS_OK(status)) {
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;
173 static krb5_error_code make_pac_checksum(TALLOC_CTX *mem_ctx,
175 struct PAC_SIGNATURE_DATA *sig,
176 krb5_context context,
177 krb5_keyblock *keyblock)
184 ret = krb5_crypto_init(context,
189 DEBUG(0,("krb5_crypto_init() failed\n"));
192 ret = krb5_create_checksum(context,
200 DEBUG(2, ("PAC Verification failed: %s\n",
201 smb_get_krb5_error_message(context, ret, mem_ctx)));
204 krb5_crypto_destroy(context, crypto);
210 sig->type = cksum.cksumtype;
211 if (cksum.checksum.length == sizeof(sig->signature)) {
212 memcpy(sig->signature, cksum.checksum.data, sizeof(sig->signature));
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,
226 DATA_BLOB zero_blob = data_blob(NULL, 0);
227 DATA_BLOB tmp_blob = data_blob(NULL, 0);
228 DATA_BLOB server_checksum_blob;
230 struct PAC_DATA *pac_data = talloc(mem_ctx, struct PAC_DATA);
231 struct netr_SamBaseInfo *sam;
232 struct timeval tv = timeval_current();
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
246 pac_data->num_buffers = PAC_BUF_NUM_BUFFERS;
247 pac_data->version = 0;
249 pac_data->buffers = talloc_array(pac_data,
251 pac_data->num_buffers);
253 if (!pac_data->buffers) {
254 talloc_free(pac_data);
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,
262 nt_status = auth_convert_server_info_sambaseinfo(pac_data->buffers[0].info,
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);
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,
275 pac_data->buffers[PAC_BUF_LOGON_TYPE]._pad = 0;
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,
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,
288 pac_data->buffers[PAC_BUF_LOGON_NAME]._pad = 0;
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,
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);
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);
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,
309 pac_data->buffers[PAC_BUF_KDC_CHECKSUM]._pad = 0;
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,
315 ret = make_pac_checksum(mem_ctx, zero_blob,
316 &pac_data->buffers[PAC_BUF_SRV_CHECKSUM].info->srv_cksum,
317 context, server_keyblock);
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);
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,
329 pac_data->buffers[PAC_BUF_SRV_CHECKSUM]._pad = 0;
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);
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);
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);
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);
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);
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);
373 talloc_free(pac_data);