2 Unix SMB/CIFS implementation.
4 Validate the krb5 pac generation routines
6 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2015
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.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "system/kerberos.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/popt_common.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 (((((char *)(dat)->data)[0] & ~0x20) == ((tag) | 0x40))))
38 #define krb5_is_krb_error(dat) krb5_is_app_tag(dat, 30)
40 enum torture_krb5_test {
41 TORTURE_KRB5_TEST_PLAIN,
42 TORTURE_KRB5_TEST_PAC_REQUEST,
43 TORTURE_KRB5_TEST_BREAK_PW,
44 TORTURE_KRB5_TEST_CLOCK_SKEW,
45 TORTURE_KRB5_TEST_AES,
46 TORTURE_KRB5_TEST_RC4,
47 TORTURE_KRB5_TEST_AES_RC4,
50 struct torture_krb5_context {
51 struct torture_context *tctx;
52 struct addrinfo *server;
53 enum torture_krb5_test test;
60 * Confirm that the outgoing packet meets certain expectations. This
61 * should be extended to further assert the correct and expected
62 * behaviour of the krb5 libs, so we know what we are sending to the
67 static bool torture_krb5_pre_send_test(struct torture_krb5_context *test_context, const krb5_data *send_buf)
70 switch (test_context->test)
72 case TORTURE_KRB5_TEST_PLAIN:
73 case TORTURE_KRB5_TEST_PAC_REQUEST:
74 case TORTURE_KRB5_TEST_BREAK_PW:
75 case TORTURE_KRB5_TEST_CLOCK_SKEW:
76 case TORTURE_KRB5_TEST_AES:
77 case TORTURE_KRB5_TEST_RC4:
78 case TORTURE_KRB5_TEST_AES_RC4:
79 torture_assert_int_equal(test_context->tctx,
80 decode_AS_REQ(send_buf->data, send_buf->length, &test_context->as_req, &used), 0,
81 "decode_AS_REQ failed");
82 torture_assert_int_equal(test_context->tctx, used, send_buf->length, "length mismatch");
83 torture_assert_int_equal(test_context->tctx, test_context->as_req.pvno, 5, "Got wrong as_req->pvno");
89 static bool torture_check_krb5_error(struct torture_krb5_context *test_context,
90 const krb5_data *reply,
91 krb5_error_code expected_error,
94 KRB_ERROR error = { 0 };
98 rc = decode_KRB_ERROR(reply->data, reply->length, &error, &used);
99 torture_assert_int_equal(test_context->tctx,
101 "decode_AS_REP failed");
103 torture_assert_int_equal(test_context->tctx,
106 torture_assert_int_equal(test_context->tctx,
108 "Got wrong error.pvno");
109 torture_assert_int_equal(test_context->tctx,
110 error.error_code, expected_error - KRB5KDC_ERR_NONE,
111 "Got wrong error.error_code");
118 torture_assert(test_context->tctx,
119 error.e_data != NULL,
120 "No e-data returned");
122 rc = decode_METHOD_DATA(error.e_data->data,
123 error.e_data->length,
126 torture_assert_int_equal(test_context->tctx,
128 "Got invalid method data");
130 torture_assert(test_context->tctx,
133 for (i = 0; i < m.len; i++) {
134 if (m.val[i].padata_type == KRB5_PADATA_ENC_TIMESTAMP) {
139 torture_assert(test_context->tctx,
141 "Encrypted timestamp not found");
144 free_KRB_ERROR(&error);
149 static bool torture_check_krb5_as_rep_enctype(struct torture_krb5_context *test_context,
150 const krb5_data *reply,
151 krb5_enctype expected_enctype)
153 ENCTYPE reply_enctype = { 0 };
157 rc = decode_AS_REP(reply->data,
159 &test_context->as_rep,
161 torture_assert_int_equal(test_context->tctx,
163 "decode_AS_REP failed");
164 torture_assert_int_equal(test_context->tctx,
167 torture_assert_int_equal(test_context->tctx,
168 test_context->as_rep.pvno, 5,
169 "Got wrong as_rep->pvno");
170 torture_assert_int_equal(test_context->tctx,
171 test_context->as_rep.ticket.tkt_vno, 5,
172 "Got wrong as_rep->ticket.tkt_vno");
173 torture_assert(test_context->tctx,
174 test_context->as_rep.ticket.enc_part.kvno,
175 "Did not get a KVNO in test_context->as_rep.ticket.enc_part.kvno");
177 reply_enctype = test_context->as_rep.enc_part.etype;
179 torture_assert_int_equal(test_context->tctx,
180 reply_enctype, expected_enctype,
181 "Ticket encrypted with invalid algorithm");
187 * Confirm that the incoming packet from the KDC meets certain
188 * expectations. This uses a switch and the packet count to work out
189 * what test we are in, and where in the test we are, so we can assert
190 * on the expected reply packets from the KDC.
194 static bool torture_krb5_post_recv_test(struct torture_krb5_context *test_context, const krb5_data *recv_buf)
200 switch (test_context->test)
202 case TORTURE_KRB5_TEST_PLAIN:
203 if (test_context->packet_count == 0) {
204 ok = torture_check_krb5_error(test_context,
206 KRB5KDC_ERR_PREAUTH_REQUIRED,
208 torture_assert(test_context->tctx,
210 "torture_check_krb5_error failed");
211 } else if ((decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used) == 0)
212 && (test_context->packet_count == 1)) {
213 torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch");
214 torture_assert_int_equal(test_context->tctx, error.pvno, 5, "Got wrong error.pvno");
215 torture_assert_int_equal(test_context->tctx, error.error_code, KRB5KRB_ERR_RESPONSE_TOO_BIG - KRB5KDC_ERR_NONE,
216 "Got wrong error.error_code");
217 free_KRB_ERROR(&error);
219 torture_assert_int_equal(test_context->tctx,
220 decode_AS_REP(recv_buf->data, recv_buf->length, &test_context->as_rep, &used), 0,
221 "decode_AS_REP failed");
222 torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch");
223 torture_assert_int_equal(test_context->tctx,
224 test_context->as_rep.pvno, 5,
225 "Got wrong as_rep->pvno");
226 torture_assert_int_equal(test_context->tctx,
227 test_context->as_rep.ticket.tkt_vno, 5,
228 "Got wrong as_rep->ticket.tkt_vno");
229 torture_assert(test_context->tctx,
230 test_context->as_rep.ticket.enc_part.kvno,
231 "Did not get a KVNO in test_context->as_rep.ticket.enc_part.kvno");
232 if (torture_setting_bool(test_context->tctx, "expect_cached_at_rodc", false)) {
233 torture_assert_int_not_equal(test_context->tctx,
234 *test_context->as_rep.ticket.enc_part.kvno & 0xFFFF0000,
235 0, "Did not get a RODC number in the KVNO");
237 torture_assert_int_equal(test_context->tctx,
238 *test_context->as_rep.ticket.enc_part.kvno & 0xFFFF0000,
239 0, "Unexpecedly got a RODC number in the KVNO");
241 free_AS_REP(&test_context->as_rep);
243 torture_assert(test_context->tctx, test_context->packet_count < 3, "too many packets");
244 free_AS_REQ(&test_context->as_req);
248 * Confirm correct error codes when we ask for the PAC. This behaviour is rather odd...
250 case TORTURE_KRB5_TEST_PAC_REQUEST:
251 if (test_context->packet_count == 0) {
252 ok = torture_check_krb5_error(test_context,
254 KRB5KRB_ERR_RESPONSE_TOO_BIG,
256 torture_assert(test_context->tctx,
258 "torture_check_krb5_error failed");
259 } else if (test_context->packet_count == 1) {
260 ok = torture_check_krb5_error(test_context,
262 KRB5KDC_ERR_PREAUTH_REQUIRED,
264 torture_assert(test_context->tctx,
266 "torture_check_krb5_error failed");
267 } else if ((decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used) == 0)
268 && (test_context->packet_count == 2)) {
269 torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch");
270 torture_assert_int_equal(test_context->tctx, error.pvno, 5, "Got wrong error.pvno");
271 torture_assert_int_equal(test_context->tctx, error.error_code, KRB5KRB_ERR_RESPONSE_TOO_BIG - KRB5KDC_ERR_NONE,
272 "Got wrong error.error_code");
273 free_KRB_ERROR(&error);
275 torture_assert_int_equal(test_context->tctx,
276 decode_AS_REP(recv_buf->data, recv_buf->length, &test_context->as_rep, &used), 0,
277 "decode_AS_REP failed");
278 torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch");
279 torture_assert_int_equal(test_context->tctx, test_context->as_rep.pvno, 5, "Got wrong as_rep->pvno");
280 free_AS_REP(&test_context->as_rep);
282 torture_assert(test_context->tctx, test_context->packet_count < 3, "too many packets");
283 free_AS_REQ(&test_context->as_req);
287 * Confirm correct error codes when we deliberatly send the wrong password
289 case TORTURE_KRB5_TEST_BREAK_PW:
290 if (test_context->packet_count == 0) {
291 ok = torture_check_krb5_error(test_context,
293 KRB5KDC_ERR_PREAUTH_REQUIRED,
295 torture_assert(test_context->tctx,
297 "torture_check_krb5_error failed");
298 } else if (test_context->packet_count == 1) {
299 ok = torture_check_krb5_error(test_context,
301 KRB5KDC_ERR_PREAUTH_FAILED,
303 torture_assert(test_context->tctx,
305 "torture_check_krb5_error failed");
307 torture_assert(test_context->tctx, test_context->packet_count < 2, "too many packets");
308 free_AS_REQ(&test_context->as_req);
312 * Confirm correct error codes when we deliberatly skew the client clock
314 case TORTURE_KRB5_TEST_CLOCK_SKEW:
315 if (test_context->packet_count == 0) {
316 ok = torture_check_krb5_error(test_context,
318 KRB5KDC_ERR_PREAUTH_REQUIRED,
320 torture_assert(test_context->tctx,
322 "torture_check_krb5_error failed");
323 } else if (test_context->packet_count == 1) {
324 ok = torture_check_krb5_error(test_context,
328 torture_assert(test_context->tctx,
330 "torture_check_krb5_error failed");
332 torture_assert(test_context->tctx, test_context->packet_count < 2, "too many packets");
333 free_AS_REQ(&test_context->as_req);
335 case TORTURE_KRB5_TEST_AES:
336 torture_comment(test_context->tctx, "TORTURE_KRB5_TEST_AES\n");
338 if (test_context->packet_count == 0) {
339 ok = torture_check_krb5_error(test_context,
341 KRB5KDC_ERR_PREAUTH_REQUIRED,
343 torture_assert(test_context->tctx,
345 "torture_check_krb5_error failed");
346 } else if (krb5_is_krb_error(recv_buf)) {
347 ok = torture_check_krb5_error(test_context,
349 KRB5KRB_ERR_RESPONSE_TOO_BIG,
351 torture_assert(test_context->tctx,
353 "torture_check_krb5_error failed");
355 ok = torture_check_krb5_as_rep_enctype(test_context,
357 KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96);
358 torture_assert(test_context->tctx,
360 "torture_check_krb5_as_rep_enctype failed");
363 torture_assert(test_context->tctx,
364 test_context->packet_count < 3,
367 case TORTURE_KRB5_TEST_RC4:
368 torture_comment(test_context->tctx, "TORTURE_KRB5_TEST_RC4\n");
370 if (test_context->packet_count == 0) {
371 ok = torture_check_krb5_error(test_context,
373 KRB5KDC_ERR_PREAUTH_REQUIRED,
375 torture_assert(test_context->tctx,
377 "torture_check_krb5_error failed");
378 } else if (krb5_is_krb_error(recv_buf)) {
379 ok = torture_check_krb5_error(test_context,
381 KRB5KRB_ERR_RESPONSE_TOO_BIG,
383 torture_assert(test_context->tctx,
385 "torture_check_krb5_error failed");
387 ok = torture_check_krb5_as_rep_enctype(test_context,
389 KRB5_ENCTYPE_ARCFOUR_HMAC_MD5);
390 torture_assert(test_context->tctx,
392 "torture_check_krb5_as_rep_enctype failed");
395 torture_assert(test_context->tctx,
396 test_context->packet_count < 3,
399 case TORTURE_KRB5_TEST_AES_RC4:
400 torture_comment(test_context->tctx, "TORTURE_KRB5_TEST_AES_RC4\n");
402 if (test_context->packet_count == 0) {
403 ok = torture_check_krb5_error(test_context,
405 KRB5KDC_ERR_PREAUTH_REQUIRED,
407 torture_assert(test_context->tctx,
409 "torture_check_krb5_error failed");
410 } else if (krb5_is_krb_error(recv_buf)) {
411 ok = torture_check_krb5_error(test_context,
413 KRB5KRB_ERR_RESPONSE_TOO_BIG,
415 torture_assert(test_context->tctx,
417 "torture_check_krb5_error failed");
419 ok = torture_check_krb5_as_rep_enctype(test_context,
421 KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96);
422 torture_assert(test_context->tctx,
424 "torture_check_krb5_as_rep_enctype failed");
427 torture_assert(test_context->tctx,
428 test_context->packet_count < 3,
437 * This function is set in torture_krb5_init_context as krb5
438 * send_and_recv function. This allows us to override what server the
439 * test is aimed at, and to inspect the packets just before they are
440 * sent to the network, and before they are processed on the recv
443 * The torture_krb5_pre_send_test() and torture_krb5_post_recv_test()
444 * functions are implement the actual tests.
446 * When this asserts, the caller will get a spurious 'cannot contact
450 static krb5_error_code smb_krb5_send_and_recv_func_override(krb5_context context,
451 void *data, /* struct torture_krb5_context */
452 krb5_krbhst_info *hi,
454 const krb5_data *send_buf,
457 krb5_error_code k5ret;
460 struct torture_krb5_context *test_context
461 = talloc_get_type_abort(data, struct torture_krb5_context);
463 ok = torture_krb5_pre_send_test(test_context, send_buf);
468 k5ret = smb_krb5_send_and_recv_func_forced(context, test_context->server,
469 hi, timeout, send_buf, recv_buf);
473 ok = torture_krb5_post_recv_test(test_context, recv_buf);
478 test_context->packet_count++;
483 static int test_context_destructor(struct torture_krb5_context *test_context)
485 freeaddrinfo(test_context->server);
490 static bool torture_krb5_init_context(struct torture_context *tctx,
491 enum torture_krb5_test test,
492 struct smb_krb5_context **smb_krb5_context)
494 const char *host = torture_setting_string(tctx, "host", NULL);
495 krb5_error_code k5ret;
498 struct torture_krb5_context *test_context = talloc_zero(tctx, struct torture_krb5_context);
499 torture_assert(tctx, test_context != NULL, "Failed to allocate");
501 test_context->test = test;
502 test_context->tctx = tctx;
504 k5ret = smb_krb5_init_context(tctx, tctx->lp_ctx, smb_krb5_context);
505 torture_assert_int_equal(tctx, k5ret, 0, "smb_krb5_init_context failed");
507 ok = interpret_string_addr_internal(&test_context->server, host, AI_NUMERICHOST);
508 torture_assert(tctx, ok, "Failed to parse target server");
510 talloc_set_destructor(test_context, test_context_destructor);
512 set_sockaddr_port(test_context->server->ai_addr, 88);
514 k5ret = krb5_set_send_to_kdc_func((*smb_krb5_context)->krb5_context,
515 smb_krb5_send_and_recv_func_override,
517 torture_assert_int_equal(tctx, k5ret, 0, "krb5_set_send_to_kdc_func failed");
521 static bool torture_krb5_as_req_creds(struct torture_context *tctx,
522 struct cli_credentials *credentials,
523 enum torture_krb5_test test)
525 krb5_error_code k5ret;
528 krb5_principal principal;
529 struct smb_krb5_context *smb_krb5_context;
530 enum credentials_obtained obtained;
531 const char *error_string;
532 const char *password = cli_credentials_get_password(credentials);
533 krb5_get_init_creds_opt *krb_options = NULL;
535 ok = torture_krb5_init_context(tctx, test, &smb_krb5_context);
536 torture_assert(tctx, ok, "torture_krb5_init_context failed");
538 k5ret = principal_from_credentials(tctx, credentials, smb_krb5_context,
539 &principal, &obtained, &error_string);
540 torture_assert_int_equal(tctx, k5ret, 0, error_string);
544 case TORTURE_KRB5_TEST_PLAIN:
547 case TORTURE_KRB5_TEST_PAC_REQUEST:
548 torture_assert_int_equal(tctx,
549 krb5_get_init_creds_opt_alloc(smb_krb5_context->krb5_context, &krb_options),
550 0, "krb5_get_init_creds_opt_alloc failed");
552 torture_assert_int_equal(tctx,
553 krb5_get_init_creds_opt_set_pac_request(smb_krb5_context->krb5_context, krb_options, true),
554 0, "krb5_get_init_creds_opt_set_pac_request failed");
557 case TORTURE_KRB5_TEST_BREAK_PW:
558 password = "NOT the password";
561 case TORTURE_KRB5_TEST_CLOCK_SKEW:
562 torture_assert_int_equal(tctx,
563 krb5_set_real_time(smb_krb5_context->krb5_context, time(NULL) + 3600, 0),
564 0, "krb5_set_real_time failed");
567 case TORTURE_KRB5_TEST_AES: {
568 krb5_enctype etype_list[] = { KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96 };
570 k5ret = krb5_get_init_creds_opt_alloc(smb_krb5_context->krb5_context,
572 torture_assert_int_equal(tctx,
574 "krb5_get_init_creds_opt_alloc failed");
576 krb5_get_init_creds_opt_set_etype_list(krb_options,
581 case TORTURE_KRB5_TEST_RC4: {
582 krb5_enctype etype_list[] = { KRB5_ENCTYPE_ARCFOUR_HMAC_MD5 };
584 k5ret = krb5_get_init_creds_opt_alloc(smb_krb5_context->krb5_context,
586 torture_assert_int_equal(tctx,
588 "krb5_get_init_creds_opt_alloc failed");
590 krb5_get_init_creds_opt_set_etype_list(krb_options,
595 case TORTURE_KRB5_TEST_AES_RC4: {
596 krb5_enctype etype_list[] = { KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96,
597 KRB5_ENCTYPE_ARCFOUR_HMAC_MD5 };
599 k5ret = krb5_get_init_creds_opt_alloc(smb_krb5_context->krb5_context,
601 torture_assert_int_equal(tctx,
603 "krb5_get_init_creds_opt_alloc failed");
605 krb5_get_init_creds_opt_set_etype_list(krb_options,
613 k5ret = krb5_get_init_creds_password(smb_krb5_context->krb5_context, &my_creds, principal,
614 password, NULL, NULL, 0,
616 krb5_get_init_creds_opt_free(smb_krb5_context->krb5_context, krb_options);
620 case TORTURE_KRB5_TEST_PLAIN:
621 case TORTURE_KRB5_TEST_PAC_REQUEST:
622 case TORTURE_KRB5_TEST_AES:
623 case TORTURE_KRB5_TEST_RC4:
624 case TORTURE_KRB5_TEST_AES_RC4:
625 torture_assert_int_equal(tctx, k5ret, 0, "krb5_get_init_creds_password failed");
628 case TORTURE_KRB5_TEST_BREAK_PW:
629 torture_assert_int_equal(tctx, k5ret, KRB5KDC_ERR_PREAUTH_FAILED, "krb5_get_init_creds_password should have failed");
632 case TORTURE_KRB5_TEST_CLOCK_SKEW:
633 torture_assert_int_equal(tctx, k5ret, KRB5KRB_AP_ERR_SKEW, "krb5_get_init_creds_password should have failed");
638 k5ret = krb5_free_cred_contents(smb_krb5_context->krb5_context, &my_creds);
639 torture_assert_int_equal(tctx, k5ret, 0, "krb5_free_creds failed");
644 static bool torture_krb5_as_req_cmdline(struct torture_context *tctx)
646 return torture_krb5_as_req_creds(tctx, cmdline_credentials, TORTURE_KRB5_TEST_PLAIN);
649 static bool torture_krb5_as_req_pac_request(struct torture_context *tctx)
651 if (torture_setting_bool(tctx, "expect_rodc", false)) {
652 torture_skip(tctx, "This test needs further investigation in the RODC case against a Windows DC, in particular with non-cached users");
654 return torture_krb5_as_req_creds(tctx, cmdline_credentials, TORTURE_KRB5_TEST_PAC_REQUEST);
657 static bool torture_krb5_as_req_break_pw(struct torture_context *tctx)
659 return torture_krb5_as_req_creds(tctx, cmdline_credentials, TORTURE_KRB5_TEST_BREAK_PW);
662 static bool torture_krb5_as_req_clock_skew(struct torture_context *tctx)
664 return torture_krb5_as_req_creds(tctx, cmdline_credentials, TORTURE_KRB5_TEST_CLOCK_SKEW);
667 static bool torture_krb5_as_req_aes(struct torture_context *tctx)
669 return torture_krb5_as_req_creds(tctx,
671 TORTURE_KRB5_TEST_AES);
674 static bool torture_krb5_as_req_rc4(struct torture_context *tctx)
676 return torture_krb5_as_req_creds(tctx,
678 TORTURE_KRB5_TEST_RC4);
681 static bool torture_krb5_as_req_aes_rc4(struct torture_context *tctx)
683 return torture_krb5_as_req_creds(tctx,
685 TORTURE_KRB5_TEST_AES_RC4);
688 NTSTATUS torture_krb5_init(TALLOC_CTX *ctx)
690 struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "krb5");
691 struct torture_suite *kdc_suite = torture_suite_create(suite, "kdc");
692 suite->description = talloc_strdup(suite, "Kerberos tests");
693 kdc_suite->description = talloc_strdup(kdc_suite, "Kerberos KDC tests");
695 torture_suite_add_simple_test(kdc_suite, "as-req-cmdline",
696 torture_krb5_as_req_cmdline);
698 torture_suite_add_simple_test(kdc_suite, "as-req-pac-request",
699 torture_krb5_as_req_pac_request);
701 torture_suite_add_simple_test(kdc_suite, "as-req-break-pw",
702 torture_krb5_as_req_break_pw);
704 torture_suite_add_simple_test(kdc_suite, "as-req-clock-skew",
705 torture_krb5_as_req_clock_skew);
707 torture_suite_add_simple_test(kdc_suite,
709 torture_krb5_as_req_aes);
711 torture_suite_add_simple_test(kdc_suite,
713 torture_krb5_as_req_rc4);
715 torture_suite_add_simple_test(kdc_suite,
717 torture_krb5_as_req_aes_rc4);
719 torture_suite_add_suite(kdc_suite, torture_krb5_canon(kdc_suite));
720 torture_suite_add_suite(suite, kdc_suite);
722 torture_register_suite(suite);