2 Unix SMB/CIFS implementation.
4 Validate the krb5 pac generation routines
6 Copyright (c) 2016 Andreas Schneider <asn@samba.org>
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 3 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.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "system/kerberos.h"
24 #include "system/time.h"
25 #include "torture/smbtorture.h"
26 #include "torture/winbind/proto.h"
27 #include "torture/krb5/proto.h"
28 #include "auth/credentials/credentials.h"
29 #include "lib/cmdline/cmdline.h"
30 #include "source4/auth/kerberos/kerberos.h"
31 #include "source4/auth/kerberos/kerberos_util.h"
32 #include "lib/util/util_net.h"
34 #define krb5_is_app_tag(dat,tag) \
35 ((dat != NULL) && (dat)->length && \
36 ((((dat)->data[0] & ~0x20) == ((tag) | 0x40))))
38 #define krb5_is_as_req(dat) krb5_is_app_tag(dat, 10)
39 #define krb5_is_as_rep(dat) krb5_is_app_tag(dat, 11)
40 #define krb5_is_krb_error(dat) krb5_is_app_tag(dat, 30)
42 enum torture_krb5_test
{
43 TORTURE_KRB5_TEST_PLAIN
,
44 TORTURE_KRB5_TEST_PAC_REQUEST
,
45 TORTURE_KRB5_TEST_BREAK_PW
,
46 TORTURE_KRB5_TEST_CLOCK_SKEW
,
47 TORTURE_KRB5_TEST_AES
,
48 TORTURE_KRB5_TEST_RC4
,
49 TORTURE_KRB5_TEST_AES_RC4
,
52 struct torture_krb5_context
{
53 struct torture_context
*tctx
;
54 krb5_context krb5_context
;
55 enum torture_krb5_test test
;
56 int recv_packet_count
;
61 krb5_error_code
decode_krb5_error(const krb5_data
*output
, krb5_error
**rep
);
63 krb5_error_code
decode_krb5_as_req(const krb5_data
*output
, krb5_kdc_req
**req
);
64 krb5_error_code
decode_krb5_as_rep(const krb5_data
*output
, krb5_kdc_rep
**rep
);
66 krb5_error_code
decode_krb5_padata_sequence(const krb5_data
*output
, krb5_pa_data
***rep
);
68 void krb5_free_kdc_req(krb5_context ctx
, krb5_kdc_req
*req
);
69 void krb5_free_kdc_rep(krb5_context ctx
, krb5_kdc_rep
*rep
);
70 void krb5_free_pa_data(krb5_context ctx
, krb5_pa_data
**data
);
72 static bool torture_check_krb5_as_req(struct torture_krb5_context
*test_context
,
74 const krb5_data
*message
)
79 code
= decode_krb5_as_req(message
, &test_context
->as_req
);
80 torture_assert_int_equal(test_context
->tctx
,
82 "decode_as_req failed");
83 torture_assert_int_equal(test_context
->tctx
,
84 test_context
->as_req
->msg_type
,
88 nktypes
= test_context
->as_req
->nktypes
;
89 torture_assert_int_not_equal(test_context
->tctx
,
96 static krb5_error_code
torture_krb5_pre_send_test(krb5_context context
,
98 const krb5_data
*realm
,
99 const krb5_data
*message
,
100 krb5_data
**new_message_out
,
101 krb5_data
**new_reply_out
)
104 struct torture_krb5_context
*test_context
=
105 (struct torture_krb5_context
*)data
;
107 switch (test_context
->test
)
109 case TORTURE_KRB5_TEST_PLAIN
:
110 case TORTURE_KRB5_TEST_PAC_REQUEST
:
111 case TORTURE_KRB5_TEST_BREAK_PW
:
112 case TORTURE_KRB5_TEST_CLOCK_SKEW
:
113 case TORTURE_KRB5_TEST_AES
:
114 case TORTURE_KRB5_TEST_RC4
:
115 case TORTURE_KRB5_TEST_AES_RC4
:
116 ok
= torture_check_krb5_as_req(test_context
,
120 return KRB5KDC_ERR_BADOPTION
;
129 * We need these function to validate packets because our torture macros
130 * do a 'return false' on error.
132 static bool torture_check_krb5_error(struct torture_krb5_context
*test_context
,
133 krb5_context context
,
134 const krb5_data
*reply
,
135 krb5_error_code error_code
,
139 krb5_error
*krb_error
;
140 krb5_error_code code
;
142 code
= decode_krb5_error(reply
, &krb_error
);
143 torture_assert_int_equal(test_context
->tctx
,
146 "decode_krb5_error failed");
148 torture_assert_int_equal(test_context
->tctx
,
150 error_code
- KRB5KDC_ERR_NONE
,
151 "Got wrong error code");
154 krb5_pa_data
**d
, **pa_data
= NULL
;
155 bool timestamp_found
= false;
157 torture_assert_int_not_equal(test_context
->tctx
,
158 krb_error
->e_data
.length
, 0,
159 "No e-data returned");
161 code
= decode_krb5_padata_sequence(&krb_error
->e_data
,
163 torture_assert_int_equal(test_context
->tctx
,
166 "decode_krb5_padata_sequence failed");
168 for (d
= pa_data
; d
!= NULL
; d
++) {
169 if ((*d
)->pa_type
== KRB5_PADATA_ENC_TIMESTAMP
) {
170 timestamp_found
= true;
174 torture_assert(test_context
->tctx
,
176 "Encrypted timestamp not found");
178 krb5_free_pa_data(context
, pa_data
);
181 krb5_free_error(context
, krb_error
);
186 static bool torture_check_krb5_as_rep(struct torture_krb5_context
*test_context
,
187 krb5_context context
,
188 const krb5_data
*reply
)
190 krb5_error_code code
;
193 code
= decode_krb5_as_rep(reply
, &test_context
->as_rep
);
194 torture_assert_int_equal(test_context
->tctx
,
197 "decode_krb5_as_rep failed");
199 torture_assert(test_context
->tctx
,
200 test_context
->as_rep
->ticket
->enc_part
.kvno
,
203 ok
= torture_setting_bool(test_context
->tctx
,
204 "expect_cached_at_rodc",
207 torture_assert_int_not_equal(test_context
->tctx
,
208 test_context
->as_rep
->ticket
->enc_part
.kvno
& 0xFFFF0000,
210 "Did not get a RODC number in the KVNO");
212 torture_assert_int_equal(test_context
->tctx
,
213 test_context
->as_rep
->ticket
->enc_part
.kvno
& 0xFFFF0000,
215 "Unexpecedly got a RODC number in the KVNO");
221 static bool torture_check_krb5_as_rep_enctype(struct torture_krb5_context
*test_context
,
222 krb5_context context
,
223 const krb5_data
*reply
,
224 krb5_enctype expected_enctype
)
226 krb5_enctype reply_enctype
;
229 ok
= torture_check_krb5_as_rep(test_context
,
236 reply_enctype
= test_context
->as_rep
->enc_part
.enctype
;
238 torture_assert_int_equal(test_context
->tctx
,
239 reply_enctype
, expected_enctype
,
240 "Ticket encrypted with invalid algorithm");
245 static krb5_error_code
torture_krb5_post_recv_test(krb5_context context
,
247 krb5_error_code kdc_code
,
248 const krb5_data
*realm
,
249 const krb5_data
*message
,
250 const krb5_data
*reply
,
251 krb5_data
**new_reply_out
)
253 struct torture_krb5_context
*test_context
=
254 (struct torture_krb5_context
*)data
;
255 krb5_error_code code
;
258 torture_comment(test_context
->tctx
,
259 "PACKET COUNT = %d\n",
260 test_context
->recv_packet_count
);
262 torture_comment(test_context
->tctx
,
263 "KRB5_AS_REP = %d\n",
264 krb5_is_as_req(reply
));
266 torture_comment(test_context
->tctx
,
268 krb5_is_krb_error(reply
));
270 torture_comment(test_context
->tctx
,
271 "KDC ERROR CODE = %d\n",
274 switch (test_context
->test
)
276 case TORTURE_KRB5_TEST_PLAIN
:
277 if (test_context
->recv_packet_count
== 0) {
278 ok
= torture_check_krb5_error(test_context
,
281 KRB5KDC_ERR_PREAUTH_REQUIRED
,
283 torture_assert_goto(test_context
->tctx
,
287 "torture_check_krb5_error failed");
289 ok
= torture_check_krb5_as_rep(test_context
,
292 torture_assert_goto(test_context
->tctx
,
296 "torture_check_krb5_as_rep failed");
299 torture_assert_goto(test_context
->tctx
,
300 test_context
->recv_packet_count
< 2,
306 case TORTURE_KRB5_TEST_PAC_REQUEST
:
307 if (test_context
->recv_packet_count
== 0) {
308 ok
= torture_check_krb5_error(test_context
,
311 KRB5KRB_ERR_RESPONSE_TOO_BIG
,
313 torture_assert_goto(test_context
->tctx
,
317 "torture_check_krb5_error failed");
318 } else if (test_context
->recv_packet_count
== 1) {
319 ok
= torture_check_krb5_error(test_context
,
322 KRB5KDC_ERR_PREAUTH_REQUIRED
,
324 torture_assert_goto(test_context
->tctx
,
328 "torture_check_krb5_error failed");
329 } else if (krb5_is_krb_error(reply
)) {
330 ok
= torture_check_krb5_error(test_context
,
333 KRB5KRB_ERR_RESPONSE_TOO_BIG
,
335 torture_assert_goto(test_context
->tctx
,
339 "torture_check_krb5_error failed");
341 ok
= torture_check_krb5_as_rep(test_context
,
344 torture_assert_goto(test_context
->tctx
,
348 "torture_check_krb5_as_rep failed");
351 torture_assert_goto(test_context
->tctx
,
352 test_context
->recv_packet_count
< 3,
357 case TORTURE_KRB5_TEST_BREAK_PW
:
358 if (test_context
->recv_packet_count
== 0) {
359 ok
= torture_check_krb5_error(test_context
,
362 KRB5KDC_ERR_PREAUTH_REQUIRED
,
364 torture_assert_goto(test_context
->tctx
,
368 "torture_check_krb5_error failed");
372 } else if (test_context
->recv_packet_count
== 1) {
373 ok
= torture_check_krb5_error(test_context
,
376 KRB5KDC_ERR_PREAUTH_FAILED
,
378 torture_assert_goto(test_context
->tctx
,
382 "torture_check_krb5_error failed");
385 torture_assert_goto(test_context
->tctx
,
386 test_context
->recv_packet_count
< 2,
391 case TORTURE_KRB5_TEST_CLOCK_SKEW
:
392 if (test_context
->recv_packet_count
== 0) {
393 ok
= torture_check_krb5_error(test_context
,
396 KRB5KDC_ERR_PREAUTH_REQUIRED
,
398 torture_assert_goto(test_context
->tctx
,
402 "torture_check_krb5_error failed");
406 } else if (test_context
->recv_packet_count
== 1) {
408 * This only works if kdc_timesync 0 is set in krb5.conf
410 * See commit 5f39a4438eafd693a3eb8366bbc3901efe62e538
411 * in the MIT Kerberos source tree.
413 ok
= torture_check_krb5_error(test_context
,
418 torture_assert_goto(test_context
->tctx
,
422 "torture_check_krb5_error failed");
425 torture_assert_goto(test_context
->tctx
,
426 test_context
->recv_packet_count
< 2,
431 case TORTURE_KRB5_TEST_AES
:
432 torture_comment(test_context
->tctx
, "TORTURE_KRB5_TEST_AES\n");
434 if (test_context
->recv_packet_count
== 0) {
435 ok
= torture_check_krb5_error(test_context
,
438 KRB5KDC_ERR_PREAUTH_REQUIRED
,
444 ok
= torture_check_krb5_as_rep_enctype(test_context
,
447 ENCTYPE_AES256_CTS_HMAC_SHA1_96
);
453 case TORTURE_KRB5_TEST_RC4
:
454 torture_comment(test_context
->tctx
, "TORTURE_KRB5_TEST_RC4\n");
456 if (test_context
->recv_packet_count
== 0) {
457 ok
= torture_check_krb5_error(test_context
,
460 KRB5KDC_ERR_PREAUTH_REQUIRED
,
466 ok
= torture_check_krb5_as_rep_enctype(test_context
,
469 ENCTYPE_ARCFOUR_HMAC
);
475 case TORTURE_KRB5_TEST_AES_RC4
:
476 torture_comment(test_context
->tctx
, "TORTURE_KRB5_TEST_AES_RC4\n");
478 if (test_context
->recv_packet_count
== 0) {
479 ok
= torture_check_krb5_error(test_context
,
482 KRB5KDC_ERR_PREAUTH_REQUIRED
,
488 ok
= torture_check_krb5_as_rep_enctype(test_context
,
491 ENCTYPE_AES256_CTS_HMAC_SHA1_96
);
506 krb5_free_kdc_req(test_context
->krb5_context
, test_context
->as_req
);
507 krb5_free_kdc_rep(test_context
->krb5_context
, test_context
->as_rep
);
509 test_context
->recv_packet_count
++;
514 static bool torture_krb5_init_context(struct torture_context
*tctx
,
515 enum torture_krb5_test test
,
516 struct smb_krb5_context
**smb_krb5_context
)
518 krb5_error_code code
;
520 struct torture_krb5_context
*test_context
= talloc_zero(tctx
,
521 struct torture_krb5_context
);
522 torture_assert(tctx
, test_context
!= NULL
, "Failed to allocate");
524 test_context
->test
= test
;
525 test_context
->tctx
= tctx
;
527 code
= smb_krb5_init_context(tctx
, tctx
->lp_ctx
, smb_krb5_context
);
528 torture_assert_int_equal(tctx
, code
, 0, "smb_krb5_init_context failed");
530 test_context
->krb5_context
= (*smb_krb5_context
)->krb5_context
;
532 krb5_set_kdc_send_hook((*smb_krb5_context
)->krb5_context
,
533 torture_krb5_pre_send_test
,
536 krb5_set_kdc_recv_hook((*smb_krb5_context
)->krb5_context
,
537 torture_krb5_post_recv_test
,
542 static bool torture_krb5_as_req_creds(struct torture_context
*tctx
,
543 struct cli_credentials
*credentials
,
544 enum torture_krb5_test test
)
546 krb5_get_init_creds_opt
*krb_options
= NULL
;
547 struct smb_krb5_context
*smb_krb5_context
;
548 enum credentials_obtained obtained
;
549 const char *error_string
;
550 const char *password
;
551 krb5_principal principal
;
552 krb5_error_code code
;
556 ok
= torture_krb5_init_context(tctx
, test
, &smb_krb5_context
);
557 torture_assert(tctx
, ok
, "torture_krb5_init_context failed");
559 code
= principal_from_credentials(tctx
,
565 torture_assert_int_equal(tctx
, code
, 0, error_string
);
567 password
= cli_credentials_get_password(credentials
);
571 case TORTURE_KRB5_TEST_PLAIN
:
573 case TORTURE_KRB5_TEST_PAC_REQUEST
:
574 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PAC_REQUEST
575 code
= krb5_get_init_creds_opt_alloc(smb_krb5_context
->krb5_context
,
577 torture_assert_int_equal(tctx
,
579 "krb5_get_init_creds_opt_alloc failed");
581 code
= krb5_get_init_creds_opt_set_pac_request(smb_krb5_context
->krb5_context
,
584 torture_assert_int_equal(tctx
,
586 "krb5_get_init_creds_opt_set_pac_request failed");
589 case TORTURE_KRB5_TEST_BREAK_PW
:
590 password
= "NOT the password";
592 case TORTURE_KRB5_TEST_CLOCK_SKEW
:
593 code
= krb5_set_real_time(smb_krb5_context
->krb5_context
,
596 torture_assert_int_equal(tctx
,
598 "krb5_set_real_time failed");
600 case TORTURE_KRB5_TEST_AES
: {
601 krb5_enctype etype
[] = { ENCTYPE_AES256_CTS_HMAC_SHA1_96
};
603 code
= krb5_get_init_creds_opt_alloc(smb_krb5_context
->krb5_context
,
605 torture_assert_int_equal(tctx
,
607 "krb5_get_init_creds_opt_alloc failed");
609 krb5_get_init_creds_opt_set_etype_list(krb_options
,
614 case TORTURE_KRB5_TEST_RC4
: {
615 krb5_enctype etype
[] = { ENCTYPE_ARCFOUR_HMAC
};
617 code
= krb5_get_init_creds_opt_alloc(smb_krb5_context
->krb5_context
,
619 torture_assert_int_equal(tctx
,
621 "krb5_get_init_creds_opt_alloc failed");
623 krb5_get_init_creds_opt_set_etype_list(krb_options
,
628 case TORTURE_KRB5_TEST_AES_RC4
: {
629 krb5_enctype etype
[] = { ENCTYPE_AES256_CTS_HMAC_SHA1_96
, ENCTYPE_ARCFOUR_HMAC
};
631 code
= krb5_get_init_creds_opt_alloc(smb_krb5_context
->krb5_context
,
633 torture_assert_int_equal(tctx
,
635 "krb5_get_init_creds_opt_alloc failed");
638 krb5_get_init_creds_opt_set_etype_list(krb_options
,
645 code
= krb5_get_init_creds_password(smb_krb5_context
->krb5_context
,
654 krb5_get_init_creds_opt_free(smb_krb5_context
->krb5_context
,
659 case TORTURE_KRB5_TEST_PLAIN
:
660 case TORTURE_KRB5_TEST_PAC_REQUEST
:
661 case TORTURE_KRB5_TEST_AES
:
662 case TORTURE_KRB5_TEST_RC4
:
663 case TORTURE_KRB5_TEST_AES_RC4
:
664 torture_assert_int_equal(tctx
,
667 "krb5_get_init_creds_password failed");
669 case TORTURE_KRB5_TEST_BREAK_PW
:
670 torture_assert_int_equal(tctx
,
672 KRB5KDC_ERR_PREAUTH_FAILED
,
673 "krb5_get_init_creds_password should "
676 case TORTURE_KRB5_TEST_CLOCK_SKEW
:
677 torture_assert_int_equal(tctx
,
680 "krb5_get_init_creds_password should "
685 krb5_free_cred_contents(smb_krb5_context
->krb5_context
,
691 static bool torture_krb5_as_req_cmdline(struct torture_context
*tctx
)
693 return torture_krb5_as_req_creds(tctx
,
694 samba_cmdline_get_creds(),
695 TORTURE_KRB5_TEST_PLAIN
);
698 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PAC_REQUEST
699 static bool torture_krb5_as_req_pac_request(struct torture_context
*tctx
)
703 ok
= torture_setting_bool(tctx
, "expect_rodc", false);
706 "This test needs further investigation in the "
707 "RODC case against a Windows DC, in particular "
708 "with non-cached users");
710 return torture_krb5_as_req_creds(tctx
, samba_cmdline_get_creds(),
711 TORTURE_KRB5_TEST_PAC_REQUEST
);
713 #endif /* HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PAC_REQUEST */
715 static bool torture_krb5_as_req_break_pw(struct torture_context
*tctx
)
717 return torture_krb5_as_req_creds(tctx
,
718 samba_cmdline_get_creds(),
719 TORTURE_KRB5_TEST_BREAK_PW
);
722 static bool torture_krb5_as_req_clock_skew(struct torture_context
*tctx
)
724 return torture_krb5_as_req_creds(tctx
,
725 samba_cmdline_get_creds(),
726 TORTURE_KRB5_TEST_CLOCK_SKEW
);
729 static bool torture_krb5_as_req_aes(struct torture_context
*tctx
)
731 return torture_krb5_as_req_creds(tctx
,
732 samba_cmdline_get_creds(),
733 TORTURE_KRB5_TEST_AES
);
736 static bool torture_krb5_as_req_rc4(struct torture_context
*tctx
)
738 return torture_krb5_as_req_creds(tctx
,
739 samba_cmdline_get_creds(),
740 TORTURE_KRB5_TEST_RC4
);
743 static bool torture_krb5_as_req_aes_rc4(struct torture_context
*tctx
)
745 return torture_krb5_as_req_creds(tctx
,
746 samba_cmdline_get_creds(),
747 TORTURE_KRB5_TEST_AES_RC4
);
750 NTSTATUS
torture_krb5_init(TALLOC_CTX
*ctx
)
752 struct torture_suite
*suite
=
753 torture_suite_create(ctx
, "krb5");
754 struct torture_suite
*kdc_suite
= torture_suite_create(suite
, "kdc");
755 suite
->description
= talloc_strdup(suite
, "Kerberos tests");
756 kdc_suite
->description
= talloc_strdup(kdc_suite
, "Kerberos KDC tests");
758 torture_suite_add_simple_test(kdc_suite
,
760 torture_krb5_as_req_cmdline
);
762 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PAC_REQUEST
763 /* Only available with MIT Kerveros 1.15 and newer */
764 torture_suite_add_simple_test(kdc_suite
, "as-req-pac-request",
765 torture_krb5_as_req_pac_request
);
768 torture_suite_add_simple_test(kdc_suite
, "as-req-break-pw",
769 torture_krb5_as_req_break_pw
);
771 /* This only works if kdc_timesync 0 is set in krb5.conf */
772 torture_suite_add_simple_test(kdc_suite
, "as-req-clock-skew",
773 torture_krb5_as_req_clock_skew
);
776 torture_suite_add_suite(kdc_suite
, torture_krb5_canon(kdc_suite
));
778 torture_suite_add_simple_test(kdc_suite
,
780 torture_krb5_as_req_aes
);
782 torture_suite_add_simple_test(kdc_suite
,
784 torture_krb5_as_req_rc4
);
786 torture_suite_add_simple_test(kdc_suite
,
788 torture_krb5_as_req_aes_rc4
);
790 torture_suite_add_suite(suite
, kdc_suite
);
792 torture_register_suite(ctx
, suite
);