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/krb5/proto.h"
27 #include "auth/credentials/credentials.h"
28 #include "lib/cmdline/popt_common.h"
29 #include "source4/auth/kerberos/kerberos.h"
30 #include "source4/auth/kerberos/kerberos_util.h"
31 #include "lib/util/util_net.h"
33 #define TEST_CANONICALIZE 0x0000001
34 #define TEST_ENTERPRISE 0x0000002
35 #define TEST_UPPER_REALM 0x0000004
36 #define TEST_UPPER_USERNAME 0x0000008
37 #define TEST_NETBIOS_REALM 0x0000010
38 #define TEST_ALL 0x000001F
41 struct smb_krb5_context *smb_krb5_context;
43 const char *real_realm;
51 struct torture_krb5_context {
52 struct torture_context *tctx;
53 struct addrinfo *server;
54 struct test_data *test_data;
62 * Confirm that the outgoing packet meets certain expectations. This
63 * should be extended to further assert the correct and expected
64 * behaviour of the krb5 libs, so we know what we are sending to the
67 * Additionally, this CHANGES the request to remove the canonicalize
68 * flag automatically added by the krb5 libs when an enterprise
69 * principal is used, so we can test what the server does in this
74 static bool torture_krb5_pre_send_test(struct torture_krb5_context *test_context, const krb5_data *send_buf, krb5_data *modified_send_buf)
76 krb5_error_code k5ret;
78 torture_assert_int_equal(test_context->tctx,
79 decode_AS_REQ(send_buf->data, send_buf->length, &test_context->as_req, &used), 0,
80 "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");
84 if (test_context->test_data->canonicalize || test_context->test_data->enterprise) {
85 torture_assert(test_context->tctx, test_context->as_req.req_body.kdc_options.canonicalize, "krb5 libs did not set canonicalize!");
87 torture_assert_int_equal(test_context->tctx, test_context->as_req.req_body.kdc_options.canonicalize, false, "krb5 libs unexpectedly set canonicalize!");
90 if (test_context->test_data->enterprise) {
91 torture_assert_int_equal(test_context->tctx, test_context->as_req.req_body.cname->name_type, KRB5_NT_ENTERPRISE_PRINCIPAL, "krb5 libs did not pass principal as enterprise!");
93 torture_assert_int_equal(test_context->tctx, test_context->as_req.req_body.cname->name_type, KRB5_NT_PRINCIPAL, "krb5 libs unexpectedly set principal as enterprise!");
96 /* Force off canonicalize that was forced on by the krb5 libs */
97 if (test_context->test_data->canonicalize == false && test_context->test_data->enterprise) {
98 test_context->as_req.req_body.kdc_options.canonicalize = false;
101 ASN1_MALLOC_ENCODE(AS_REQ, modified_send_buf->data, modified_send_buf->length,
102 &test_context->as_req, &used, k5ret);
103 torture_assert_int_equal(test_context->tctx,
105 "encode_AS_REQ failed");
106 torture_assert_int_equal(test_context->tctx, used, send_buf->length, "re-encode length mismatch");
111 * Confirm that the incoming packet from the KDC meets certain
112 * expectations. This uses a packet count to work out what test we
113 * are in, and where in the test we are, so we can assert on the
114 * expected reply packets from the KDC.
118 static bool torture_krb5_post_recv_test(struct torture_krb5_context *test_context, const krb5_data *recv_buf)
122 if (test_context->packet_count == 0) {
123 torture_assert_int_equal(test_context->tctx,
124 decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used), 0,
125 "decode_AS_REP failed");
126 torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch");
127 torture_assert_int_equal(test_context->tctx, error.pvno, 5, "Got wrong error.pvno");
128 torture_assert_int_equal(test_context->tctx, error.error_code, KRB5KDC_ERR_PREAUTH_REQUIRED - KRB5KDC_ERR_NONE,
129 "Got wrong error.error_code");
130 free_KRB_ERROR(&error);
131 } else if ((decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used) == 0)
132 && (test_context->packet_count == 1)) {
133 torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch");
134 torture_assert_int_equal(test_context->tctx, error.pvno, 5, "Got wrong error.pvno");
135 torture_assert_int_equal(test_context->tctx, error.error_code, KRB5KRB_ERR_RESPONSE_TOO_BIG - KRB5KDC_ERR_NONE,
136 "Got wrong error.error_code");
137 free_KRB_ERROR(&error);
139 torture_assert_int_equal(test_context->tctx,
140 decode_AS_REP(recv_buf->data, recv_buf->length, &test_context->as_rep, &used), 0,
141 "decode_AS_REP failed");
142 torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch");
143 torture_assert_int_equal(test_context->tctx,
144 test_context->as_rep.pvno, 5,
145 "Got wrong as_rep->pvno");
146 torture_assert_int_equal(test_context->tctx,
147 test_context->as_rep.ticket.tkt_vno, 5,
148 "Got wrong as_rep->ticket.tkt_vno");
149 torture_assert(test_context->tctx,
150 test_context->as_rep.ticket.enc_part.kvno,
151 "Did not get a KVNO in test_context->as_rep.ticket.enc_part.kvno");
152 if (torture_setting_bool(test_context->tctx, "expect_cached_at_rodc", false)) {
153 torture_assert_int_not_equal(test_context->tctx,
154 *test_context->as_rep.ticket.enc_part.kvno & 0xFFFF0000,
155 0, "Did not get a RODC number in the KVNO");
157 torture_assert_int_equal(test_context->tctx,
158 *test_context->as_rep.ticket.enc_part.kvno & 0xFFFF0000,
159 0, "Unexpecedly got a RODC number in the KVNO");
161 free_AS_REP(&test_context->as_rep);
163 torture_assert(test_context->tctx, test_context->packet_count < 3, "too many packets");
164 free_AS_REQ(&test_context->as_req);
169 * This function is set in torture_krb5_init_context_canon as krb5
170 * send_and_recv function. This allows us to override what server the
171 * test is aimed at, and to inspect the packets just before they are
172 * sent to the network, and before they are processed on the recv
175 * The torture_krb5_pre_send_test() and torture_krb5_post_recv_test()
176 * functions are implement the actual tests.
178 * When this asserts, the caller will get a spurious 'cannot contact
182 static krb5_error_code smb_krb5_send_and_recv_func_canon_override(krb5_context context,
183 void *data, /* struct torture_krb5_context */
184 krb5_krbhst_info *hi,
186 const krb5_data *send_buf,
189 krb5_error_code k5ret;
191 krb5_data modified_send_buf;
193 struct torture_krb5_context *test_context
194 = talloc_get_type_abort(data, struct torture_krb5_context);
196 ok = torture_krb5_pre_send_test(test_context, send_buf, &modified_send_buf);
201 k5ret = smb_krb5_send_and_recv_func_forced(context, test_context->server,
202 hi, timeout, &modified_send_buf, recv_buf);
204 ok = torture_krb5_post_recv_test(test_context, recv_buf);
209 test_context->packet_count++;
214 static int test_context_destructor(struct torture_krb5_context *test_context)
216 freeaddrinfo(test_context->server);
221 static bool torture_krb5_init_context_canon(struct torture_context *tctx,
222 struct test_data *test_data,
223 struct smb_krb5_context **smb_krb5_context)
225 const char *host = torture_setting_string(tctx, "host", NULL);
226 krb5_error_code k5ret;
229 struct torture_krb5_context *test_context = talloc_zero(tctx, struct torture_krb5_context);
230 torture_assert(tctx, test_context != NULL, "Failed to allocate");
232 test_context->test_data = test_data;
233 test_context->tctx = tctx;
235 k5ret = smb_krb5_init_context(tctx, tctx->lp_ctx, smb_krb5_context);
236 torture_assert_int_equal(tctx, k5ret, 0, "smb_krb5_init_context failed");
238 ok = interpret_string_addr_internal(&test_context->server, host, AI_NUMERICHOST);
239 torture_assert(tctx, ok, "Failed to parse target server");
241 talloc_set_destructor(test_context, test_context_destructor);
243 set_sockaddr_port(test_context->server->ai_addr, 88);
245 k5ret = krb5_set_send_to_kdc_func((*smb_krb5_context)->krb5_context,
246 smb_krb5_send_and_recv_func_canon_override,
248 torture_assert_int_equal(tctx, k5ret, 0, "krb5_set_send_to_kdc_func failed");
253 static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void *tcase_data)
255 krb5_error_code k5ret;
256 krb5_get_init_creds_opt *krb_options = NULL;
257 struct test_data *test_data = talloc_get_type_abort(tcase_data, struct test_data);
259 char *upper_real_realm;
261 krb5_principal principal;
262 krb5_principal expected_principal;
263 char *principal_string;
265 char *expected_principal_string;
266 int expected_principal_flags;
267 char *got_principal_string;
268 char *assertion_message;
269 const char *password = cli_credentials_get_password(cmdline_credentials);
270 struct smb_krb5_context *smb_krb5_context;
274 ok = torture_krb5_init_context_canon(tctx, test_data, &smb_krb5_context);
275 torture_assert(tctx, ok, "torture_krb5_init_context failed");
277 if (test_data->upper_realm) {
278 realm = strupper_talloc(test_data, test_data->realm);
280 realm = strlower_talloc(test_data, test_data->realm);
282 if (test_data->upper_username) {
283 username = strupper_talloc(test_data, test_data->username);
285 username = talloc_strdup(test_data, test_data->username);
288 principal_string = talloc_asprintf(test_data, "%s@%s", username, realm);
290 upper_real_realm = strupper_talloc(test_data, test_data->real_realm);
293 * If we are set to canonicalize, we get back the fixed UPPER
294 * case realm, and the real username (ie matching LDAP
297 * Otherwise, if we are set to enterprise, we
298 * get back the whole principal as-sent
300 * Finally, if we are not set to canonicalize, we get back the
301 * fixed UPPER case realm, but the as-sent username
303 if (test_data->canonicalize) {
304 expected_principal_string = talloc_asprintf(test_data, "%s@%s", test_data->username, upper_real_realm);
305 } else if (test_data->enterprise) {
306 expected_principal_string = principal_string;
308 expected_principal_string = talloc_asprintf(test_data, "%s@%s", username, upper_real_realm);
311 if (test_data->enterprise) {
312 principal_flags = KRB5_PRINCIPAL_PARSE_ENTERPRISE;
317 if (test_data->canonicalize) {
318 expected_principal_flags = 0;
320 expected_principal_flags = principal_flags;
323 torture_assert_int_equal(tctx,
324 krb5_parse_name_flags(smb_krb5_context->krb5_context,
328 0, "krb5_parse_name_flags failed");
329 torture_assert_int_equal(tctx,
330 krb5_parse_name_flags(smb_krb5_context->krb5_context,
331 expected_principal_string,
332 expected_principal_flags,
333 &expected_principal),
334 0, "krb5_parse_name_flags failed");
337 * Set the canonicalize flag if this test requires it
339 torture_assert_int_equal(tctx,
340 krb5_get_init_creds_opt_alloc(smb_krb5_context->krb5_context, &krb_options),
341 0, "krb5_get_init_creds_opt_alloc failed");
343 torture_assert_int_equal(tctx,
344 krb5_get_init_creds_opt_set_canonicalize(smb_krb5_context->krb5_context, krb_options, test_data->canonicalize),
345 0, "krb5_get_init_creds_opt_set_canonicalize failed");
347 k5ret = krb5_get_init_creds_password(smb_krb5_context->krb5_context, &my_creds, principal,
348 password, NULL, NULL, 0,
350 krb5_get_init_creds_opt_free(smb_krb5_context->krb5_context, krb_options);
352 assertion_message = talloc_asprintf(tctx,
353 "krb5_get_init_creds_password for %s failed: %s",
355 smb_get_krb5_error_message(smb_krb5_context->krb5_context, k5ret, tctx));
356 torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
359 * Assert that the reply was with the correct type of
360 * principal, depending on the flags we set
362 if (test_data->canonicalize == false && test_data->enterprise) {
363 torture_assert_int_equal(tctx,
364 krb5_principal_get_type(smb_krb5_context->krb5_context,
365 my_creds.client), KRB5_NT_ENTERPRISE_PRINCIPAL,
366 "smb_krb5_init_context gave incorrect client->name.name_type");
368 torture_assert_int_equal(tctx,
369 krb5_principal_get_type(smb_krb5_context->krb5_context,
370 my_creds.client), KRB5_NT_PRINCIPAL,
371 "smb_krb5_init_context gave incorrect client->name.name_type");
374 torture_assert_int_equal(tctx,
375 krb5_unparse_name(smb_krb5_context->krb5_context,
376 my_creds.client, &got_principal_string), 0,
377 "krb5_unparse_name failed");
379 assertion_message = talloc_asprintf(tctx,
380 "krb5_get_init_creds_password returned a different principal %s to what was expected %s",
381 got_principal_string, expected_principal_string);
382 krb5_free_unparsed_name(smb_krb5_context->krb5_context, got_principal_string);
384 torture_assert(tctx, krb5_principal_compare(smb_krb5_context->krb5_context,
385 my_creds.client, expected_principal),
388 torture_assert_int_equal(tctx,
389 krb5_principal_get_type(smb_krb5_context->krb5_context,
390 my_creds.server), KRB5_NT_SRV_INST,
391 "smb_krb5_init_context gave incorrect client->name.name_type");
393 torture_assert_str_equal(tctx, krb5_principal_get_comp_string(smb_krb5_context->krb5_context,
396 "smb_krb5_init_context gave incorrect my_creds.server->name.name_string[0]");
398 krb5_free_principal(smb_krb5_context->krb5_context, principal);
400 k5ret = krb5_free_cred_contents(smb_krb5_context->krb5_context, &my_creds);
401 torture_assert_int_equal(tctx, k5ret, 0, "krb5_free_creds failed");
406 struct torture_suite *torture_krb5_canon(TALLOC_CTX *mem_ctx)
409 struct torture_suite *suite = torture_suite_create(mem_ctx, "canon");
410 suite->description = talloc_strdup(suite, "Kerberos Canonicalisation tests");
412 for (i = 0; i < TEST_ALL; i++) {
413 char *name = talloc_asprintf(suite, "%s.%s.%s.%s.%s",
414 (i & TEST_CANONICALIZE) ? "canon" : "no-canon",
415 (i & TEST_ENTERPRISE) ? "enterprise" : "no-enterprise",
416 (i & TEST_UPPER_REALM) ? "uc-realm" : "lc-realm",
417 (i & TEST_UPPER_USERNAME) ? "uc-user" : "lc-user",
418 (i & TEST_NETBIOS_REALM) ? "netbios-realm" : "krb5-realm");
420 struct test_data *test_data = talloc(suite, struct test_data);
421 if (i & TEST_NETBIOS_REALM) {
422 test_data->realm = cli_credentials_get_domain(cmdline_credentials);
424 test_data->realm = cli_credentials_get_realm(cmdline_credentials);
426 test_data->real_realm = cli_credentials_get_realm(cmdline_credentials);
427 test_data->username = cli_credentials_get_username(cmdline_credentials);
428 test_data->canonicalize = (i & TEST_CANONICALIZE) != 0;
429 test_data->enterprise = (i & TEST_ENTERPRISE) != 0;
430 test_data->upper_realm = (i & TEST_UPPER_REALM) != 0;
431 test_data->upper_username = (i & TEST_UPPER_USERNAME) != 0;
432 torture_suite_add_simple_tcase_const(suite, name, torture_krb5_as_req_canon,