2 Unix SMB/CIFS implementation.
4 Validate the krb5 pac generation routines
6 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #include "system/kerberos.h"
26 #include "auth/auth.h"
27 #include "auth/kerberos/kerberos.h"
28 #include "librpc/gen_ndr/ndr_krb5pac.h"
29 #include "librpc/gen_ndr/ndr_samr.h"
31 static BOOL torture_pac_self_check(void)
34 TALLOC_CTX *mem_ctx = talloc_named(NULL, 0, "PAC self check");
36 struct PAC_DATA *pac_data;
37 struct PAC_LOGON_INFO *logon_info;
38 union netr_Validation validation;
40 /* Generate a nice, arbitary keyblock */
41 uint8_t server_bytes[16];
42 uint8_t krbtgt_bytes[16];
43 krb5_keyblock server_keyblock;
44 krb5_keyblock krbtgt_keyblock;
48 struct smb_krb5_context *smb_krb5_context;
50 struct auth_serversupplied_info *server_info;
51 struct auth_serversupplied_info *server_info_out;
53 ret = smb_krb5_init_context(mem_ctx, &smb_krb5_context);
60 generate_random_buffer(server_bytes, 16);
61 generate_random_buffer(krbtgt_bytes, 16);
63 ret = krb5_keyblock_init(smb_krb5_context->krb5_context,
65 server_bytes, sizeof(server_bytes),
68 printf("Server Keyblock encoding failed: %s\n",
69 smb_get_krb5_error_message(smb_krb5_context->krb5_context,
76 ret = krb5_keyblock_init(smb_krb5_context->krb5_context,
78 krbtgt_bytes, sizeof(krbtgt_bytes),
81 printf("KRBTGT Keyblock encoding failed: %s\n",
82 smb_get_krb5_error_message(smb_krb5_context->krb5_context,
85 krb5_free_keyblock_contents(smb_krb5_context->krb5_context,
91 /* We need an input, and this one requires no underlying database */
92 nt_status = auth_anonymous_server_info(mem_ctx, &server_info);
94 if (!NT_STATUS_IS_OK(nt_status)) {
95 krb5_free_keyblock_contents(smb_krb5_context->krb5_context,
97 krb5_free_keyblock_contents(smb_krb5_context->krb5_context,
103 /* OK, go ahead and make a PAC */
104 ret = kerberos_create_pac(mem_ctx, server_info,
105 smb_krb5_context->krb5_context,
111 printf("PAC encoding failed: %s\n",
112 smb_get_krb5_error_message(smb_krb5_context->krb5_context,
115 krb5_free_keyblock_contents(smb_krb5_context->krb5_context,
117 krb5_free_keyblock_contents(smb_krb5_context->krb5_context,
119 talloc_free(mem_ctx);
123 dump_data(10,tmp_blob.data,tmp_blob.length);
125 /* Now check that we can read it back */
126 nt_status = kerberos_decode_pac(mem_ctx, &pac_data,
132 if (!NT_STATUS_IS_OK(nt_status)) {
133 krb5_free_keyblock_contents(smb_krb5_context->krb5_context,
135 krb5_free_keyblock_contents(smb_krb5_context->krb5_context,
137 DEBUG(1, ("PAC decoding failed: %s\n",
138 nt_errstr(nt_status)));
140 talloc_free(mem_ctx);
144 /* Now check that we can read it back */
145 nt_status = kerberos_pac_logon_info(mem_ctx, &logon_info,
151 if (!NT_STATUS_IS_OK(nt_status)) {
152 krb5_free_keyblock_contents(smb_krb5_context->krb5_context,
154 krb5_free_keyblock_contents(smb_krb5_context->krb5_context,
156 printf("PAC decoding (for logon info) failed: %s\n",
157 nt_errstr(nt_status));
159 talloc_free(mem_ctx);
163 krb5_free_keyblock_contents(smb_krb5_context->krb5_context,
165 krb5_free_keyblock_contents(smb_krb5_context->krb5_context,
168 validation.sam3 = &logon_info->info3;
169 nt_status = make_server_info_netlogon_validation(mem_ctx,
173 if (!NT_STATUS_IS_OK(nt_status)) {
174 printf("PAC decoding (make server info) failed: %s\n",
175 nt_errstr(nt_status));
177 talloc_free(mem_ctx);
181 if (!dom_sid_equal(server_info->account_sid,
182 server_info_out->account_sid)) {
183 printf("PAC Decode resulted in *different* domain SID: %s != %s\n",
184 dom_sid_string(mem_ctx, server_info->account_sid),
185 dom_sid_string(mem_ctx, server_info_out->account_sid));
186 talloc_free(mem_ctx);
190 talloc_free(mem_ctx);
195 /* This is the PAC generated on my test network, by my test Win2k3 server.
196 -- abartlet 2005-07-04
199 static const char saved_pac[] = {
200 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xd8, 0x01, 0x00, 0x00,
201 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
202 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
203 0x40, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
204 0x58, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x08, 0x00, 0xcc, 0xcc, 0xcc, 0xcc,
205 0xc8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x30, 0xdf, 0xa6, 0xcb,
206 0x4f, 0x7d, 0xc5, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff,
207 0xff, 0xff, 0xff, 0x7f, 0xc0, 0x3c, 0x4e, 0x59, 0x62, 0x73, 0xc5, 0x01, 0xc0, 0x3c, 0x4e, 0x59,
208 0x62, 0x73, 0xc5, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x16, 0x00, 0x16, 0x00,
209 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
210 0x0c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
211 0x14, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x02, 0x00, 0x65, 0x00, 0x00, 0x00,
212 0xed, 0x03, 0x00, 0x00, 0x04, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x02, 0x00,
213 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
214 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x16, 0x00, 0x20, 0x00, 0x02, 0x00, 0x16, 0x00, 0x18, 0x00,
215 0x24, 0x00, 0x02, 0x00, 0x28, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
216 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
217 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
218 0x01, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
219 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
220 0x57, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x33, 0x00, 0x46, 0x00, 0x49, 0x00, 0x4e, 0x00,
221 0x41, 0x00, 0x4c, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
222 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
223 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
224 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
225 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
226 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x57, 0x00, 0x32, 0x00,
227 0x30, 0x00, 0x30, 0x00, 0x33, 0x00, 0x46, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x41, 0x00, 0x4c, 0x00,
228 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x57, 0x00, 0x49, 0x00,
229 0x4e, 0x00, 0x32, 0x00, 0x4b, 0x00, 0x33, 0x00, 0x54, 0x00, 0x48, 0x00, 0x49, 0x00, 0x4e, 0x00,
230 0x4b, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
231 0x15, 0x00, 0x00, 0x00, 0x11, 0x2f, 0xaf, 0xb5, 0x90, 0x04, 0x1b, 0xec, 0x50, 0x3b, 0xec, 0xdc,
232 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
233 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
234 0x80, 0x66, 0x28, 0xea, 0x37, 0x80, 0xc5, 0x01, 0x16, 0x00, 0x77, 0x00, 0x32, 0x00, 0x30, 0x00,
235 0x30, 0x00, 0x33, 0x00, 0x66, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x24, 0x00,
236 0x76, 0xff, 0xff, 0xff, 0x37, 0xd5, 0xb0, 0xf7, 0x24, 0xf0, 0xd6, 0xd4, 0xec, 0x09, 0x86, 0x5a,
237 0xa0, 0xe8, 0xc3, 0xa9, 0x00, 0x00, 0x00, 0x00, 0x76, 0xff, 0xff, 0xff, 0xb4, 0xd8, 0xb8, 0xfe,
238 0x83, 0xb3, 0x13, 0x3f, 0xfc, 0x5c, 0x41, 0xad, 0xe2, 0x64, 0x83, 0xe0, 0x00, 0x00, 0x00, 0x00
241 /* Check with a known 'well formed' PAC, from my test server */
242 static BOOL torture_pac_saved_check(void)
245 TALLOC_CTX *mem_ctx = talloc_named(NULL, 0, "PAC saved check");
246 DATA_BLOB tmp_blob, validate_blob;
247 struct PAC_DATA *pac_data;
248 struct PAC_LOGON_INFO *logon_info;
249 union netr_Validation validation;
250 const char *pac_file, *pac_kdc_key, *pac_member_key;
252 struct auth_serversupplied_info *server_info_out;
254 krb5_keyblock server_keyblock;
255 krb5_keyblock krbtgt_keyblock;
256 struct samr_Password *krbtgt_bytes, *krbsrv_bytes;
260 struct smb_krb5_context *smb_krb5_context;
262 ret = smb_krb5_init_context(mem_ctx, &smb_krb5_context);
265 talloc_free(mem_ctx);
269 pac_kdc_key = lp_parm_string(-1,"torture","pac_kdc_key");
270 if (pac_kdc_key == NULL) {
271 pac_kdc_key = "B286757148AF7FD252C53603A150B7E7";
274 pac_member_key = lp_parm_string(-1,"torture","pac_member_key");
275 if (pac_member_key == NULL) {
276 pac_member_key = "D217FAEAE5E6B5F95CCC94077AB8A5FC";
279 printf("Using pac_kdc_key '%s'\n", pac_kdc_key);
280 printf("Using pac_member_key '%s'\n", pac_member_key);
282 /* The krbtgt key in use when the above PAC was generated.
283 * This is an arcfour-hmac-md5 key, extracted with our 'net
285 krbtgt_bytes = smbpasswd_gethexpwd(mem_ctx, pac_kdc_key);
287 DEBUG(0, ("Could not interpret krbtgt key"));
288 talloc_free(mem_ctx);
292 krbsrv_bytes = smbpasswd_gethexpwd(mem_ctx, pac_member_key);
294 DEBUG(0, ("Could not interpret krbsrv key"));
295 talloc_free(mem_ctx);
299 ret = krb5_keyblock_init(smb_krb5_context->krb5_context,
300 ENCTYPE_ARCFOUR_HMAC,
301 krbsrv_bytes->hash, sizeof(krbsrv_bytes->hash),
304 DEBUG(1, ("Server Keyblock encoding failed: %s\n",
305 smb_get_krb5_error_message(smb_krb5_context->krb5_context,
308 talloc_free(mem_ctx);
312 ret = krb5_keyblock_init(smb_krb5_context->krb5_context,
313 ENCTYPE_ARCFOUR_HMAC,
314 krbtgt_bytes->hash, sizeof(krbtgt_bytes->hash),
317 DEBUG(1, ("Server Keyblock encoding failed: %s\n",
318 smb_get_krb5_error_message(smb_krb5_context->krb5_context,
321 krb5_free_keyblock_contents(smb_krb5_context->krb5_context,
323 talloc_free(mem_ctx);
327 pac_file = lp_parm_string(-1,"torture","pac_file");
329 tmp_blob.data = file_load(pac_file, &tmp_blob.length, mem_ctx);
330 printf("Loaded pac of size %d from %s\n", tmp_blob.length, pac_file);
332 tmp_blob = data_blob(saved_pac, sizeof(saved_pac));
333 file_save("x.dat", tmp_blob.data, tmp_blob.length);
336 dump_data(10,tmp_blob.data,tmp_blob.length);
338 /* Decode and verify the signaure on the PAC */
339 nt_status = kerberos_decode_pac(mem_ctx, &pac_data,
344 if (!NT_STATUS_IS_OK(nt_status)) {
345 DEBUG(1, ("PAC decoding failed: %s\n",
346 nt_errstr(nt_status)));
348 krb5_free_keyblock_contents(smb_krb5_context->krb5_context,
350 krb5_free_keyblock_contents(smb_krb5_context->krb5_context,
352 talloc_free(mem_ctx);
356 /* Parse the PAC again, for the logon info this time */
357 nt_status = kerberos_pac_logon_info(mem_ctx, &logon_info,
363 if (!NT_STATUS_IS_OK(nt_status)) {
364 krb5_free_keyblock_contents(smb_krb5_context->krb5_context,
366 krb5_free_keyblock_contents(smb_krb5_context->krb5_context,
368 printf("PAC decoding (for logon info) failed: %s\n",
369 nt_errstr(nt_status));
371 talloc_free(mem_ctx);
375 validation.sam3 = &logon_info->info3;
376 nt_status = make_server_info_netlogon_validation(mem_ctx,
380 if (!NT_STATUS_IS_OK(nt_status)) {
381 krb5_free_keyblock_contents(smb_krb5_context->krb5_context,
383 krb5_free_keyblock_contents(smb_krb5_context->krb5_context,
386 printf("PAC decoding (make server info) failed: %s\n",
387 nt_errstr(nt_status));
389 talloc_free(mem_ctx);
394 !dom_sid_equal(dom_sid_parse_talloc(mem_ctx, "S-1-5-21-3048156945-3961193616-3706469200-1005"),
395 server_info_out->account_sid)) {
396 krb5_free_keyblock_contents(smb_krb5_context->krb5_context,
398 krb5_free_keyblock_contents(smb_krb5_context->krb5_context,
401 printf("PAC Decode resulted in *different* domain SID: %s != %s\n",
402 "S-1-5-21-3048156945-3961193616-3706469200-1005",
403 dom_sid_string(mem_ctx, server_info_out->account_sid));
404 talloc_free(mem_ctx);
408 ret = kerberos_encode_pac(mem_ctx,
410 smb_krb5_context->krb5_context,
416 krb5_free_keyblock_contents(smb_krb5_context->krb5_context,
418 krb5_free_keyblock_contents(smb_krb5_context->krb5_context,
421 DEBUG(0, ("PAC push failed\n"));
422 talloc_free(mem_ctx);
426 dump_data(10,validate_blob.data,validate_blob.length);
428 /* compare both the length and the data bytes after a
429 * pull/push cycle. This ensures we use the exact same
430 * pointer, padding etc algorithms as win2k3.
432 if (tmp_blob.length != validate_blob.length) {
433 krb5_free_keyblock_contents(smb_krb5_context->krb5_context,
435 krb5_free_keyblock_contents(smb_krb5_context->krb5_context,
438 DEBUG(0, ("PAC push failed: original buffer length[%u] != created buffer length[%u]\n",
439 (unsigned)tmp_blob.length, (unsigned)validate_blob.length));
440 talloc_free(mem_ctx);
444 if (memcmp(tmp_blob.data, validate_blob.data, tmp_blob.length) != 0) {
445 krb5_free_keyblock_contents(smb_krb5_context->krb5_context,
447 krb5_free_keyblock_contents(smb_krb5_context->krb5_context,
450 DEBUG(0, ("PAC push failed: length[%u] matches, but data does not\n",
451 (unsigned)tmp_blob.length));
452 talloc_free(mem_ctx);
456 /* Finally... Bugger up the signature, and check we fail the checksum */
457 tmp_blob.data[tmp_blob.length - 2]++;
459 nt_status = kerberos_decode_pac(mem_ctx, &pac_data,
464 if (NT_STATUS_IS_OK(nt_status)) {
465 DEBUG(1, ("PAC decoding DID NOT fail on broken checksum\n"));
467 krb5_free_keyblock_contents(smb_krb5_context->krb5_context,
469 krb5_free_keyblock_contents(smb_krb5_context->krb5_context,
471 talloc_free(mem_ctx);
475 krb5_free_keyblock_contents(smb_krb5_context->krb5_context,
477 krb5_free_keyblock_contents(smb_krb5_context->krb5_context,
480 talloc_free(mem_ctx);
484 BOOL torture_pac(void)
487 ret &= torture_pac_self_check();
488 ret &= torture_pac_saved_check();