Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : Validate the krb5 pac generation routines
5 :
6 : Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2015
7 :
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.
12 :
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.
17 :
18 :
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/>.
21 : */
22 :
23 : #include "includes.h"
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/cmdline.h"
29 : #include "source4/auth/kerberos/kerberos.h"
30 : #include "source4/auth/kerberos/kerberos_util.h"
31 : #include "lib/util/util_net.h"
32 : #include "auth/auth.h"
33 : #include "auth/auth_sam_reply.h"
34 : #include "auth/gensec/gensec.h"
35 : #include "param/param.h"
36 :
37 : #undef strcasecmp
38 :
39 : #define TEST_CANONICALIZE 0x0000001
40 : #define TEST_ENTERPRISE 0x0000002
41 : #define TEST_UPPER_USERNAME 0x0000008
42 : #define TEST_WIN2K 0x0000020
43 : #define TEST_UPN 0x0000040
44 : #define TEST_S4U2SELF 0x0000080
45 : #define TEST_REMOVEDOLLAR 0x0000100
46 : #define TEST_AS_REQ_SPN 0x0000200
47 : #define TEST_ALL 0x00003FF
48 :
49 : struct test_data {
50 : const char *test_name;
51 : const char *realm;
52 : const char *real_realm;
53 : const char *real_domain;
54 : const char *username;
55 : const char *real_username;
56 : bool canonicalize;
57 : bool enterprise;
58 : bool upper_username;
59 : bool win2k;
60 : bool upn;
61 : bool other_upn_suffix;
62 : bool s4u2self;
63 : bool removedollar;
64 : bool as_req_spn;
65 : bool spn_is_upn;
66 : const char *krb5_service;
67 : const char *krb5_hostname;
68 : };
69 :
70 : struct torture_krb5_context {
71 : struct smb_krb5_context *smb_krb5_context;
72 : struct torture_context *tctx;
73 : struct addrinfo *server;
74 : struct test_data *test_data;
75 : int packet_count;
76 : };
77 :
78 : struct pac_data {
79 : const char *principal_name;
80 : };
81 :
82 : /*
83 : * A helper function which avoids touching the local databases to
84 : * generate the session info, as we just want to verify the principal
85 : * name that we found in the ticket not the full local token
86 : */
87 1152 : static NTSTATUS test_generate_session_info_pac(struct auth4_context *auth_ctx,
88 : TALLOC_CTX *mem_ctx,
89 : struct smb_krb5_context *smb_krb5_context,
90 : DATA_BLOB *pac_blob,
91 : const char *principal_name,
92 : const struct tsocket_address *remote_address,
93 : uint32_t session_info_flags,
94 : struct auth_session_info **session_info)
95 : {
96 0 : NTSTATUS nt_status;
97 0 : struct auth_user_info_dc *user_info_dc;
98 0 : TALLOC_CTX *tmp_ctx;
99 0 : struct pac_data *pac_data;
100 :
101 1152 : if (pac_blob == NULL) {
102 0 : DBG_ERR("pac_blob missing\n");
103 0 : return NT_STATUS_NO_IMPERSONATION_TOKEN;
104 : }
105 :
106 1152 : tmp_ctx = talloc_named(mem_ctx, 0, "gensec_gssapi_session_info context");
107 1152 : NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
108 :
109 1152 : auth_ctx->private_data = pac_data = talloc_zero(auth_ctx, struct pac_data);
110 :
111 1152 : pac_data->principal_name = talloc_strdup(pac_data, principal_name);
112 1152 : if (!pac_data->principal_name) {
113 0 : talloc_free(tmp_ctx);
114 0 : return NT_STATUS_NO_MEMORY;
115 : }
116 :
117 1152 : nt_status = kerberos_pac_blob_to_user_info_dc(tmp_ctx,
118 : *pac_blob,
119 : smb_krb5_context->krb5_context,
120 : &user_info_dc, NULL, NULL);
121 1152 : if (!NT_STATUS_IS_OK(nt_status)) {
122 0 : talloc_free(tmp_ctx);
123 0 : return nt_status;
124 : }
125 :
126 1152 : if (!(user_info_dc->info->user_flags & NETLOGON_GUEST)) {
127 1152 : session_info_flags |= AUTH_SESSION_INFO_AUTHENTICATED;
128 : }
129 :
130 1152 : session_info_flags |= AUTH_SESSION_INFO_SIMPLE_PRIVILEGES;
131 1152 : nt_status = auth_generate_session_info(mem_ctx,
132 : NULL,
133 : NULL,
134 : user_info_dc, session_info_flags,
135 : session_info);
136 1152 : if (!NT_STATUS_IS_OK(nt_status)) {
137 0 : talloc_free(tmp_ctx);
138 0 : return nt_status;
139 : }
140 :
141 1152 : talloc_free(tmp_ctx);
142 1152 : return NT_STATUS_OK;
143 : }
144 :
145 : /* Check to see if we can pass the PAC across to the NETLOGON server for validation */
146 :
147 : /* Also happens to be a really good one-step verification of our Kerberos stack */
148 :
149 1152 : static bool test_accept_ticket(struct torture_context *tctx,
150 : struct cli_credentials *credentials,
151 : const char *principal,
152 : DATA_BLOB client_to_server)
153 : {
154 0 : NTSTATUS status;
155 0 : struct gensec_security *gensec_server_context;
156 0 : DATA_BLOB server_to_client;
157 0 : struct auth4_context *auth_context;
158 0 : struct auth_session_info *session_info;
159 0 : struct pac_data *pac_data;
160 1152 : TALLOC_CTX *tmp_ctx = talloc_new(tctx);
161 :
162 1152 : torture_assert(tctx, tmp_ctx != NULL, "talloc_new() failed");
163 :
164 1152 : auth_context = talloc_zero(tmp_ctx, struct auth4_context);
165 1152 : torture_assert(tctx, auth_context != NULL, "talloc_new() failed");
166 :
167 1152 : auth_context->generate_session_info_pac = test_generate_session_info_pac;
168 :
169 1152 : status = gensec_server_start(tctx,
170 : lpcfg_gensec_settings(tctx, tctx->lp_ctx),
171 : auth_context, &gensec_server_context);
172 1152 : torture_assert_ntstatus_ok(tctx, status, "gensec_server_start (server) failed");
173 :
174 1152 : status = gensec_set_credentials(gensec_server_context, credentials);
175 1152 : torture_assert_ntstatus_ok(tctx, status, "gensec_set_credentials (server) failed");
176 :
177 1152 : status = gensec_start_mech_by_name(gensec_server_context, "krb5");
178 1152 : torture_assert_ntstatus_ok(tctx, status, "gensec_start_mech_by_name (server) failed");
179 :
180 1152 : server_to_client = data_blob(NULL, 0);
181 :
182 : /* Do a client-server update dance */
183 1152 : status = gensec_update(gensec_server_context, tmp_ctx, client_to_server, &server_to_client);
184 1152 : torture_assert_ntstatus_ok(tctx, status, "gensec_update (server) failed");
185 :
186 : /* Extract the PAC using Samba's code */
187 :
188 1152 : status = gensec_session_info(gensec_server_context, gensec_server_context, &session_info);
189 1152 : torture_assert_ntstatus_ok(tctx, status, "gensec_session_info failed");
190 :
191 1152 : pac_data = talloc_get_type(auth_context->private_data, struct pac_data);
192 :
193 1152 : torture_assert(tctx, pac_data != NULL, "gensec_update failed to fill in pac_data in auth_context");
194 1152 : torture_assert(tctx, pac_data->principal_name != NULL, "principal_name not present");
195 1152 : torture_assert_str_equal(tctx, pac_data->principal_name, principal, "wrong principal name");
196 1152 : return true;
197 : }
198 :
199 : /*
200 : * This function is set in torture_krb5_init_context_canon as krb5
201 : * send_and_recv function. This allows us to override what server the
202 : * test is aimed at, and to inspect the packets just before they are
203 : * sent to the network, and before they are processed on the recv
204 : * side.
205 : *
206 : */
207 11952 : static krb5_error_code test_krb5_send_to_realm_canon_override(struct smb_krb5_context *smb_krb5_context,
208 : void *data, /* struct torture_krb5_context */
209 : krb5_const_realm realm,
210 : time_t timeout,
211 : const krb5_data *send_buf,
212 : krb5_data *recv_buf)
213 : {
214 0 : krb5_error_code k5ret;
215 :
216 0 : struct torture_krb5_context *test_context
217 11952 : = talloc_get_type_abort(data, struct torture_krb5_context);
218 :
219 11952 : SMB_ASSERT(smb_krb5_context == test_context->smb_krb5_context);
220 :
221 11952 : k5ret = smb_krb5_send_and_recv_func_forced_tcp(smb_krb5_context,
222 : test_context->server,
223 : timeout,
224 : send_buf,
225 : recv_buf);
226 11952 : if (k5ret != 0) {
227 0 : return k5ret;
228 : }
229 :
230 11952 : test_context->packet_count++;
231 :
232 11952 : return k5ret;
233 : }
234 :
235 2688 : static int test_context_destructor(struct torture_krb5_context *test_context)
236 : {
237 2688 : freeaddrinfo(test_context->server);
238 2688 : return 0;
239 : }
240 :
241 :
242 2688 : static bool torture_krb5_init_context_canon(struct torture_context *tctx,
243 : struct test_data *test_data,
244 : struct torture_krb5_context **torture_krb5_context)
245 : {
246 2688 : const char *host = torture_setting_string(tctx, "host", NULL);
247 0 : krb5_error_code k5ret;
248 0 : bool ok;
249 :
250 2688 : struct torture_krb5_context *test_context = talloc_zero(tctx, struct torture_krb5_context);
251 2688 : torture_assert(tctx, test_context != NULL, "Failed to allocate");
252 :
253 2688 : test_context->test_data = test_data;
254 2688 : test_context->tctx = tctx;
255 :
256 2688 : k5ret = smb_krb5_init_context(test_context, tctx->lp_ctx, &test_context->smb_krb5_context);
257 2688 : torture_assert_int_equal(tctx, k5ret, 0, "smb_krb5_init_context failed");
258 :
259 2688 : ok = interpret_string_addr_internal(&test_context->server, host, AI_NUMERICHOST);
260 2688 : torture_assert(tctx, ok, "Failed to parse target server");
261 :
262 2688 : talloc_set_destructor(test_context, test_context_destructor);
263 :
264 2688 : set_sockaddr_port(test_context->server->ai_addr, 88);
265 :
266 2688 : k5ret = smb_krb5_set_send_to_kdc_func(test_context->smb_krb5_context,
267 : test_krb5_send_to_realm_canon_override,
268 : NULL, /* send_to_kdc */
269 : test_context);
270 2688 : torture_assert_int_equal(tctx, k5ret, 0, "krb5_set_send_to_kdc_func failed");
271 2688 : *torture_krb5_context = test_context;
272 2688 : return true;
273 : }
274 :
275 :
276 4096 : static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void *tcase_data)
277 : {
278 0 : krb5_error_code k5ret;
279 4096 : krb5_get_init_creds_opt *krb_options = NULL;
280 4096 : struct test_data *test_data = talloc_get_type_abort(tcase_data, struct test_data);
281 0 : krb5_principal principal;
282 0 : krb5_principal krbtgt_other;
283 0 : krb5_principal expected_principal;
284 4096 : const char *principal_string = NULL;
285 0 : char *krbtgt_other_string;
286 0 : int principal_flags;
287 4096 : const char *expected_principal_string = NULL;
288 0 : char *expected_unparse_principal_string;
289 0 : int expected_principal_flags;
290 0 : char *got_principal_string;
291 0 : char *assertion_message;
292 4096 : const char *password = cli_credentials_get_password(
293 : samba_cmdline_get_creds());
294 0 : krb5_context k5_context;
295 0 : struct torture_krb5_context *test_context;
296 0 : bool ok;
297 0 : krb5_creds my_creds;
298 0 : krb5_creds *server_creds;
299 0 : krb5_ccache ccache;
300 0 : krb5_auth_context auth_context;
301 0 : char *cc_name;
302 0 : krb5_data in_data, enc_ticket;
303 0 : krb5_get_creds_opt opt;
304 :
305 4096 : const char *spn = NULL;
306 4096 : const char *spn_real_realm = NULL;
307 4096 : const char *upn = torture_setting_string(tctx, "krb5-upn", "");
308 4096 : test_data->krb5_service = torture_setting_string(tctx, "krb5-service", "host");
309 4096 : test_data->krb5_hostname = torture_setting_string(tctx, "krb5-hostname", "");
310 :
311 : /*
312 : * If we have not passed a UPN on the command line,
313 : * then skip the UPN tests.
314 : */
315 4096 : if (test_data->upn && upn[0] == '\0') {
316 640 : torture_skip(tctx, "This test needs a UPN specified as --option=torture:krb5-upn=user@example.com to run");
317 : }
318 :
319 : /*
320 : * If we have not passed a SPN on the command line,
321 : * then skip the SPN tests.
322 : */
323 3456 : if (test_data->as_req_spn && test_data->krb5_hostname[0] == '\0') {
324 256 : torture_skip(tctx, "This test needs a hostname specified as --option=torture:krb5-hostname=hostname.example.com and optionally --option=torture:krb5-service=service (defaults to host) to run");
325 : }
326 :
327 3200 : if (test_data->removedollar &&
328 1024 : !torture_setting_bool(tctx, "run_removedollar_test", false))
329 : {
330 512 : torture_skip(tctx, "--option=torture:run_removedollar_test=true not specified");
331 : }
332 :
333 2688 : test_data->realm = test_data->real_realm;
334 :
335 2688 : if (test_data->upn) {
336 0 : char *p;
337 384 : test_data->username = talloc_strdup(test_data, upn);
338 384 : p = strchr(test_data->username, '@');
339 384 : if (p) {
340 384 : *p = '\0';
341 384 : p++;
342 : }
343 : /*
344 : * Test the UPN behaviour carefully. We can
345 : * test in two different modes, depending on
346 : * what UPN has been set up for us.
347 : *
348 : * If the UPN is in our realm, then we do all the tests with this name also.
349 : *
350 : * If the UPN is not in our realm, then we
351 : * expect the tests that replace the realm to
352 : * fail (as it won't match)
353 : */
354 384 : if (strcasecmp(p, test_data->real_realm) != 0) {
355 128 : test_data->other_upn_suffix = true;
356 : } else {
357 256 : test_data->other_upn_suffix = false;
358 : }
359 :
360 : /*
361 : * This lets us test the combination of the UPN prefix
362 : * with a valid domain, without adding even more
363 : * combinations
364 : */
365 384 : test_data->realm = p;
366 : }
367 :
368 2688 : ok = torture_krb5_init_context_canon(tctx, test_data, &test_context);
369 2688 : torture_assert(tctx, ok, "torture_krb5_init_context failed");
370 2688 : k5_context = test_context->smb_krb5_context->krb5_context;
371 :
372 2688 : test_data->realm = strupper_talloc(test_data, test_data->realm);
373 2688 : if (test_data->upper_username) {
374 1344 : test_data->username = strupper_talloc(test_data, test_data->username);
375 : } else {
376 1344 : test_data->username = talloc_strdup(test_data, test_data->username);
377 : }
378 :
379 2688 : if (test_data->removedollar) {
380 0 : char *p;
381 :
382 512 : p = strchr_m(test_data->username, '$');
383 512 : torture_assert(tctx, p != NULL, talloc_asprintf(tctx,
384 : "username[%s] contains no '$'\n",
385 : test_data->username));
386 512 : *p = '\0';
387 : }
388 :
389 2688 : spn = talloc_asprintf(test_data, "%s/%s@%s",
390 : test_data->krb5_service,
391 : test_data->krb5_hostname,
392 : test_data->realm);
393 :
394 2688 : spn_real_realm = talloc_asprintf(test_data, "%s/%s@%s",
395 : test_data->krb5_service,
396 : test_data->krb5_hostname,
397 : test_data->real_realm);
398 :
399 2688 : if (!test_data->canonicalize && test_data->enterprise) {
400 672 : torture_skip(tctx,
401 : "This test combination "
402 : "is skipped intentionally");
403 : }
404 :
405 2016 : if (test_data->as_req_spn) {
406 576 : if (test_data->enterprise) {
407 192 : torture_skip(tctx,
408 : "This test combination "
409 : "is skipped intentionally");
410 : }
411 384 : principal_string = spn;
412 : } else {
413 1440 : principal_string = talloc_asprintf(test_data,
414 : "%s@%s",
415 : test_data->username,
416 : test_data->realm);
417 :
418 : }
419 :
420 0 : test_data->spn_is_upn
421 1824 : = (strcasecmp(upn, spn) == 0);
422 :
423 : /*
424 : * If we are set to canonicalize, we get back the fixed UPPER
425 : * case realm, and the real username (ie matching LDAP
426 : * samAccountName)
427 : *
428 : * Otherwise, if we are set to enterprise, we
429 : * get back the whole principal as-sent
430 : *
431 : * Finally, if we are not set to canonicalize, we get back the
432 : * fixed UPPER case realm, but the as-sent username
433 : */
434 1824 : if (test_data->as_req_spn && !test_data->spn_is_upn) {
435 320 : expected_principal_string = spn;
436 1504 : } else if (test_data->canonicalize) {
437 992 : expected_principal_string = talloc_asprintf(test_data,
438 : "%s@%s",
439 : test_data->real_username,
440 : test_data->real_realm);
441 512 : } else if (test_data->as_req_spn && test_data->spn_is_upn) {
442 32 : expected_principal_string = spn_real_realm;
443 : } else {
444 480 : expected_principal_string = talloc_asprintf(test_data,
445 : "%s@%s",
446 : test_data->username,
447 : test_data->real_realm);
448 : }
449 :
450 1824 : if (test_data->enterprise) {
451 480 : principal_flags = KRB5_PRINCIPAL_PARSE_ENTERPRISE;
452 : } else {
453 1344 : if (test_data->upn && test_data->other_upn_suffix) {
454 64 : torture_skip(tctx, "UPN test for UPN with other UPN suffix only runs with enterprise principals");
455 : }
456 1280 : principal_flags = 0;
457 : }
458 :
459 1760 : if (test_data->canonicalize) {
460 1120 : expected_principal_flags = 0;
461 : } else {
462 640 : expected_principal_flags = principal_flags;
463 : }
464 :
465 1760 : torture_assert_int_equal(tctx,
466 : krb5_parse_name_flags(k5_context,
467 : principal_string,
468 : principal_flags,
469 : &principal),
470 : 0, "krb5_parse_name_flags failed");
471 1760 : torture_assert_int_equal(tctx,
472 : krb5_parse_name_flags(k5_context,
473 : expected_principal_string,
474 : expected_principal_flags,
475 : &expected_principal),
476 : 0, "krb5_parse_name_flags failed");
477 :
478 1760 : if (test_data->as_req_spn) {
479 384 : if (test_data->upn) {
480 0 : krb5_principal_set_type(k5_context,
481 : principal,
482 : KRB5_NT_PRINCIPAL);
483 0 : krb5_principal_set_type(k5_context,
484 : expected_principal,
485 : KRB5_NT_PRINCIPAL);
486 : } else {
487 384 : krb5_principal_set_type(k5_context,
488 : principal,
489 : KRB5_NT_SRV_HST);
490 384 : krb5_principal_set_type(k5_context,
491 : expected_principal,
492 : KRB5_NT_SRV_HST);
493 : }
494 : }
495 :
496 1760 : torture_assert_int_equal(tctx,
497 : krb5_unparse_name(k5_context,
498 : expected_principal,
499 : &expected_unparse_principal_string),
500 : 0, "krb5_unparse_name failed");
501 : /*
502 : * Prepare a AS-REQ and run the TEST_AS_REQ tests
503 : *
504 : */
505 :
506 1760 : test_context->packet_count = 0;
507 :
508 : /*
509 : * Set the canonicalize flag if this test requires it
510 : */
511 1760 : torture_assert_int_equal(tctx,
512 : krb5_get_init_creds_opt_alloc(k5_context, &krb_options),
513 : 0, "krb5_get_init_creds_opt_alloc failed");
514 :
515 1760 : torture_assert_int_equal(tctx,
516 : krb5_get_init_creds_opt_set_canonicalize(k5_context,
517 : krb_options,
518 : test_data->canonicalize),
519 : 0, "krb5_get_init_creds_opt_set_canonicalize failed");
520 :
521 1760 : torture_assert_int_equal(tctx,
522 : krb5_get_init_creds_opt_set_win2k(k5_context,
523 : krb_options,
524 : test_data->win2k),
525 : 0, "krb5_get_init_creds_opt_set_win2k failed");
526 :
527 1760 : k5ret = krb5_get_init_creds_password(k5_context, &my_creds, principal,
528 : password, NULL, NULL, 0,
529 : NULL, krb_options);
530 :
531 1760 : if (test_context->test_data->as_req_spn
532 384 : && !test_context->test_data->spn_is_upn) {
533 320 : torture_assert_int_equal(tctx, k5ret,
534 : KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN,
535 : "Got wrong error_code from "
536 : "krb5_get_init_creds_password");
537 : /* We can't proceed with more checks */
538 320 : return true;
539 : } else {
540 1440 : assertion_message = talloc_asprintf(tctx,
541 : "krb5_get_init_creds_password for %s failed: %s",
542 : principal_string,
543 : smb_get_krb5_error_message(k5_context, k5ret, tctx));
544 1440 : torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
545 : }
546 :
547 1440 : torture_assert(tctx,
548 : test_context->packet_count > 1,
549 : "Expected krb5_get_init_creds_password to send more packets");
550 :
551 : /*
552 : * Assert that the reply was with the correct type of
553 : * principal, depending on the flags we set
554 : */
555 1440 : if (test_data->canonicalize == false && test_data->as_req_spn) {
556 32 : torture_assert_int_equal(tctx,
557 : krb5_principal_get_type(k5_context,
558 : my_creds.client),
559 : KRB5_NT_SRV_HST,
560 : "smb_krb5_init_context gave incorrect client->name.name_type");
561 : } else {
562 1408 : torture_assert_int_equal(tctx,
563 : krb5_principal_get_type(k5_context,
564 : my_creds.client),
565 : KRB5_NT_PRINCIPAL,
566 : "smb_krb5_init_context gave incorrect client->name.name_type");
567 : }
568 :
569 1440 : torture_assert_int_equal(tctx,
570 : krb5_unparse_name(k5_context,
571 : my_creds.client, &got_principal_string), 0,
572 : "krb5_unparse_name failed");
573 :
574 1440 : assertion_message = talloc_asprintf(tctx,
575 : "krb5_get_init_creds_password returned a different principal %s to what was expected %s",
576 : got_principal_string, expected_principal_string);
577 1440 : krb5_xfree(got_principal_string);
578 :
579 1440 : torture_assert(tctx, krb5_principal_compare(k5_context,
580 : my_creds.client, expected_principal),
581 : assertion_message);
582 :
583 :
584 1440 : torture_assert_int_equal(tctx,
585 : krb5_principal_get_type(k5_context,
586 : my_creds.server), KRB5_NT_SRV_INST,
587 : "smb_krb5_init_context gave incorrect server->name.name_type");
588 :
589 1440 : torture_assert_int_equal(tctx,
590 : krb5_principal_get_num_comp(k5_context,
591 : my_creds.server), 2,
592 : "smb_krb5_init_context gave incorrect number of components in my_creds.server->name");
593 :
594 1440 : torture_assert_str_equal(tctx,
595 : krb5_principal_get_comp_string(k5_context,
596 : my_creds.server, 0),
597 : "krbtgt",
598 : "smb_krb5_init_context gave incorrect my_creds.server->name.name_string[0]");
599 :
600 1440 : if (test_data->canonicalize) {
601 960 : torture_assert_str_equal(tctx,
602 : krb5_principal_get_comp_string(k5_context,
603 : my_creds.server, 1),
604 : test_data->real_realm,
605 :
606 : "smb_krb5_init_context gave incorrect my_creds.server->name.name_string[1]");
607 : } else {
608 480 : torture_assert_str_equal(tctx,
609 : krb5_principal_get_comp_string(k5_context,
610 : my_creds.server, 1),
611 : test_data->realm,
612 :
613 : "smb_krb5_init_context gave incorrect my_creds.server->name.name_string[1]");
614 : }
615 1440 : torture_assert_str_equal(tctx,
616 : krb5_principal_get_realm(k5_context,
617 : my_creds.server),
618 : test_data->real_realm,
619 : "smb_krb5_init_context gave incorrect my_creds.server->realm");
620 :
621 : /* Store the result of the 'kinit' above into a memory ccache */
622 1440 : cc_name = talloc_asprintf(tctx, "MEMORY:%s", test_data->test_name);
623 1440 : torture_assert_int_equal(tctx, krb5_cc_resolve(k5_context, cc_name,
624 : &ccache),
625 : 0, "krb5_cc_resolve failed");
626 :
627 1440 : torture_assert_int_equal(tctx, krb5_cc_initialize(k5_context,
628 : ccache, my_creds.client),
629 : 0, "krb5_cc_initialize failed");
630 :
631 1440 : torture_assert_int_equal(tctx, krb5_cc_store_cred(k5_context,
632 : ccache, &my_creds),
633 : 0, "krb5_cc_store_cred failed");
634 :
635 : /*
636 : * Prepare a TGS-REQ and run the TEST_TGS_REQ_KRBTGT_CANON tests
637 : *
638 : * This tests krb5_get_creds behaviour, which allows us to set
639 : * the KRB5_GC_CANONICALIZE option against the krbtgt/ principal
640 : */
641 :
642 1440 : krbtgt_other_string = talloc_asprintf(test_data, "krbtgt/%s@%s", test_data->real_domain, test_data->real_realm);
643 1440 : torture_assert_int_equal(tctx,
644 : krb5_make_principal(k5_context, &krbtgt_other,
645 : test_data->real_realm, "krbtgt",
646 : test_data->real_domain, NULL),
647 : 0, "krb5_make_principal failed");
648 :
649 1440 : test_context->packet_count = 0;
650 :
651 1440 : torture_assert_int_equal(tctx,
652 : krb5_get_creds_opt_alloc(k5_context, &opt),
653 : 0, "krb5_get_creds_opt_alloc");
654 :
655 1440 : krb5_get_creds_opt_add_options(k5_context,
656 : opt,
657 : KRB5_GC_CANONICALIZE);
658 :
659 1440 : krb5_get_creds_opt_add_options(k5_context,
660 : opt,
661 : KRB5_GC_NO_STORE);
662 :
663 : /* Confirm if we can get a ticket krbtgt/realm that we got back with the initial kinit */
664 1440 : k5ret = krb5_get_creds(k5_context, opt, ccache, krbtgt_other, &server_creds);
665 :
666 : {
667 : /*
668 : * In these situations, the code above does not store a
669 : * principal in the credentials cache matching what
670 : * krb5_get_creds() needs without talking to the KDC, so the
671 : * test fails with looping detected because when we set
672 : * canonicalize we confuse the client libs.
673 : *
674 : */
675 1440 : assertion_message = talloc_asprintf(tctx,
676 : "krb5_get_creds for %s should have failed with looping detected: %s",
677 : krbtgt_other_string,
678 : smb_get_krb5_error_message(k5_context, k5ret,
679 : tctx));
680 :
681 1440 : torture_assert_int_equal(tctx, k5ret, KRB5_GET_IN_TKT_LOOP, assertion_message);
682 1440 : torture_assert_int_equal(tctx,
683 : test_context->packet_count,
684 : 2, "Expected krb5_get_creds to send packets");
685 : }
686 :
687 : /*
688 : * Prepare a TGS-REQ and run the TEST_TGS_REQ_CANON tests
689 : *
690 : * This tests krb5_get_creds behaviour, which allows us to set
691 : * the KRB5_GC_CANONICALIZE option
692 : */
693 :
694 1440 : test_context->packet_count = 0;
695 :
696 1440 : torture_assert_int_equal(tctx,
697 : krb5_get_creds_opt_alloc(k5_context, &opt),
698 : 0, "krb5_get_creds_opt_alloc");
699 :
700 1440 : krb5_get_creds_opt_add_options(k5_context,
701 : opt,
702 : KRB5_GC_CANONICALIZE);
703 :
704 1440 : krb5_get_creds_opt_add_options(k5_context,
705 : opt,
706 : KRB5_GC_NO_STORE);
707 :
708 1440 : if (test_data->s4u2self) {
709 720 : torture_assert_int_equal(tctx,
710 : krb5_get_creds_opt_set_impersonate(k5_context,
711 : opt,
712 : principal),
713 : 0, "krb5_get_creds_opt_set_impersonate failed");
714 : }
715 :
716 : /* Confirm if we can get a ticket to our own name */
717 1440 : k5ret = krb5_get_creds(k5_context, opt, ccache, principal, &server_creds);
718 :
719 : /*
720 : * In these situations, the code above does not store a
721 : * principal in the credentials cache matching what
722 : * krb5_get_creds() needs, so the test fails.
723 : *
724 : */
725 : {
726 1440 : assertion_message = talloc_asprintf(tctx,
727 : "krb5_get_creds for %s failed: %s",
728 : principal_string,
729 : smb_get_krb5_error_message(k5_context, k5ret,
730 : tctx));
731 :
732 : /*
733 : * Only machine accounts (strictly, accounts with a
734 : * servicePrincipalName) can expect this test to succeed
735 : */
736 1440 : if (torture_setting_bool(tctx, "expect_machine_account", false)
737 1216 : && (test_data->enterprise
738 832 : || test_data->spn_is_upn
739 640 : || test_data->upn == false)) {
740 1152 : torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
741 1152 : torture_assert_int_equal(tctx, krb5_cc_store_cred(k5_context,
742 : ccache, server_creds),
743 : 0, "krb5_cc_store_cred failed");
744 :
745 1152 : torture_assert_int_equal(tctx,
746 : krb5_free_creds(k5_context,
747 : server_creds),
748 : 0, "krb5_free_cred_contents failed");
749 :
750 1152 : torture_assert_int_equal(tctx,
751 : test_context->packet_count,
752 : 1, "Expected krb5_get_creds to send one packet");
753 :
754 : } else {
755 288 : torture_assert_int_equal(tctx, k5ret, KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN,
756 : assertion_message);
757 : /* Account for get_cred_kdc_capath() and get_cred_kdc_referral() fallback */
758 288 : torture_assert_int_equal(tctx,
759 : test_context->packet_count,
760 : 2, "Expected krb5_get_creds to send 2 packets");
761 : }
762 : }
763 :
764 : /*
765 : * Confirm getting a ticket to pass to the server, running
766 : * either the TEST_TGS_REQ or TEST_SELF_TRUST_TGS_REQ stage.
767 : *
768 : * This triggers the client to attempt to get a
769 : * cross-realm ticket between the alternate names of
770 : * the server, and we need to confirm that behaviour.
771 : *
772 : */
773 :
774 1440 : test_context->packet_count = 0;
775 1440 : torture_assert_int_equal(tctx, krb5_auth_con_init(k5_context, &auth_context),
776 : 0, "krb5_auth_con_init failed");
777 :
778 1440 : in_data.length = 0;
779 1440 : k5ret = krb5_mk_req_exact(k5_context,
780 : &auth_context,
781 : AP_OPTS_USE_SUBKEY,
782 : principal,
783 : &in_data, ccache,
784 : &enc_ticket);
785 1440 : assertion_message = talloc_asprintf(tctx,
786 : "krb5_mk_req_exact for %s failed: %s",
787 : principal_string,
788 : smb_get_krb5_error_message(k5_context, k5ret, tctx));
789 :
790 : /*
791 : * Only machine accounts (strictly, accounts with a
792 : * servicePrincipalName) can expect this test to succeed
793 : */
794 1440 : if (torture_setting_bool(tctx, "expect_machine_account", false)
795 1216 : && (test_data->enterprise ||
796 832 : (test_context->test_data->as_req_spn
797 768 : || test_context->test_data->spn_is_upn)
798 1792 : || test_data->upn == false)) {
799 0 : DATA_BLOB client_to_server;
800 1152 : torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
801 1152 : client_to_server = data_blob_const(enc_ticket.data, enc_ticket.length);
802 :
803 : /* This is very weird */
804 1152 : if (test_data->canonicalize == false
805 384 : && test_context->test_data->as_req_spn
806 32 : && test_context->test_data->spn_is_upn
807 32 : && test_context->test_data->s4u2self) {
808 :
809 16 : torture_assert(tctx,
810 : test_accept_ticket(tctx,
811 : samba_cmdline_get_creds(),
812 : spn_real_realm,
813 : client_to_server),
814 : "test_accept_ticket failed - failed to accept the ticket we just created");
815 1136 : } else if (test_data->canonicalize == true
816 768 : && test_context->test_data->as_req_spn
817 32 : && test_context->test_data->spn_is_upn
818 32 : && test_context->test_data->s4u2self) {
819 :
820 16 : torture_assert(tctx,
821 : test_accept_ticket(tctx,
822 : samba_cmdline_get_creds(),
823 : expected_principal_string,
824 : client_to_server),
825 : "test_accept_ticket failed - failed to accept the ticket we just created");
826 1120 : } else if (test_data->canonicalize == true
827 752 : && test_data->enterprise == false
828 368 : && test_context->test_data->upn
829 32 : && test_context->test_data->spn_is_upn
830 32 : && test_context->test_data->s4u2self) {
831 :
832 16 : torture_assert(tctx,
833 : test_accept_ticket(tctx,
834 : samba_cmdline_get_creds(),
835 : expected_principal_string,
836 : client_to_server),
837 : "test_accept_ticket failed - failed to accept the ticket we just created");
838 1104 : } else if (test_data->canonicalize == false
839 368 : && test_context->test_data->upn
840 32 : && test_context->test_data->spn_is_upn
841 48 : && test_context->test_data->s4u2self) {
842 :
843 0 : const char *accept_expected_principal_string
844 16 : = talloc_asprintf(test_data,
845 : "%s@%s",
846 : test_data->username,
847 : test_data->real_realm);
848 :
849 16 : torture_assert(tctx,
850 : test_accept_ticket(tctx,
851 : samba_cmdline_get_creds(),
852 : accept_expected_principal_string,
853 : client_to_server),
854 : "test_accept_ticket failed - failed to accept the ticket we just created");
855 : } else {
856 :
857 1088 : torture_assert(tctx,
858 : test_accept_ticket(tctx,
859 : samba_cmdline_get_creds(),
860 : expected_unparse_principal_string,
861 : client_to_server),
862 : "test_accept_ticket failed - failed to accept the ticket we just created");
863 : }
864 1152 : krb5_data_free(&enc_ticket);
865 : } else {
866 288 : torture_assert_int_equal(tctx, k5ret, KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN,
867 : assertion_message);
868 : }
869 :
870 : /*
871 : * Confirm getting a ticket to pass to the server, running
872 : * the TEST_TGS_REQ_HOST, TEST_TGS_REQ_HOST_SRV_INST, TEST_TGS_REQ_HOST_SRV_HST stage
873 : *
874 : * This triggers the client to attempt to get a
875 : * cross-realm ticket between the alternate names of
876 : * the server, and we need to confirm that behaviour.
877 : *
878 : */
879 :
880 1440 : if (*test_data->krb5_service && *test_data->krb5_hostname) {
881 0 : krb5_principal host_principal_srv_inst;
882 : /*
883 : * This tries to guess when the krb5 libs will ask for a
884 : * cross-realm ticket, and when they will just ask the KDC
885 : * directly.
886 : */
887 1216 : test_context->packet_count = 0;
888 1216 : torture_assert_int_equal(tctx, krb5_auth_con_init(k5_context, &auth_context),
889 : 0, "krb5_auth_con_init failed");
890 :
891 1216 : in_data.length = 0;
892 1216 : k5ret = krb5_mk_req(k5_context,
893 : &auth_context,
894 : 0,
895 : test_data->krb5_service,
896 : test_data->krb5_hostname,
897 : &in_data, ccache,
898 : &enc_ticket);
899 :
900 : {
901 1216 : assertion_message = talloc_asprintf(tctx,
902 : "krb5_mk_req for %s/%s failed: %s",
903 : test_data->krb5_service,
904 : test_data->krb5_hostname,
905 : smb_get_krb5_error_message(k5_context, k5ret, tctx));
906 :
907 1216 : torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
908 :
909 1216 : if (test_data->spn_is_upn == false) {
910 : /*
911 : * Only in these cases would the above
912 : * code have needed to send packets to
913 : * the network
914 : */
915 960 : torture_assert(tctx,
916 : test_context->packet_count > 0,
917 : "Expected krb5_get_creds to send packets");
918 : }
919 : }
920 :
921 :
922 1216 : test_context->packet_count = 0;
923 :
924 1216 : torture_assert_int_equal(tctx,
925 : krb5_make_principal(k5_context, &host_principal_srv_inst,
926 : test_data->real_realm,
927 : strupper_talloc(tctx, test_data->krb5_service),
928 : test_data->krb5_hostname,
929 : NULL),
930 : 0, "krb5_make_principal failed");
931 :
932 1216 : krb5_principal_set_type(k5_context, host_principal_srv_inst, KRB5_NT_SRV_INST);
933 :
934 1216 : torture_assert_int_equal(tctx, krb5_auth_con_init(k5_context, &auth_context),
935 : 0, "krb5_auth_con_init failed");
936 :
937 1216 : in_data.length = 0;
938 1216 : k5ret = krb5_mk_req_exact(k5_context,
939 : &auth_context,
940 : 0,
941 : host_principal_srv_inst,
942 : &in_data, ccache,
943 : &enc_ticket);
944 1216 : krb5_free_principal(k5_context, host_principal_srv_inst);
945 : {
946 1216 : assertion_message = talloc_asprintf(tctx,
947 : "krb5_mk_req for %s/%s KRB5_NT_SRV_INST failed: %s",
948 : test_data->krb5_service,
949 : test_data->krb5_hostname,
950 : smb_get_krb5_error_message(k5_context, k5ret, tctx));
951 :
952 1216 : torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
953 : /*
954 : * Only in these cases would the above code have needed to
955 : * send packets to the network
956 : */
957 1216 : torture_assert(tctx,
958 : test_context->packet_count > 0,
959 : "Expected krb5_get_creds to send packets");
960 : }
961 :
962 :
963 1216 : test_context->packet_count = 0;
964 :
965 1216 : torture_assert_int_equal(tctx,
966 : krb5_make_principal(k5_context, &host_principal_srv_inst,
967 : test_data->real_realm,
968 : test_data->krb5_service,
969 : strupper_talloc(tctx, test_data->krb5_hostname),
970 : NULL),
971 : 0, "krb5_make_principal failed");
972 :
973 1216 : krb5_principal_set_type(k5_context, host_principal_srv_inst, KRB5_NT_SRV_HST);
974 :
975 1216 : torture_assert_int_equal(tctx, krb5_auth_con_init(k5_context, &auth_context),
976 : 0, "krb5_auth_con_init failed");
977 :
978 1216 : in_data.length = 0;
979 1216 : k5ret = krb5_mk_req_exact(k5_context,
980 : &auth_context,
981 : 0,
982 : host_principal_srv_inst,
983 : &in_data, ccache,
984 : &enc_ticket);
985 1216 : krb5_free_principal(k5_context, host_principal_srv_inst);
986 : {
987 1216 : assertion_message = talloc_asprintf(tctx,
988 : "krb5_mk_req for %s/%s KRB5_NT_SRV_INST failed: %s",
989 : test_data->krb5_service,
990 : test_data->krb5_hostname,
991 : smb_get_krb5_error_message(k5_context, k5ret, tctx));
992 :
993 1216 : torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
994 : /*
995 : * Only in these cases would the above code have needed to
996 : * send packets to the network
997 : */
998 1216 : torture_assert(tctx,
999 : test_context->packet_count > 0,
1000 : "Expected krb5_get_creds to send packets");
1001 : }
1002 : }
1003 :
1004 : /*
1005 : * Confirm getting a ticket for the same krbtgt/realm that we
1006 : * got back with the initial ticket, running the
1007 : * TEST_TGS_REQ_KRBTGT stage.
1008 : *
1009 : */
1010 :
1011 1440 : test_context->packet_count = 0;
1012 :
1013 1440 : in_data.length = 0;
1014 1440 : k5ret = krb5_mk_req_exact(k5_context,
1015 : &auth_context,
1016 : 0,
1017 : my_creds.server,
1018 : &in_data, ccache,
1019 : &enc_ticket);
1020 :
1021 1440 : assertion_message = talloc_asprintf(tctx,
1022 : "krb5_mk_req_exact for %s failed: %s",
1023 : principal_string,
1024 : smb_get_krb5_error_message(k5_context, k5ret, tctx));
1025 1440 : torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
1026 :
1027 1440 : krb5_free_principal(k5_context, principal);
1028 1440 : krb5_get_init_creds_opt_free(k5_context, krb_options);
1029 :
1030 1440 : torture_assert_int_equal(tctx, krb5_free_cred_contents(k5_context, &my_creds),
1031 : 0, "krb5_free_cred_contents failed");
1032 :
1033 1440 : return true;
1034 : }
1035 :
1036 1077 : struct torture_suite *torture_krb5_canon(TALLOC_CTX *mem_ctx)
1037 : {
1038 125 : unsigned int i;
1039 1077 : struct torture_suite *suite = torture_suite_create(mem_ctx, "canon");
1040 1077 : suite->description = talloc_strdup(suite, "Kerberos Canonicalisation tests");
1041 :
1042 1102848 : for (i = 0; i < TEST_ALL; i++) {
1043 5971251 : char *name = talloc_asprintf(suite, "%s.%s.%s.%s.%s.%s",
1044 1101771 : (i & TEST_CANONICALIZE) ? "canon" : "no-canon",
1045 1101771 : (i & TEST_ENTERPRISE) ? "enterprise" : "no-enterprise",
1046 1101771 : (i & TEST_UPPER_USERNAME) ? "uc-user" : "lc-user",
1047 1101771 : (i & TEST_WIN2K) ? "win2k" : "no-win2k",
1048 1101771 : (i & TEST_UPN) ? "upn" :
1049 795136 : ((i & TEST_AS_REQ_SPN) ? "spn" :
1050 275712 : ((i & TEST_REMOVEDOLLAR) ? "removedollar" : "samaccountname")),
1051 1101771 : (i & TEST_S4U2SELF) ? "s4u2self" : "normal");
1052 1101771 : struct torture_suite *sub_suite = torture_suite_create(mem_ctx, name);
1053 :
1054 1101771 : struct test_data *test_data = talloc_zero(suite, struct test_data);
1055 1101771 : if (i & TEST_UPN) {
1056 550347 : if (i & TEST_AS_REQ_SPN) {
1057 274635 : continue;
1058 : }
1059 : }
1060 827136 : if ((i & TEST_UPN) || (i & TEST_AS_REQ_SPN)) {
1061 551424 : if (i & TEST_REMOVEDOLLAR) {
1062 275712 : continue;
1063 : }
1064 : }
1065 :
1066 551424 : test_data->test_name = name;
1067 64000 : test_data->real_realm
1068 551424 : = strupper_talloc(test_data,
1069 : cli_credentials_get_realm(
1070 : samba_cmdline_get_creds()));
1071 551424 : test_data->real_domain = cli_credentials_get_domain(
1072 : samba_cmdline_get_creds());
1073 551424 : test_data->username = cli_credentials_get_username(
1074 : samba_cmdline_get_creds());
1075 551424 : test_data->real_username = cli_credentials_get_username(
1076 : samba_cmdline_get_creds());
1077 551424 : test_data->canonicalize = (i & TEST_CANONICALIZE) != 0;
1078 551424 : test_data->enterprise = (i & TEST_ENTERPRISE) != 0;
1079 551424 : test_data->upper_username = (i & TEST_UPPER_USERNAME) != 0;
1080 551424 : test_data->win2k = (i & TEST_WIN2K) != 0;
1081 551424 : test_data->upn = (i & TEST_UPN) != 0;
1082 551424 : test_data->s4u2self = (i & TEST_S4U2SELF) != 0;
1083 551424 : test_data->removedollar = (i & TEST_REMOVEDOLLAR) != 0;
1084 551424 : test_data->as_req_spn = (i & TEST_AS_REQ_SPN) != 0;
1085 551424 : torture_suite_add_simple_tcase_const(sub_suite, name, torture_krb5_as_req_canon,
1086 : test_data);
1087 551424 : torture_suite_add_suite(suite, sub_suite);
1088 :
1089 : }
1090 1077 : return suite;
1091 : }
|