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/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"
32 #include "auth/auth.h"
33 #include "auth/auth_sam_reply.h"
34 #include "auth/gensec/gensec.h"
35 #include "param/param.h"
36 #include "zlib.h"
37
38 #define TEST_CANONICALIZE 0x0000001
39 #define TEST_ENTERPRISE 0x0000002
40 #define TEST_UPPER_REALM 0x0000004
41 #define TEST_UPPER_USERNAME 0x0000008
42 #define TEST_NETBIOS_REALM 0x0000010
43 #define TEST_WIN2K 0x0000020
44 #define TEST_UPN 0x0000040
45 #define TEST_S4U2SELF 0x0000080
46 #define TEST_REMOVEDOLLAR 0x0000100
47 #define TEST_AS_REQ_SPN 0x0000200
48 #define TEST_MITM_S4U2SELF 0x0000400
49 #define TEST_ALL 0x00007FF
50
51 struct test_data {
52 const char *test_name;
53 const char *realm;
54 const char *real_realm;
55 const char *real_domain;
56 const char *username;
57 const char *real_username;
58 bool canonicalize;
59 bool enterprise;
60 bool upper_realm;
61 bool upper_username;
62 bool netbios_realm;
63 bool win2k;
64 bool upn;
65 bool other_upn_suffix;
66 bool s4u2self;
67 bool mitm_s4u2self;
68 bool removedollar;
69 bool as_req_spn;
70 bool spn_is_upn;
71 const char *krb5_service;
72 const char *krb5_hostname;
73 };
74
75 enum test_stage {
76 TEST_AS_REQ = 0,
77 TEST_TGS_REQ_KRBTGT_CANON = 1,
78 TEST_TGS_REQ_CANON = 2,
79 TEST_SELF_TRUST_TGS_REQ = 3,
80 TEST_TGS_REQ = 4,
81 TEST_TGS_REQ_KRBTGT = 5,
82 TEST_TGS_REQ_HOST = 6,
83 TEST_TGS_REQ_HOST_SRV_INST = 7,
84 TEST_TGS_REQ_HOST_SRV_HST = 8,
85 TEST_AS_REQ_SELF = 9,
86 TEST_DONE = 10
87 };
88
89 struct torture_krb5_context {
90 struct smb_krb5_context *smb_krb5_context;
91 struct torture_context *tctx;
92 struct addrinfo *server;
93 struct test_data *test_data;
94 int packet_count;
95 enum test_stage test_stage;
96 AS_REQ as_req;
97 AS_REP as_rep;
98 TGS_REQ tgs_req;
99 TGS_REP tgs_rep;
100 };
101
102 struct pac_data {
103 const char *principal_name;
104 };
105
106 /*
107 * A helper function which avoids touching the local databases to
108 * generate the session info, as we just want to verify the principal
109 * name that we found in the ticket not the full local token
110 */
test_generate_session_info_pac(struct auth4_context * auth_ctx,TALLOC_CTX * mem_ctx,struct smb_krb5_context * smb_krb5_context,DATA_BLOB * pac_blob,const char * principal_name,const struct tsocket_address * remote_address,uint32_t session_info_flags,struct auth_session_info ** session_info)111 static NTSTATUS test_generate_session_info_pac(struct auth4_context *auth_ctx,
112 TALLOC_CTX *mem_ctx,
113 struct smb_krb5_context *smb_krb5_context,
114 DATA_BLOB *pac_blob,
115 const char *principal_name,
116 const struct tsocket_address *remote_address,
117 uint32_t session_info_flags,
118 struct auth_session_info **session_info)
119 {
120 NTSTATUS nt_status;
121 struct auth_user_info_dc *user_info_dc;
122 TALLOC_CTX *tmp_ctx;
123 struct pac_data *pac_data;
124
125 tmp_ctx = talloc_named(mem_ctx, 0, "gensec_gssapi_session_info context");
126 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
127
128 auth_ctx->private_data = pac_data = talloc_zero(auth_ctx, struct pac_data);
129
130 pac_data->principal_name = talloc_strdup(pac_data, principal_name);
131 if (!pac_data->principal_name) {
132 talloc_free(tmp_ctx);
133 return NT_STATUS_NO_MEMORY;
134 }
135
136 nt_status = kerberos_pac_blob_to_user_info_dc(tmp_ctx,
137 *pac_blob,
138 smb_krb5_context->krb5_context,
139 &user_info_dc, NULL, NULL);
140 if (!NT_STATUS_IS_OK(nt_status)) {
141 talloc_free(tmp_ctx);
142 return nt_status;
143 }
144
145 if (user_info_dc->info->authenticated) {
146 session_info_flags |= AUTH_SESSION_INFO_AUTHENTICATED;
147 }
148
149 session_info_flags |= AUTH_SESSION_INFO_SIMPLE_PRIVILEGES;
150 nt_status = auth_generate_session_info(mem_ctx,
151 NULL,
152 NULL,
153 user_info_dc, session_info_flags,
154 session_info);
155 if (!NT_STATUS_IS_OK(nt_status)) {
156 talloc_free(tmp_ctx);
157 return nt_status;
158 }
159
160 talloc_free(tmp_ctx);
161 return NT_STATUS_OK;
162 }
163
164 /* Check to see if we can pass the PAC across to the NETLOGON server for validation */
165
166 /* Also happens to be a really good one-step verfication of our Kerberos stack */
167
test_accept_ticket(struct torture_context * tctx,struct cli_credentials * credentials,const char * principal,DATA_BLOB client_to_server)168 static bool test_accept_ticket(struct torture_context *tctx,
169 struct cli_credentials *credentials,
170 const char *principal,
171 DATA_BLOB client_to_server)
172 {
173 NTSTATUS status;
174 struct gensec_security *gensec_server_context;
175 DATA_BLOB server_to_client;
176 struct auth4_context *auth_context;
177 struct auth_session_info *session_info;
178 struct pac_data *pac_data;
179 TALLOC_CTX *tmp_ctx = talloc_new(tctx);
180
181 torture_assert(tctx, tmp_ctx != NULL, "talloc_new() failed");
182
183 auth_context = talloc_zero(tmp_ctx, struct auth4_context);
184 torture_assert(tctx, auth_context != NULL, "talloc_new() failed");
185
186 auth_context->generate_session_info_pac = test_generate_session_info_pac;
187
188 status = gensec_server_start(tctx,
189 lpcfg_gensec_settings(tctx, tctx->lp_ctx),
190 auth_context, &gensec_server_context);
191 torture_assert_ntstatus_ok(tctx, status, "gensec_server_start (server) failed");
192
193 status = gensec_set_credentials(gensec_server_context, credentials);
194 torture_assert_ntstatus_ok(tctx, status, "gensec_set_credentials (server) failed");
195
196 status = gensec_start_mech_by_name(gensec_server_context, "krb5");
197 torture_assert_ntstatus_ok(tctx, status, "gensec_start_mech_by_name (server) failed");
198
199 server_to_client = data_blob(NULL, 0);
200
201 /* Do a client-server update dance */
202 status = gensec_update(gensec_server_context, tmp_ctx, client_to_server, &server_to_client);
203 torture_assert_ntstatus_ok(tctx, status, "gensec_update (server) failed");
204
205 /* Extract the PAC using Samba's code */
206
207 status = gensec_session_info(gensec_server_context, gensec_server_context, &session_info);
208 torture_assert_ntstatus_ok(tctx, status, "gensec_session_info failed");
209
210 pac_data = talloc_get_type(auth_context->private_data, struct pac_data);
211
212 torture_assert(tctx, pac_data != NULL, "gensec_update failed to fill in pac_data in auth_context");
213 torture_assert(tctx, pac_data->principal_name != NULL, "principal_name not present");
214 torture_assert_str_equal(tctx, pac_data->principal_name, principal, "wrong principal name");
215 return true;
216 }
217
218 static void
zCRC32_checksum(const void * data,size_t len,Checksum * C)219 zCRC32_checksum(const void *data,
220 size_t len,
221 Checksum *C)
222 {
223 uint32_t *crc = C->checksum.data;
224 *crc = ~(crc32(0xffffffff, data, len));
225 C->checksum.length = 4;
226 C->cksumtype = 1;
227 }
228
229 krb5_error_code
230 _krb5_s4u2self_to_checksumdata(krb5_context context,
231 const PA_S4U2Self *self,
232 krb5_data *data);
233
234 /* Helper function to modify the principal in PA_FOR_USER padata */
change_for_user_principal(struct torture_krb5_context * test_context,krb5_data * modified_send_buf)235 static bool change_for_user_principal(struct torture_krb5_context *test_context,
236 krb5_data *modified_send_buf)
237 {
238 PA_DATA *for_user;
239 int i = 0;
240 size_t used;
241 krb5_error_code ret;
242 PA_S4U2Self self, mod_self;
243 krb5_data cksum_data;
244 krb5_principal admin;
245 heim_octet_string orig_padata_value;
246 krb5_context k5_ctx = test_context->smb_krb5_context->krb5_context;
247
248 for_user = krb5_find_padata(test_context->tgs_req.padata->val,
249 test_context->tgs_req.padata->len, KRB5_PADATA_FOR_USER, &i);
250 torture_assert(test_context->tctx, for_user != NULL, "No PA_FOR_USER in s4u2self request");
251 orig_padata_value = for_user->padata_value;
252
253 torture_assert_int_equal(test_context->tctx,
254 krb5_make_principal(k5_ctx, &admin, test_context->test_data->realm,
255 "Administrator", NULL),
256 0, "krb5_make_principal() failed");
257 torture_assert_int_equal(test_context->tctx,
258 decode_PA_S4U2Self(for_user->padata_value.data,
259 for_user->padata_value.length, &self, NULL),
260 0, "decode_PA_S4U2Self() failed");
261 mod_self = self;
262 mod_self.name = admin->name;
263
264 torture_assert_int_equal(test_context->tctx,
265 _krb5_s4u2self_to_checksumdata(k5_ctx, &mod_self, &cksum_data),
266 0, "_krb5_s4u2self_to_checksumdata() failed");
267 zCRC32_checksum(cksum_data.data, cksum_data.length, &mod_self.cksum);
268
269 ASN1_MALLOC_ENCODE(PA_S4U2Self, for_user->padata_value.data, for_user->padata_value.length,
270 &mod_self, &used, ret);
271 torture_assert(test_context->tctx, ret == 0, "Failed to encode PA_S4U2Self ASN1 struct");
272 ASN1_MALLOC_ENCODE(TGS_REQ, modified_send_buf->data, modified_send_buf->length,
273 &test_context->tgs_req, &used, ret);
274 torture_assert(test_context->tctx, ret == 0, "Failed to encode TGS_REQ ASN1 struct");
275
276 free(for_user->padata_value.data);
277 for_user->padata_value = orig_padata_value;
278
279 free_PA_S4U2Self(&self);
280 krb5_data_free(&cksum_data);
281
282 return true;
283 }
284
285 /*
286 * TEST_AS_REQ and TEST_AS_REQ_SELF - SEND
287 *
288 * Confirm that the outgoing packet meets certain expectations. This
289 * should be extended to further assert the correct and expected
290 * behaviour of the krb5 libs, so we know what we are sending to the
291 * server.
292 *
293 * Additionally, this CHANGES the request to remove the canonicalize
294 * flag automatically added by the krb5 libs when an enterprise
295 * principal is used, so we can test what the server does in this
296 * combination.
297 *
298 */
299
torture_krb5_pre_send_as_req_test(struct torture_krb5_context * test_context,const krb5_data * send_buf,krb5_data * modified_send_buf)300 static bool torture_krb5_pre_send_as_req_test(struct torture_krb5_context *test_context,
301 const krb5_data *send_buf,
302 krb5_data *modified_send_buf)
303 {
304 AS_REQ mod_as_req;
305 krb5_error_code k5ret;
306 size_t used;
307 torture_assert_int_equal(test_context->tctx, decode_AS_REQ(send_buf->data, send_buf->length,
308 &test_context->as_req, &used),
309 0, "decode_AS_REQ for TEST_AS_REQ failed");
310 mod_as_req = test_context->as_req;
311 torture_assert_int_equal(test_context->tctx, used, send_buf->length, "length mismatch");
312 torture_assert_int_equal(test_context->tctx, test_context->as_req.pvno,
313 5, "Got wrong as_req->pvno");
314 if (test_context->test_data->canonicalize
315 || test_context->test_data->enterprise) {
316 torture_assert(test_context->tctx,
317 test_context->as_req.req_body.kdc_options.canonicalize,
318 "krb5 libs did not set canonicalize!");
319 } else {
320 torture_assert_int_equal(test_context->tctx,
321 test_context->as_req.req_body.kdc_options.canonicalize,
322 false,
323 "krb5 libs unexpectedly set canonicalize!");
324 }
325
326 if (test_context->test_data->as_req_spn) {
327 if (test_context->test_data->upn) {
328 torture_assert_int_equal(test_context->tctx,
329 test_context->as_req.req_body.cname->name_type,
330 KRB5_NT_PRINCIPAL,
331 "krb5 libs unexpectedly "
332 "did not set principal "
333 "as NT_SRV_HST!");
334 } else {
335 torture_assert_int_equal(test_context->tctx,
336 test_context->as_req.req_body.cname->name_type,
337 KRB5_NT_SRV_HST,
338 "krb5 libs unexpectedly "
339 "did not set principal "
340 "as NT_SRV_HST!");
341 }
342 } else if (test_context->test_data->enterprise) {
343 torture_assert_int_equal(test_context->tctx,
344 test_context->as_req.req_body.cname->name_type,
345 KRB5_NT_ENTERPRISE_PRINCIPAL,
346 "krb5 libs did not pass principal as enterprise!");
347 } else {
348 torture_assert_int_equal(test_context->tctx,
349 test_context->as_req.req_body.cname->name_type,
350 KRB5_NT_PRINCIPAL,
351 "krb5 libs unexpectedly set principal as enterprise!");
352 }
353
354 /* Force off canonicalize that was forced on by the krb5 libs */
355 if (test_context->test_data->canonicalize == false && test_context->test_data->enterprise) {
356 mod_as_req.req_body.kdc_options.canonicalize = false;
357 }
358
359 if (test_context->test_stage == TEST_AS_REQ_SELF) {
360 /*
361 * Force the server name to match the client name,
362 * including the name type. This isn't possible with
363 * the krb5 client libs alone
364 */
365 mod_as_req.req_body.sname = test_context->as_req.req_body.cname;
366 }
367
368 ASN1_MALLOC_ENCODE(AS_REQ, modified_send_buf->data, modified_send_buf->length,
369 &mod_as_req, &used, k5ret);
370 torture_assert_int_equal(test_context->tctx,
371 k5ret, 0,
372 "encode_AS_REQ failed");
373
374 if (test_context->test_stage != TEST_AS_REQ_SELF) {
375 torture_assert_int_equal(test_context->tctx, used, send_buf->length,
376 "re-encode length mismatch");
377 }
378 return true;
379 }
380
381 /*
382 * TEST_AS_REQ - RECV
383 *
384 * Confirm that the reply packet from the KDC meets certain
385 * expectations as part of TEST_AS_REQ. This uses a packet count to
386 * work out what packet we are up to in the multiple exchanged
387 * triggerd by krb5_get_init_creds_password().
388 *
389 */
390
torture_krb5_post_recv_as_req_test(struct torture_krb5_context * test_context,const krb5_data * recv_buf)391 static bool torture_krb5_post_recv_as_req_test(struct torture_krb5_context *test_context,
392 const krb5_data *recv_buf)
393 {
394 KRB_ERROR error;
395 size_t used;
396 if (test_context->packet_count == 0) {
397 krb5_error_code k5ret;
398 /*
399 * The client libs obtain the salt by attempting to
400 * authenticate without pre-authentication and getting
401 * the correct salt with the
402 * KRB5KDC_ERR_PREAUTH_REQUIRED error. If we are in
403 * the test (netbios_realm && upn) that deliberatly
404 * has an incorrect principal, we check we get the
405 * correct error.
406 */
407 k5ret = decode_KRB_ERROR(recv_buf->data, recv_buf->length,
408 &error, &used);
409 if (k5ret != 0) {
410 AS_REP as_rep;
411 k5ret = decode_AS_REP(recv_buf->data, recv_buf->length,
412 &as_rep, &used);
413 if (k5ret == 0) {
414 if (test_context->test_data->netbios_realm && test_context->test_data->upn) {
415 torture_assert(test_context->tctx, false,
416 "expected to get a KRB_ERROR packet with "
417 "KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN, got valid AS-REP");
418 } else {
419 torture_assert(test_context->tctx, false,
420 "expected to get a KRB_ERROR packet with "
421 "KRB5KDC_ERR_PREAUTH_REQUIRED, got valid AS-REP");
422 }
423 } else {
424 if (test_context->test_data->netbios_realm && test_context->test_data->upn) {
425 torture_assert(test_context->tctx, false,
426 "unable to decode as KRB-ERROR or AS-REP, "
427 "expected to get a KRB_ERROR packet with KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN");
428 } else {
429 torture_assert(test_context->tctx, false,
430 "unable to decode as KRB-ERROR or AS-REP, "
431 "expected to get a KRB_ERROR packet with KRB5KDC_ERR_PREAUTH_REQUIRED");
432 }
433 }
434 }
435 torture_assert_int_equal(test_context->tctx, used, recv_buf->length,
436 "length mismatch");
437 torture_assert_int_equal(test_context->tctx, error.pvno, 5,
438 "Got wrong error.pvno");
439 if (test_context->test_data->netbios_realm && test_context->test_data->upn) {
440 torture_assert_int_equal(test_context->tctx,
441 error.error_code,
442 KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN - KRB5KDC_ERR_NONE,
443 "Got wrong error.error_code");
444 } else if (test_context->test_data->as_req_spn && !test_context->test_data->spn_is_upn) {
445 torture_assert_int_equal(test_context->tctx,
446 error.error_code,
447 KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN - KRB5KDC_ERR_NONE,
448 "Got wrong error.error_code");
449 } else {
450 torture_assert_int_equal(test_context->tctx,
451 error.error_code,
452 KRB5KDC_ERR_PREAUTH_REQUIRED - KRB5KDC_ERR_NONE,
453 "Got wrong error.error_code");
454 }
455
456 free_KRB_ERROR(&error);
457 } else if ((decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used) == 0)
458 && (test_context->packet_count == 1)) {
459 /*
460 * The Windows 2012R2 KDC will always respond with
461 * KRB5KRB_ERR_RESPONSE_TOO_BIG over UDP as the ticket
462 * won't fit, because of the PAC. (It appears to do
463 * this always, even if it will). This triggers the
464 * client to try again over TCP.
465 */
466 torture_assert_int_equal(test_context->tctx,
467 used, recv_buf->length,
468 "length mismatch");
469 torture_assert_int_equal(test_context->tctx,
470 error.pvno, 5,
471 "Got wrong error.pvno");
472 torture_assert_int_equal(test_context->tctx,
473 error.error_code,
474 KRB5KRB_ERR_RESPONSE_TOO_BIG - KRB5KDC_ERR_NONE,
475 "Got wrong error.error_code");
476 free_KRB_ERROR(&error);
477 } else {
478 /*
479 * Finally the successful packet.
480 */
481 torture_assert_int_equal(test_context->tctx,
482 decode_AS_REP(recv_buf->data, recv_buf->length,
483 &test_context->as_rep, &used), 0,
484 "decode_AS_REP failed");
485 torture_assert_int_equal(test_context->tctx, used, recv_buf->length,
486 "length mismatch");
487 torture_assert_int_equal(test_context->tctx,
488 test_context->as_rep.pvno, 5,
489 "Got wrong as_rep->pvno");
490 torture_assert_int_equal(test_context->tctx,
491 test_context->as_rep.ticket.tkt_vno, 5,
492 "Got wrong as_rep->ticket.tkt_vno");
493 torture_assert(test_context->tctx,
494 test_context->as_rep.ticket.enc_part.kvno,
495 "Did not get a KVNO in test_context->as_rep.ticket.enc_part.kvno");
496
497 /*
498 * We can confirm that the correct proxy behaviour is
499 * in use on the KDC by checking the KVNO of the
500 * krbtgt account returned in the reply.
501 *
502 * A packet passed to the full RW DC will not have a
503 * KVNO in the upper bits, while a packet processed
504 * locally on the RODC will have these bits filled in
505 * the msDS-SecondaryKrbTgtNumber
506 */
507 if (torture_setting_bool(test_context->tctx, "expect_cached_at_rodc", false)) {
508 torture_assert_int_not_equal(test_context->tctx,
509 *test_context->as_rep.ticket.enc_part.kvno & 0xFFFF0000,
510 0, "Did not get a RODC number in the KVNO");
511 } else {
512 torture_assert_int_equal(test_context->tctx,
513 *test_context->as_rep.ticket.enc_part.kvno & 0xFFFF0000,
514 0, "Unexpecedly got a RODC number in the KVNO");
515 }
516 free_AS_REP(&test_context->as_rep);
517 }
518 torture_assert(test_context->tctx, test_context->packet_count < 3, "too many packets");
519 free_AS_REQ(&test_context->as_req);
520 return true;
521 }
522
523 /*
524 * TEST_TGS_REQ_KRBTGT_CANON
525 *
526 *
527 * Confirm that the outgoing TGS-REQ packet from krb5_get_creds()
528 * for the krbtgt/realm principal meets certain expectations, like
529 * that the canonicalize bit is not set
530 *
531 */
532
torture_krb5_pre_send_tgs_req_krbtgt_canon_test(struct torture_krb5_context * test_context,const krb5_data * send_buf,krb5_data * modified_send_buf)533 static bool torture_krb5_pre_send_tgs_req_krbtgt_canon_test(struct torture_krb5_context *test_context, const krb5_data *send_buf, krb5_data *modified_send_buf)
534 {
535 size_t used;
536 torture_assert_int_equal(test_context->tctx,
537 decode_TGS_REQ(send_buf->data, send_buf->length,
538 &test_context->tgs_req, &used),
539 0, "decode_TGS_REQ for TEST_TGS_REQ test failed");
540 torture_assert_int_equal(test_context->tctx,
541 used, send_buf->length,
542 "length mismatch");
543 torture_assert_int_equal(test_context->tctx,
544 test_context->tgs_req.pvno, 5,
545 "Got wrong as_req->pvno");
546 torture_assert_int_equal(test_context->tctx,
547 test_context->tgs_req.req_body.kdc_options.canonicalize,
548 true,
549 "krb5 libs unexpectedly did not set canonicalize!");
550
551 torture_assert_int_equal(test_context->tctx,
552 test_context->tgs_req.req_body.sname->name_type,
553 KRB5_NT_PRINCIPAL,
554 "Mismatch in name_type between request and expected request");
555
556 torture_assert_str_equal(test_context->tctx,
557 test_context->tgs_req.req_body.realm,
558 test_context->test_data->real_realm,
559 "Mismatch in realm between request and expected request");
560
561 *modified_send_buf = *send_buf;
562 return true;
563 }
564
565 /*
566 * TEST_TGS_REQ_KRBTGT_CANON
567 *
568 * Confirm that the reply TGS-REP packet for krb5_get_creds()
569 * where the client is behaving as if this is a cross-realm trust due
570 * to case or netbios vs dns name differences meets certain
571 * expectations, while canonicalize is set
572 *
573 */
574
torture_krb5_post_recv_tgs_req_krbtgt_canon_test(struct torture_krb5_context * test_context,const krb5_data * recv_buf)575 static bool torture_krb5_post_recv_tgs_req_krbtgt_canon_test(struct torture_krb5_context *test_context, const krb5_data *recv_buf)
576 {
577 size_t used;
578 torture_assert_int_equal(test_context->tctx,
579 decode_TGS_REP(recv_buf->data, recv_buf->length,
580 &test_context->tgs_rep, &used),
581 0,
582 "decode_TGS_REP failed");
583 torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch");
584 torture_assert_int_equal(test_context->tctx,
585 test_context->tgs_rep.pvno, 5,
586 "Got wrong as_rep->pvno");
587 torture_assert_int_equal(test_context->tctx,
588 test_context->tgs_rep.ticket.tkt_vno, 5,
589 "Got wrong as_rep->ticket.tkt_vno");
590 torture_assert(test_context->tctx,
591 test_context->tgs_rep.ticket.enc_part.kvno,
592 "Did not get a KVNO in test_context->as_rep.ticket.enc_part.kvno");
593 torture_assert_str_equal(test_context->tctx,
594 test_context->tgs_req.req_body.realm,
595 test_context->tgs_rep.ticket.realm,
596 "Mismatch in realm between request and ticket response");
597 torture_assert_str_equal(test_context->tctx,
598 test_context->tgs_rep.ticket.realm,
599 test_context->test_data->real_realm,
600 "Mismatch in realm between ticket response and expected ticket response");
601 torture_assert_int_equal(test_context->tctx,
602 test_context->tgs_rep.ticket.sname.name_type,
603 KRB5_NT_SRV_INST,
604 "Mismatch in name_type between ticket response and expected value of KRB5_NT_SRV_INST");
605
606 torture_assert_int_equal(test_context->tctx,
607 test_context->tgs_rep.ticket.sname.name_string.len,
608 2,
609 "Mismatch in name_type between ticket response and expected value, expected krbtgt/REALM@REALM");
610
611 torture_assert_str_equal(test_context->tctx,
612 test_context->tgs_rep.ticket.sname.name_string.val[0], "krbtgt",
613 "Mismatch in name between response and expected response, expected krbtgt");
614 torture_assert_str_equal(test_context->tctx,
615 test_context->tgs_rep.ticket.sname.name_string.val[1], test_context->test_data->real_realm,
616 "Mismatch in realm part of krbtgt/ in expected response, expected krbtgt/REALM@REALM");
617
618 /*
619 * We can confirm that the correct proxy behaviour is
620 * in use on the KDC by checking the KVNO of the
621 * krbtgt account returned in the reply.
622 *
623 * A packet passed to the full RW DC will not have a
624 * KVNO in the upper bits, while a packet processed
625 * locally on the RODC will have these bits filled in
626 * the msDS-SecondaryKrbTgtNumber
627 */
628 if (torture_setting_bool(test_context->tctx, "expect_cached_at_rodc", false)) {
629 torture_assert_int_not_equal(test_context->tctx,
630 *test_context->tgs_rep.ticket.enc_part.kvno & 0xFFFF0000,
631 0, "Did not get a RODC number in the KVNO");
632 } else {
633 torture_assert_int_equal(test_context->tctx,
634 *test_context->tgs_rep.ticket.enc_part.kvno & 0xFFFF0000,
635 0, "Unexpecedly got a RODC number in the KVNO");
636 }
637 free_TGS_REP(&test_context->tgs_rep);
638 torture_assert(test_context->tctx,
639 test_context->packet_count < 2,
640 "too many packets");
641 free_TGS_REQ(&test_context->tgs_req);
642 return true;
643 }
644
645 /*
646 * TEST_TGS_REQ_CANON
647 *
648 * Confirm that the outgoing TGS-REQ packet from krb5_get_creds
649 * certain expectations, like that the canonicalize bit is set (this
650 * test is to force that handling) and that if an enterprise name was
651 * requested, that it was sent.
652 *
653 */
654
torture_krb5_pre_send_tgs_req_canon_test(struct torture_krb5_context * test_context,const krb5_data * send_buf,krb5_data * modified_send_buf)655 static bool torture_krb5_pre_send_tgs_req_canon_test(struct torture_krb5_context *test_context,
656 const krb5_data *send_buf,
657 krb5_data *modified_send_buf)
658 {
659 size_t used;
660 torture_assert_int_equal(test_context->tctx,
661 decode_TGS_REQ(send_buf->data, send_buf->length,
662 &test_context->tgs_req, &used),
663 0, "decode_TGS_REQ for TEST_TGS_REQ_CANON test failed");
664 torture_assert_int_equal(test_context->tctx, used, send_buf->length, "length mismatch");
665 torture_assert_int_equal(test_context->tctx, test_context->tgs_req.pvno, 5, "Got wrong as_req->pvno");
666 torture_assert_int_equal(test_context->tctx,
667 test_context->tgs_req.req_body.kdc_options.canonicalize,
668 true, "krb5 libs unexpectedly did not set canonicalize!");
669
670 if (test_context->test_data->enterprise) {
671 torture_assert_int_equal(test_context->tctx,
672 test_context->tgs_req.req_body.sname->name_type, KRB5_NT_ENTERPRISE_PRINCIPAL,
673 "Mismatch in name type between request and expected request, expected KRB5_NT_ENTERPRISE_PRINCIPAL");
674 torture_assert_str_equal(test_context->tctx,
675 test_context->tgs_req.req_body.realm, test_context->test_data->real_realm,
676 "Mismatch in realm between request and expected request");
677
678 } else if (test_context->test_data->as_req_spn) {
679 torture_assert_int_equal(test_context->tctx,
680 test_context->tgs_req.req_body.sname->name_type, KRB5_NT_SRV_HST,
681 "Mismatch in name type between request and expected request, expected KRB5_NT_SRV_HST");
682 torture_assert_str_equal(test_context->tctx,
683 test_context->tgs_req.req_body.realm, test_context->test_data->real_realm,
684 "Mismatch in realm between request and expected request");
685
686 } else if (test_context->test_data->canonicalize) {
687 torture_assert_int_equal(test_context->tctx,
688 test_context->tgs_req.req_body.sname->name_type, KRB5_NT_PRINCIPAL,
689 "Mismatch in name type between request and expected request, expected KRB5_NT_PRINCIPAL");
690 torture_assert_str_equal(test_context->tctx,
691 test_context->tgs_req.req_body.realm, test_context->test_data->real_realm,
692 "Mismatch in realm between request and expected request");
693
694 } else {
695 torture_assert_int_equal(test_context->tctx,
696 test_context->tgs_req.req_body.sname->name_type, KRB5_NT_PRINCIPAL,
697 "Mismatch in name type between request and expected request, expected KRB5_NT_PRINCIPAL");
698 torture_assert_str_equal(test_context->tctx,
699 test_context->tgs_req.req_body.realm, test_context->test_data->realm,
700 "Mismatch in realm between request and expected request");
701
702 }
703
704 if (test_context->test_data->mitm_s4u2self) {
705 torture_assert(test_context->tctx, change_for_user_principal(test_context, modified_send_buf),
706 "Failed to modify PA_FOR_USER principal name");
707 } else {
708 *modified_send_buf = *send_buf;
709 }
710
711 return true;
712 }
713
714 /*
715 * TEST_TGS_REQ_CANON - RECV
716 *
717 * Confirm that the reply TGS-REP or error packet from the KDC meets
718 * certain expectations as part of TEST_TGS_REQ_CANON.
719 *
720 * This is triggered by krb5_get_creds()
721 *
722 */
723
torture_krb5_post_recv_tgs_req_canon_test(struct torture_krb5_context * test_context,const krb5_data * recv_buf)724 static bool torture_krb5_post_recv_tgs_req_canon_test(struct torture_krb5_context *test_context, const krb5_data *recv_buf)
725 {
726 KRB_ERROR error;
727 size_t used;
728 krb5_error_code expected_error;
729
730 /*
731 * If this account did not have a servicePrincipalName, then
732 * we expect a errro packet, not a TGS-REQ
733 */
734 if (decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used) == 0) {
735 torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch");
736 torture_assert_int_equal(test_context->tctx,
737 error.pvno, 5,
738 "Got wrong error.pvno");
739 expected_error = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN - KRB5KDC_ERR_NONE;
740 if (!test_context->test_data->mitm_s4u2self) {
741 torture_assert_int_equal(test_context->tctx,
742 error.error_code,
743 expected_error,
744 "Got wrong error.error_code");
745 }
746 } else {
747 torture_assert_int_equal(test_context->tctx,
748 decode_TGS_REP(recv_buf->data, recv_buf->length,
749 &test_context->tgs_rep,
750 &used),
751 0,
752 "decode_TGS_REP failed");
753 torture_assert_int_equal(test_context->tctx,
754 used, recv_buf->length,
755 "length mismatch");
756 torture_assert_int_equal(test_context->tctx,
757 test_context->tgs_rep.pvno, 5,
758 "Got wrong as_rep->pvno");
759 torture_assert_int_equal(test_context->tctx,
760 test_context->tgs_rep.ticket.tkt_vno, 5,
761 "Got wrong as_rep->ticket.tkt_vno");
762 torture_assert(test_context->tctx,
763 test_context->tgs_rep.ticket.enc_part.kvno,
764 "Did not get a KVNO in test_context->as_rep.ticket.enc_part.kvno");
765 torture_assert_str_equal(test_context->tctx,
766 test_context->tgs_rep.ticket.realm,
767 test_context->test_data->real_realm,
768 "Mismatch in realm between ticket response and expected upper case REALM");
769 torture_assert_int_equal(test_context->tctx,
770 test_context->tgs_rep.ticket.sname.name_type,
771 test_context->tgs_req.req_body.sname->name_type,
772 "Mismatch in name_type between request and ticket response");
773 torture_assert_int_equal(test_context->tctx,
774 test_context->tgs_rep.ticket.sname.name_string.len,
775 test_context->tgs_req.req_body.sname->name_string.len,
776 "Mismatch in name_string.len between request and ticket response");
777 torture_assert(test_context->tctx,
778 test_context->tgs_rep.ticket.sname.name_string.len >= 1,
779 "name_string.len should be >=1 in ticket response");
780 torture_assert_str_equal(test_context->tctx,
781 test_context->tgs_rep.ticket.sname.name_string.val[0],
782 test_context->tgs_req.req_body.sname->name_string.val[0],
783 "Mismatch in name between request and expected request");
784 torture_assert_int_equal(test_context->tctx,
785 *test_context->tgs_rep.ticket.enc_part.kvno & 0xFFFF0000,
786 0, "Unexpecedly got a RODC number in the KVNO, should just be principal KVNO");
787 torture_assert(test_context->tctx, test_context->test_data->mitm_s4u2self == false,
788 "KDC accepted PA_S4U2Self with unkeyed checksum!");
789 free_TGS_REP(&test_context->tgs_rep);
790 }
791 torture_assert(test_context->tctx, test_context->packet_count == 0, "too many packets");
792 free_TGS_REQ(&test_context->tgs_req);
793
794 return true;
795 }
796
797 /*
798 * TEST_SELF_TRUST_TGS_REQ
799 *
800 * Confirm that the outgoing TGS-REQ packet from krb5_mk_req_exact()
801 * certain expectations, like that the canonicalize bit is set (this
802 * test is to force that handling).
803 *
804 * This test is for the case where the name we ask for, while a valid
805 * alternate name for our own realm is used. The client acts as if
806 * this is cross-realm trust.
807 *
808 */
809
torture_krb5_pre_send_self_trust_tgs_req_test(struct torture_krb5_context * test_context,const krb5_data * send_buf,krb5_data * modified_send_buf)810 static bool torture_krb5_pre_send_self_trust_tgs_req_test(struct torture_krb5_context *test_context,
811 const krb5_data *send_buf,
812 krb5_data *modified_send_buf)
813 {
814 size_t used;
815 torture_assert_int_equal(test_context->tctx,
816 decode_TGS_REQ(send_buf->data, send_buf->length,
817 &test_context->tgs_req, &used),
818 0, "decode_TGS_REQ for TEST_SELF_TRUST_TGS_REQ test failed");
819 torture_assert_int_equal(test_context->tctx, used, send_buf->length, "length mismatch");
820 torture_assert_int_equal(test_context->tctx, test_context->tgs_req.pvno, 5, "Got wrong as_req->pvno");
821
822 if (test_context->test_data->enterprise
823 || (test_context->test_data->spn_is_upn && test_context->test_data->upn)) {
824 torture_assert_int_equal(test_context->tctx,
825 test_context->tgs_req.req_body.kdc_options.canonicalize,
826 true,
827 "krb5 libs unexpectedly"
828 " did not set canonicalize!");
829 } else {
830 torture_assert_int_equal(test_context->tctx,
831 test_context->tgs_req.req_body.kdc_options.canonicalize,
832 false,
833 "krb5 libs unexpectedly"
834 " set canonicalize!");
835 }
836
837
838 if (test_context->test_data->canonicalize) {
839 torture_assert_str_equal(test_context->tctx,
840 test_context->tgs_req.req_body.realm,
841 test_context->test_data->real_realm,
842 "Mismatch in realm between request and expected request");
843 } else {
844 torture_assert_str_equal(test_context->tctx,
845 test_context->tgs_req.req_body.realm,
846 test_context->test_data->realm,
847 "Mismatch in realm between request and expected request");
848 }
849 torture_assert_int_equal(test_context->tctx,
850 test_context->tgs_req.req_body.sname->name_type, KRB5_NT_PRINCIPAL,
851 "Mismatch in name type between request and expected request, expected KRB5_NT_PRINCIPAL");
852 torture_assert_int_equal(test_context->tctx,
853 test_context->tgs_req.req_body.sname->name_string.len, 2,
854 "Mismatch in name between request and expected request, expected krbtgt/realm");
855 torture_assert_str_equal(test_context->tctx,
856 test_context->tgs_req.req_body.sname->name_string.val[0], "krbtgt",
857 "Mismatch in name between request and expected request, expected krbtgt");
858 torture_assert_str_equal(test_context->tctx,
859 test_context->tgs_req.req_body.sname->name_string.val[1], test_context->test_data->realm,
860 "Mismatch in realm part of cross-realm request principal between request and expected request");
861 *modified_send_buf = *send_buf;
862
863 return true;
864 }
865
866 /*
867 * TEST_SELF_TRUST_TGS_REQ and TEST_TGS_REQ_KRBTGT - RECV
868 *
869 * Confirm that the reply TGS-REP packet for krb5_mk_req_exact(),
870 * where the client is behaving as if this is a cross-realm trust due
871 * to case or netbios vs dns name differences meets certain
872 * expectations.
873 *
874 */
875
torture_krb5_post_recv_self_trust_tgs_req_test(struct torture_krb5_context * test_context,const krb5_data * recv_buf)876 static bool torture_krb5_post_recv_self_trust_tgs_req_test(struct torture_krb5_context *test_context, const krb5_data *recv_buf)
877 {
878 size_t used;
879 torture_assert_int_equal(test_context->tctx,
880 decode_TGS_REP(recv_buf->data, recv_buf->length,
881 &test_context->tgs_rep, &used),
882 0,
883 "decode_TGS_REP failed");
884 torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch");
885 torture_assert_int_equal(test_context->tctx,
886 test_context->tgs_rep.pvno, 5,
887 "Got wrong as_rep->pvno");
888 torture_assert_int_equal(test_context->tctx,
889 test_context->tgs_rep.ticket.tkt_vno, 5,
890 "Got wrong as_rep->ticket.tkt_vno");
891 torture_assert(test_context->tctx,
892 test_context->tgs_rep.ticket.enc_part.kvno,
893 "Did not get a KVNO in test_context->as_rep.ticket.enc_part.kvno");
894 torture_assert_str_equal(test_context->tctx,
895 test_context->tgs_req.req_body.realm,
896 test_context->tgs_rep.ticket.realm,
897 "Mismatch in realm between request and ticket response");
898 torture_assert_int_equal(test_context->tctx,
899 test_context->tgs_rep.ticket.sname.name_type,
900 test_context->tgs_req.req_body.sname->name_type,
901 "Mismatch in name_type between request and ticket response");
902
903 torture_assert_int_equal(test_context->tctx,
904 test_context->tgs_rep.ticket.sname.name_string.len, 2,
905 "Mismatch in name between request and expected request, expected krbtgt/realm");
906 torture_assert_str_equal(test_context->tctx,
907 test_context->tgs_rep.ticket.sname.name_string.val[0], "krbtgt",
908 "Mismatch in name between request and expected request, expected krbtgt");
909 torture_assert_str_equal(test_context->tctx,
910 test_context->tgs_rep.ticket.sname.name_string.val[1], test_context->test_data->realm,
911 "Mismatch in realm part of cross-realm request principal between response and expected request");
912 /*
913 * We can confirm that the correct proxy behaviour is
914 * in use on the KDC by checking the KVNO of the
915 * krbtgt account returned in the reply.
916 *
917 * A packet passed to the full RW DC will not have a
918 * KVNO in the upper bits, while a packet processed
919 * locally on the RODC will have these bits filled in
920 * the msDS-SecondaryKrbTgtNumber
921 */
922 if (torture_setting_bool(test_context->tctx, "expect_cached_at_rodc", false)) {
923 torture_assert_int_not_equal(test_context->tctx,
924 *test_context->tgs_rep.ticket.enc_part.kvno & 0xFFFF0000,
925 0, "Did not get a RODC number in the KVNO");
926 } else {
927 torture_assert_int_equal(test_context->tctx,
928 *test_context->tgs_rep.ticket.enc_part.kvno & 0xFFFF0000,
929 0, "Unexpecedly got a RODC number in the KVNO");
930 }
931 free_TGS_REP(&test_context->tgs_rep);
932 torture_assert_int_equal(test_context->tctx,
933 test_context->packet_count, 0,
934 "too many packets");
935 test_context->packet_count = 0;
936 test_context->test_stage = TEST_TGS_REQ;
937 free_TGS_REQ(&test_context->tgs_req);
938 return true;
939 }
940
941 /*
942 * TEST_TGS_REQ
943 *
944 * Confirm that the outgoing TGS-REQ packet from krb5_mk_req_exact()
945 * certain expectations, like that the canonicalize bit is set (this
946 * test is to force that handling) and that if an enterprise name was
947 * requested, that it was sent.
948 *
949 */
950
torture_krb5_pre_send_tgs_req_test(struct torture_krb5_context * test_context,const krb5_data * send_buf,krb5_data * modified_send_buf)951 static bool torture_krb5_pre_send_tgs_req_test(struct torture_krb5_context *test_context, const krb5_data *send_buf, krb5_data *modified_send_buf)
952 {
953 size_t used;
954 torture_assert_int_equal(test_context->tctx,
955 decode_TGS_REQ(send_buf->data, send_buf->length,
956 &test_context->tgs_req, &used),
957 0, "decode_TGS_REQ for TEST_TGS_REQ test failed");
958 torture_assert_int_equal(test_context->tctx, used, send_buf->length, "length mismatch");
959 torture_assert_int_equal(test_context->tctx, test_context->tgs_req.pvno, 5,
960 "Got wrong as_req->pvno");
961
962 if (test_context->test_data->enterprise
963 && test_context->test_data->s4u2self == false
964 && test_context->test_data->spn_is_upn) {
965 torture_assert_int_equal(test_context->tctx,
966 test_context->tgs_req.req_body.kdc_options.canonicalize,
967 true,
968 "krb5 libs unexpectedly"
969 " did not set canonicalize!");
970 } else {
971 torture_assert_int_equal(test_context->tctx,
972 test_context->tgs_req.req_body.kdc_options.canonicalize,
973 false,
974 "krb5 libs unexpectedly"
975 " set canonicalize!");
976 }
977
978 if (test_context->test_data->enterprise) {
979 torture_assert_int_equal(test_context->tctx,
980 test_context->tgs_req.req_body.sname->name_type,
981 KRB5_NT_ENTERPRISE_PRINCIPAL,
982 "Mismatch in name type between request and expected request, expected KRB5_NT_ENTERPRISE_PRINCIPAL");
983 torture_assert_str_equal(test_context->tctx,
984 test_context->tgs_req.req_body.realm,
985 test_context->test_data->real_realm,
986 "Mismatch in realm between request and expected request");
987
988 } else if (test_context->test_data->spn_is_upn && test_context->test_data->upn && test_context->test_data->canonicalize) {
989 torture_assert_int_equal(test_context->tctx,
990 test_context->tgs_req.req_body.sname->name_type,
991 KRB5_NT_PRINCIPAL,
992 "Mismatch in name type between request and expected request, expected KRB5_NT_PRINCIPAL");
993 torture_assert_str_equal(test_context->tctx,
994 test_context->tgs_req.req_body.realm,
995 test_context->test_data->real_realm,
996 "Mismatch in realm between request and expected request");
997
998 } else if (test_context->test_data->spn_is_upn
999 && test_context->test_data->as_req_spn
1000 && test_context->test_data->canonicalize == false) {
1001 torture_assert_int_equal(test_context->tctx,
1002 test_context->tgs_req.req_body.sname->name_type,
1003 KRB5_NT_SRV_HST,
1004 "Mismatch in name type between request and expected request, expected KRB5_NT_SRV_HST");
1005 torture_assert_str_equal(test_context->tctx,
1006 test_context->tgs_req.req_body.realm,
1007 test_context->test_data->realm,
1008 "Mismatch in realm between request and expected request");
1009
1010 } else {
1011 torture_assert_int_equal(test_context->tctx,
1012 test_context->tgs_req.req_body.sname->name_type,
1013 KRB5_NT_PRINCIPAL,
1014 "Mismatch in name type between request and expected request, expected KRB5_NT_PRINCIPAL");
1015 torture_assert_str_equal(test_context->tctx,
1016 test_context->tgs_req.req_body.realm,
1017 test_context->test_data->realm,
1018 "Mismatch in realm between request and expected request");
1019
1020 }
1021
1022 *modified_send_buf = *send_buf;
1023
1024 return true;
1025 }
1026
1027 /*
1028 * TEST_TGS_REQ - RECV
1029 *
1030 * Confirm that the reply TGS-REP packet for krb5_mk_req_exact(), for
1031 * the actual target service.
1032 *
1033 */
1034
torture_krb5_post_recv_tgs_req_test(struct torture_krb5_context * test_context,const krb5_data * recv_buf)1035 static bool torture_krb5_post_recv_tgs_req_test(struct torture_krb5_context *test_context, const krb5_data *recv_buf)
1036 {
1037 KRB_ERROR error;
1038 size_t used;
1039 /*
1040 * If this account did not have a servicePrincipalName, then
1041 * we expect a errro packet, not a TGS-REQ
1042 */
1043 if (decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used) == 0) {
1044 torture_assert_int_equal(test_context->tctx,
1045 used, recv_buf->length,
1046 "length mismatch");
1047 torture_assert_int_equal(test_context->tctx,
1048 error.pvno, 5,
1049 "Got wrong error.pvno");
1050 torture_assert_int_equal(test_context->tctx,
1051 error.error_code,
1052 KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN - KRB5KDC_ERR_NONE,
1053 "Got wrong error.error_code");
1054 } else {
1055 torture_assert_int_equal(test_context->tctx,
1056 decode_TGS_REP(recv_buf->data, recv_buf->length,
1057 &test_context->tgs_rep, &used),
1058 0,
1059 "decode_TGS_REP failed");
1060 torture_assert_int_equal(test_context->tctx, used, recv_buf->length,
1061 "length mismatch");
1062 torture_assert_int_equal(test_context->tctx,
1063 test_context->tgs_rep.pvno, 5,
1064 "Got wrong as_rep->pvno");
1065 torture_assert_int_equal(test_context->tctx,
1066 test_context->tgs_rep.ticket.tkt_vno, 5,
1067 "Got wrong as_rep->ticket.tkt_vno");
1068 torture_assert(test_context->tctx,
1069 test_context->tgs_rep.ticket.enc_part.kvno,
1070 "Did not get a KVNO in test_context->as_rep.ticket.enc_part.kvno");
1071 torture_assert_str_equal(test_context->tctx,
1072 test_context->tgs_rep.ticket.realm,
1073 test_context->test_data->real_realm,
1074 "Mismatch in realm between ticket response and expected upper case REALM");
1075 torture_assert_int_equal(test_context->tctx,
1076 test_context->tgs_req.req_body.sname->name_type,
1077 test_context->tgs_rep.ticket.sname.name_type, "Mismatch in name_type between request and ticket response");
1078 torture_assert_int_equal(test_context->tctx,
1079 *test_context->tgs_rep.ticket.enc_part.kvno & 0xFFFF0000,
1080 0, "Unexpecedly got a RODC number in the KVNO, should just be principal KVNO");
1081 free_TGS_REP(&test_context->tgs_rep);
1082 }
1083 torture_assert(test_context->tctx, test_context->packet_count < 3, "too many packets");
1084 free_TGS_REQ(&test_context->tgs_req);
1085 test_context->test_stage = TEST_DONE;
1086 return true;
1087 }
1088
1089 /*
1090 * TEST_TGS_REQ_KRBTGT
1091 *
1092 *
1093 * Confirm that the outgoing TGS-REQ packet from krb5_mk_req_exact()
1094 * for the krbtgt/realm principal meets certain expectations, like
1095 * that the canonicalize bit is not set
1096 *
1097 */
1098
torture_krb5_pre_send_tgs_req_krbtgt_test(struct torture_krb5_context * test_context,const krb5_data * send_buf,krb5_data * modified_send_buf)1099 static bool torture_krb5_pre_send_tgs_req_krbtgt_test(struct torture_krb5_context *test_context, const krb5_data *send_buf, krb5_data *modified_send_buf)
1100 {
1101 size_t used;
1102 torture_assert_int_equal(test_context->tctx,
1103 decode_TGS_REQ(send_buf->data, send_buf->length,
1104 &test_context->tgs_req, &used),
1105 0, "decode_TGS_REQ for TEST_TGS_REQ test failed");
1106 torture_assert_int_equal(test_context->tctx,
1107 used, send_buf->length,
1108 "length mismatch");
1109 torture_assert_int_equal(test_context->tctx,
1110 test_context->tgs_req.pvno, 5,
1111 "Got wrong as_req->pvno");
1112 torture_assert_int_equal(test_context->tctx,
1113 test_context->tgs_req.req_body.kdc_options.canonicalize,
1114 false,
1115 "krb5 libs unexpectedly set canonicalize!");
1116
1117 torture_assert_str_equal(test_context->tctx,
1118 test_context->tgs_req.req_body.realm,
1119 test_context->test_data->realm,
1120 "Mismatch in realm between request and expected request");
1121
1122 *modified_send_buf = *send_buf;
1123 test_context->test_stage = TEST_DONE;
1124 return true;
1125 }
1126
1127 /*
1128 * TEST_TGS_REQ_HOST, TEST_TGS_REQ_HOST_SRV_INST and TEST_TGS_REQ_HOST_SRV_HST
1129 *
1130 *
1131 * Confirm that the outgoing TGS-REQ packet from krb5_mk_req_exact()
1132 * for the krbtgt/realm principal meets certain expectations, like
1133 * that the canonicalize bit is not set
1134 *
1135 */
1136
torture_krb5_pre_send_tgs_req_host_test(struct torture_krb5_context * test_context,const krb5_data * send_buf,krb5_data * modified_send_buf)1137 static bool torture_krb5_pre_send_tgs_req_host_test(struct torture_krb5_context *test_context, const krb5_data *send_buf, krb5_data *modified_send_buf)
1138 {
1139 size_t used;
1140 torture_assert_int_equal(test_context->tctx,
1141 decode_TGS_REQ(send_buf->data, send_buf->length,
1142 &test_context->tgs_req, &used),
1143 0, "decode_TGS_REQ for TEST_TGS_REQ test failed");
1144 torture_assert_int_equal(test_context->tctx,
1145 used, send_buf->length,
1146 "length mismatch");
1147 torture_assert_int_equal(test_context->tctx,
1148 test_context->tgs_req.pvno, 5,
1149 "Got wrong as_req->pvno");
1150 torture_assert_int_equal(test_context->tctx,
1151 test_context->tgs_req.req_body.sname->name_string.len, 2,
1152 "Mismatch in name between request and expected request, expected krbtgt/realm");
1153 torture_assert_int_equal(test_context->tctx,
1154 test_context->tgs_req.req_body.kdc_options.canonicalize,
1155 true,
1156 "krb5 libs unexpectedly did not set canonicalize!");
1157
1158 if (test_context->test_stage == TEST_TGS_REQ_HOST_SRV_INST) {
1159 torture_assert_int_equal(test_context->tctx,
1160 test_context->tgs_req.req_body.sname->name_type, KRB5_NT_SRV_INST,
1161 "Mismatch in name type between request and expected request, expected KRB5_NT_SRV_INST");
1162 torture_assert_str_equal(test_context->tctx,
1163 test_context->tgs_req.req_body.sname->name_string.val[0],
1164 strupper_talloc(test_context, test_context->test_data->krb5_service),
1165 "Mismatch in name between request and expected request, expected service");
1166 torture_assert_str_equal(test_context->tctx,
1167 test_context->tgs_req.req_body.sname->name_string.val[1],
1168 test_context->test_data->krb5_hostname,
1169 "Mismatch in hostname part between request and expected request");
1170
1171 } else if (test_context->test_stage == TEST_TGS_REQ_HOST_SRV_HST) {
1172
1173 torture_assert_int_equal(test_context->tctx,
1174 test_context->tgs_req.req_body.sname->name_type, KRB5_NT_SRV_HST,
1175 "Mismatch in name type between request and expected request, expected KRB5_NT_SRV_HST");
1176 torture_assert_str_equal(test_context->tctx,
1177 test_context->tgs_req.req_body.sname->name_string.val[0],
1178 test_context->test_data->krb5_service,
1179 "Mismatch in name between request and expected request, expected service");
1180 torture_assert_str_equal(test_context->tctx,
1181 test_context->tgs_req.req_body.sname->name_string.val[1],
1182 strupper_talloc(test_context, test_context->test_data->krb5_hostname),
1183 "Mismatch in hostname part between request and expected request");
1184
1185 } else {
1186 torture_assert_int_equal(test_context->tctx,
1187 test_context->tgs_req.req_body.sname->name_type, KRB5_NT_PRINCIPAL,
1188 "Mismatch in name type between request and expected request, expected KRB5_NT_PRINCIPAL");
1189 torture_assert_str_equal(test_context->tctx,
1190 test_context->tgs_req.req_body.sname->name_string.val[0],
1191 test_context->test_data->krb5_service,
1192 "Mismatch in name between request and expected request, expected service");
1193 torture_assert_str_equal(test_context->tctx,
1194 test_context->tgs_req.req_body.sname->name_string.val[1],
1195 test_context->test_data->krb5_hostname,
1196 "Mismatch in hostname part between request and expected request");
1197
1198 }
1199 torture_assert_str_equal(test_context->tctx,
1200 test_context->tgs_req.req_body.realm,
1201 test_context->test_data->real_realm,
1202 "Mismatch in realm between request and expected request");
1203
1204 *modified_send_buf = *send_buf;
1205 return true;
1206 }
1207
1208 /*
1209 * TEST_TGS_REQ_HOST, TEST_TGS_REQ_HOST_SRV_INST, TEST_TGS_REQ_HOST_SRV_HST - RECV
1210 *
1211 * Confirm that the reply TGS-REP packet for krb5_mk_req(), for
1212 * the actual target service, as a SPN, not a any other name type.
1213 *
1214 */
1215
torture_krb5_post_recv_tgs_req_host_test(struct torture_krb5_context * test_context,const krb5_data * recv_buf)1216 static bool torture_krb5_post_recv_tgs_req_host_test(struct torture_krb5_context *test_context, const krb5_data *recv_buf)
1217 {
1218 size_t used;
1219 torture_assert_int_equal(test_context->tctx,
1220 decode_TGS_REP(recv_buf->data, recv_buf->length,
1221 &test_context->tgs_rep, &used),
1222 0,
1223 "decode_TGS_REP failed");
1224 torture_assert_int_equal(test_context->tctx, used, recv_buf->length,
1225 "length mismatch");
1226 torture_assert_int_equal(test_context->tctx,
1227 test_context->tgs_rep.pvno, 5,
1228 "Got wrong as_rep->pvno");
1229 torture_assert_int_equal(test_context->tctx,
1230 test_context->tgs_rep.ticket.tkt_vno, 5,
1231 "Got wrong as_rep->ticket.tkt_vno");
1232 torture_assert(test_context->tctx,
1233 test_context->tgs_rep.ticket.enc_part.kvno,
1234 "Did not get a KVNO in test_context->as_rep.ticket.enc_part.kvno");
1235 torture_assert_str_equal(test_context->tctx,
1236 test_context->tgs_rep.ticket.realm,
1237 test_context->test_data->real_realm,
1238 "Mismatch in realm between ticket response and expected upper case REALM");
1239 torture_assert_int_equal(test_context->tctx,
1240 test_context->tgs_req.req_body.sname->name_type,
1241 test_context->tgs_rep.ticket.sname.name_type, "Mismatch in name_type between request and ticket response");
1242 torture_assert_int_equal(test_context->tctx,
1243 test_context->tgs_rep.ticket.sname.name_string.len, 2,
1244 "Mismatch in name between request and expected request, expected service/hostname");
1245 torture_assert_str_equal(test_context->tctx,
1246 test_context->tgs_rep.ticket.sname.name_string.val[0],
1247 test_context->tgs_req.req_body.sname->name_string.val[0],
1248 "Mismatch in name between request and expected request, expected service/hostname");
1249 torture_assert_str_equal(test_context->tctx,
1250 test_context->tgs_rep.ticket.sname.name_string.val[1],
1251 test_context->tgs_req.req_body.sname->name_string.val[1],
1252 "Mismatch in name between request and expected request, expected service/hostname");
1253
1254 torture_assert_int_equal(test_context->tctx,
1255 *test_context->tgs_rep.ticket.enc_part.kvno & 0xFFFF0000,
1256 0, "Unexpecedly got a RODC number in the KVNO, should just be principal KVNO");
1257 free_TGS_REP(&test_context->tgs_rep);
1258
1259 torture_assert(test_context->tctx, test_context->packet_count < 2, "too many packets");
1260 return true;
1261 }
1262
1263 /*
1264 * TEST_AS_REQ_SELF - RECV
1265 *
1266 * Confirm that the reply packet from the KDC meets certain
1267 * expectations as part of TEST_AS_REQ. This uses a packet count to
1268 * work out what packet we are up to in the multiple exchanged
1269 * triggerd by krb5_get_init_creds_password().
1270 *
1271 */
1272
torture_krb5_post_recv_as_req_self_test(struct torture_krb5_context * test_context,const krb5_data * recv_buf)1273 static bool torture_krb5_post_recv_as_req_self_test(struct torture_krb5_context *test_context,
1274 const krb5_data *recv_buf)
1275 {
1276 KRB_ERROR error;
1277 size_t used;
1278 if (test_context->packet_count == 0) {
1279 krb5_error_code k5ret;
1280 /*
1281 * The client libs obtain the salt by attempting to
1282 * authenticate without pre-authentication and getting
1283 * the correct salt with the
1284 * KRB5KDC_ERR_PREAUTH_REQUIRED error. If we are in
1285 * the test (netbios_realm && upn) that deliberatly
1286 * has an incorrect principal, we check we get the
1287 * correct error.
1288 */
1289 k5ret = decode_KRB_ERROR(recv_buf->data, recv_buf->length,
1290 &error, &used);
1291 if (k5ret != 0) {
1292 AS_REP as_rep;
1293 k5ret = decode_AS_REP(recv_buf->data, recv_buf->length,
1294 &as_rep, &used);
1295 if (k5ret == 0) {
1296 if (torture_setting_bool(test_context->tctx, "expect_machine_account", false) == false
1297 || (test_context->test_data->upn == true)) {
1298 torture_assert(test_context->tctx, false,
1299 "expected to get a KRB_ERROR packet with "
1300 "KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN or KRB5KDC_ERR_PREAUTH_REQUIRED, got valid AS-REP");
1301 } else {
1302 torture_assert(test_context->tctx, false,
1303 "expected to get a KRB_ERROR packet with "
1304 "KRB5KDC_ERR_PREAUTH_REQUIRED, got valid AS-REP");
1305 }
1306 } else {
1307 if (torture_setting_bool(test_context->tctx, "expect_machine_account", false) == false
1308 || (test_context->test_data->upn == true)) {
1309 torture_assert(test_context->tctx, false,
1310 "unable to decode as KRB-ERROR or AS-REP, "
1311 "expected to get a KRB_ERROR packet with KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN or KRB5KDC_ERR_PREAUTH_REQUIRED");
1312 } else {
1313 torture_assert(test_context->tctx, false,
1314 "unable to decode as KRB-ERROR or AS-REP, "
1315 "expected to get a KRB_ERROR packet with KRB5KDC_ERR_PREAUTH_REQUIRED");
1316 }
1317 }
1318 }
1319 torture_assert_int_equal(test_context->tctx, used, recv_buf->length,
1320 "length mismatch");
1321 torture_assert_int_equal(test_context->tctx, error.pvno, 5,
1322 "Got wrong error.pvno");
1323 if ((torture_setting_bool(test_context->tctx, "expect_machine_account", false) == false
1324 || (test_context->test_data->upn == true))
1325 && error.error_code == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN - KRB5KDC_ERR_NONE) {
1326 /*
1327 * IGNORE
1328 *
1329 * This case is because Samba's Heimdal KDC
1330 * checks server and client accounts before
1331 * checking for pre-authentication.
1332 */
1333 } else {
1334 torture_assert_int_equal(test_context->tctx,
1335 error.error_code,
1336 KRB5KDC_ERR_PREAUTH_REQUIRED - KRB5KDC_ERR_NONE,
1337 "Got wrong error.error_code");
1338 }
1339
1340 free_KRB_ERROR(&error);
1341 } else if ((decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used) == 0)
1342 && (test_context->packet_count == 1)) {
1343 /*
1344 * The Windows 2012R2 KDC will always respond with
1345 * KRB5KRB_ERR_RESPONSE_TOO_BIG over UDP as the ticket
1346 * won't fit, because of the PAC. (It appears to do
1347 * this always, even if it will). This triggers the
1348 * client to try again over TCP.
1349 */
1350 torture_assert_int_equal(test_context->tctx,
1351 used, recv_buf->length,
1352 "length mismatch");
1353 torture_assert_int_equal(test_context->tctx,
1354 error.pvno, 5,
1355 "Got wrong error.pvno");
1356 if ((torture_setting_bool(test_context->tctx, "expect_machine_account", false)
1357 && ((test_context->test_data->upn == false)
1358 || (test_context->test_data->as_req_spn &&
1359 test_context->test_data->spn_is_upn)
1360 || (test_context->test_data->enterprise == false &&
1361 test_context->test_data->upn &&
1362 test_context->test_data->spn_is_upn)))) {
1363 torture_assert_int_equal(test_context->tctx,
1364 error.error_code,
1365 KRB5KRB_ERR_RESPONSE_TOO_BIG - KRB5KDC_ERR_NONE,
1366 "Got wrong error.error_code");
1367 } else {
1368 torture_assert_int_equal(test_context->tctx,
1369 error.error_code,
1370 KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN - KRB5KDC_ERR_NONE,
1371 "Got wrong error.error_code");
1372 }
1373 free_KRB_ERROR(&error);
1374 } else {
1375 /*
1376 * Finally the successful packet.
1377 */
1378 torture_assert_int_equal(test_context->tctx,
1379 decode_AS_REP(recv_buf->data, recv_buf->length,
1380 &test_context->as_rep, &used), 0,
1381 "decode_AS_REP failed");
1382 torture_assert_int_equal(test_context->tctx, used, recv_buf->length,
1383 "length mismatch");
1384 torture_assert_int_equal(test_context->tctx,
1385 test_context->as_rep.pvno, 5,
1386 "Got wrong as_rep->pvno");
1387 torture_assert_int_equal(test_context->tctx,
1388 test_context->as_rep.ticket.tkt_vno, 5,
1389 "Got wrong as_rep->ticket.tkt_vno");
1390 torture_assert(test_context->tctx,
1391 test_context->as_rep.ticket.enc_part.kvno,
1392 "Did not get a KVNO in test_context->as_rep.ticket.enc_part.kvno");
1393
1394 /*
1395 * We do not expect an RODC number here in the KVNO,
1396 * as this is a ticket to the user's own account.
1397 */
1398 torture_assert_int_equal(test_context->tctx,
1399 *test_context->as_rep.ticket.enc_part.kvno & 0xFFFF0000,
1400 0, "Unexpecedly got a RODC number in the KVNO");
1401 free_AS_REP(&test_context->as_rep);
1402 }
1403 torture_assert(test_context->tctx, test_context->packet_count < 3, "too many packets");
1404 free_AS_REQ(&test_context->as_req);
1405 return true;
1406 }
1407
1408 /*
1409 * This function is set in torture_krb5_init_context_canon as krb5
1410 * send_and_recv function. This allows us to override what server the
1411 * test is aimed at, and to inspect the packets just before they are
1412 * sent to the network, and before they are processed on the recv
1413 * side.
1414 *
1415 * The torture_krb5_pre_send_test() and torture_krb5_post_recv_test()
1416 * functions are implement the actual tests.
1417 *
1418 * When this asserts, the caller will get a spurious 'cannot contact
1419 * any KDC' message.
1420 *
1421 */
smb_krb5_send_and_recv_func_canon_override(krb5_context context,void * data,krb5_krbhst_info * hi,time_t timeout,const krb5_data * send_buf,krb5_data * recv_buf)1422 static krb5_error_code smb_krb5_send_and_recv_func_canon_override(krb5_context context,
1423 void *data, /* struct torture_krb5_context */
1424 krb5_krbhst_info *hi,
1425 time_t timeout,
1426 const krb5_data *send_buf,
1427 krb5_data *recv_buf)
1428 {
1429 krb5_error_code k5ret;
1430 bool ok = false;
1431 krb5_data modified_send_buf;
1432
1433 struct torture_krb5_context *test_context
1434 = talloc_get_type_abort(data, struct torture_krb5_context);
1435
1436 switch (test_context->test_stage) {
1437 case TEST_DONE:
1438 torture_warning(test_context->tctx, "Unexpected outgoing packet from krb5 libs");
1439 return EINVAL;
1440 case TEST_AS_REQ:
1441 ok = torture_krb5_pre_send_as_req_test(test_context, send_buf,
1442 &modified_send_buf);
1443 break;
1444 case TEST_TGS_REQ_KRBTGT_CANON:
1445 ok = torture_krb5_pre_send_tgs_req_krbtgt_canon_test(test_context, send_buf,
1446 &modified_send_buf);
1447 break;
1448 case TEST_TGS_REQ_CANON:
1449 ok = torture_krb5_pre_send_tgs_req_canon_test(test_context, send_buf,
1450 &modified_send_buf);
1451 break;
1452 case TEST_SELF_TRUST_TGS_REQ:
1453 ok = torture_krb5_pre_send_self_trust_tgs_req_test(test_context, send_buf,
1454 &modified_send_buf);
1455 break;
1456 case TEST_TGS_REQ:
1457 ok = torture_krb5_pre_send_tgs_req_test(test_context, send_buf,
1458 &modified_send_buf);
1459 break;
1460 case TEST_TGS_REQ_KRBTGT:
1461 ok = torture_krb5_pre_send_tgs_req_krbtgt_test(test_context, send_buf,
1462 &modified_send_buf);
1463 break;
1464 case TEST_TGS_REQ_HOST:
1465 case TEST_TGS_REQ_HOST_SRV_INST:
1466 case TEST_TGS_REQ_HOST_SRV_HST:
1467 ok = torture_krb5_pre_send_tgs_req_host_test(test_context, send_buf,
1468 &modified_send_buf);
1469 break;
1470 case TEST_AS_REQ_SELF:
1471 ok = torture_krb5_pre_send_as_req_test(test_context, send_buf,
1472 &modified_send_buf);
1473 break;
1474 }
1475 if (ok == false) {
1476 return EINVAL;
1477 }
1478
1479 k5ret = smb_krb5_send_and_recv_func_forced(context, test_context->server,
1480 hi, timeout, &modified_send_buf,
1481 recv_buf);
1482 if (k5ret != 0) {
1483 return k5ret;
1484 }
1485
1486 switch (test_context->test_stage) {
1487 case TEST_DONE:
1488 torture_warning(test_context->tctx, "Unexpected outgoing packet from krb5 libs");
1489 return EINVAL;
1490 case TEST_AS_REQ:
1491 ok = torture_krb5_post_recv_as_req_test(test_context, recv_buf);
1492 break;
1493 case TEST_TGS_REQ_KRBTGT_CANON:
1494 ok = torture_krb5_post_recv_tgs_req_krbtgt_canon_test(test_context, recv_buf);
1495 break;
1496 case TEST_TGS_REQ_CANON:
1497 ok = torture_krb5_post_recv_tgs_req_canon_test(test_context, recv_buf);
1498 break;
1499 case TEST_SELF_TRUST_TGS_REQ:
1500 ok = torture_krb5_post_recv_self_trust_tgs_req_test(test_context, recv_buf);
1501 break;
1502 case TEST_TGS_REQ:
1503 ok = torture_krb5_post_recv_tgs_req_test(test_context, recv_buf);
1504 break;
1505 case TEST_TGS_REQ_KRBTGT:
1506 ok = torture_krb5_post_recv_self_trust_tgs_req_test(test_context, recv_buf);
1507 break;
1508 case TEST_TGS_REQ_HOST:
1509 case TEST_TGS_REQ_HOST_SRV_INST:
1510 case TEST_TGS_REQ_HOST_SRV_HST:
1511 ok = torture_krb5_post_recv_tgs_req_host_test(test_context, recv_buf);
1512 break;
1513 case TEST_AS_REQ_SELF:
1514 ok = torture_krb5_post_recv_as_req_self_test(test_context, recv_buf);
1515 break;
1516 }
1517 if (ok == false) {
1518 KRB_ERROR error;
1519 size_t used;
1520 torture_warning(test_context->tctx, "Packet of length %llu failed post-recv checks in test stage %d", (unsigned long long)recv_buf->length, test_context->test_stage);
1521 if (decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used) == 0) {
1522 torture_warning(test_context->tctx,
1523 "STAGE: %d Unexpectedly got a KRB-ERROR packet "
1524 "with error code %d (%s)",
1525 test_context->test_stage,
1526 error.error_code,
1527 error_message(error.error_code + KRB5KDC_ERR_NONE));
1528 free_KRB_ERROR(&error);
1529 }
1530 return EINVAL;
1531 }
1532
1533 test_context->packet_count++;
1534
1535 return k5ret;
1536 }
1537
test_context_destructor(struct torture_krb5_context * test_context)1538 static int test_context_destructor(struct torture_krb5_context *test_context)
1539 {
1540 freeaddrinfo(test_context->server);
1541 return 0;
1542 }
1543
1544
torture_krb5_init_context_canon(struct torture_context * tctx,struct test_data * test_data,struct torture_krb5_context ** torture_krb5_context)1545 static bool torture_krb5_init_context_canon(struct torture_context *tctx,
1546 struct test_data *test_data,
1547 struct torture_krb5_context **torture_krb5_context)
1548 {
1549 const char *host = torture_setting_string(tctx, "host", NULL);
1550 krb5_error_code k5ret;
1551 bool ok;
1552
1553 struct torture_krb5_context *test_context = talloc_zero(tctx, struct torture_krb5_context);
1554 torture_assert(tctx, test_context != NULL, "Failed to allocate");
1555
1556 test_context->test_data = test_data;
1557 test_context->tctx = tctx;
1558
1559 k5ret = smb_krb5_init_context(test_context, tctx->lp_ctx, &test_context->smb_krb5_context);
1560 torture_assert_int_equal(tctx, k5ret, 0, "smb_krb5_init_context failed");
1561
1562 ok = interpret_string_addr_internal(&test_context->server, host, AI_NUMERICHOST);
1563 torture_assert(tctx, ok, "Failed to parse target server");
1564
1565 talloc_set_destructor(test_context, test_context_destructor);
1566
1567 set_sockaddr_port(test_context->server->ai_addr, 88);
1568
1569 k5ret = krb5_set_send_to_kdc_func(test_context->smb_krb5_context->krb5_context,
1570 smb_krb5_send_and_recv_func_canon_override,
1571 test_context);
1572 torture_assert_int_equal(tctx, k5ret, 0, "krb5_set_send_to_kdc_func failed");
1573 *torture_krb5_context = test_context;
1574 return true;
1575 }
1576
1577
torture_krb5_as_req_canon(struct torture_context * tctx,const void * tcase_data)1578 static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void *tcase_data)
1579 {
1580 krb5_error_code k5ret;
1581 krb5_get_init_creds_opt *krb_options = NULL;
1582 struct test_data *test_data = talloc_get_type_abort(tcase_data, struct test_data);
1583 krb5_principal principal;
1584 krb5_principal krbtgt_other;
1585 krb5_principal expected_principal;
1586 const char *principal_string = NULL;
1587 char *krbtgt_other_string;
1588 int principal_flags;
1589 const char *expected_principal_string = NULL;
1590 char *expected_unparse_principal_string;
1591 int expected_principal_flags;
1592 char *got_principal_string;
1593 char *assertion_message;
1594 const char *password = cli_credentials_get_password(
1595 popt_get_cmdline_credentials());
1596 krb5_context k5_context;
1597 struct torture_krb5_context *test_context;
1598 bool ok;
1599 krb5_creds my_creds;
1600 krb5_creds *server_creds;
1601 krb5_ccache ccache;
1602 krb5_auth_context auth_context;
1603 char *cc_name;
1604 krb5_data in_data, enc_ticket;
1605 krb5_get_creds_opt opt;
1606
1607 const char *spn = NULL;
1608 const char *spn_real_realm = NULL;
1609 const char *upn = torture_setting_string(tctx, "krb5-upn", "");
1610 test_data->krb5_service = torture_setting_string(tctx, "krb5-service", "host");
1611 test_data->krb5_hostname = torture_setting_string(tctx, "krb5-hostname", "");
1612
1613 /*
1614 * If we have not passed a UPN on the command line,
1615 * then skip the UPN tests.
1616 */
1617 if (test_data->upn && upn[0] == '\0') {
1618 torture_skip(tctx, "This test needs a UPN specified as --option=torture:krb5-upn=user@example.com to run");
1619 }
1620
1621 /*
1622 * If we have not passed a SPN on the command line,
1623 * then skip the SPN tests.
1624 */
1625 if (test_data->as_req_spn && test_data->krb5_hostname[0] == '\0') {
1626 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");
1627 }
1628
1629 if (test_data->removedollar &&
1630 !torture_setting_bool(tctx, "run_removedollar_test", false))
1631 {
1632 torture_skip(tctx, "--option=torture:run_removedollar_test=true not specified");
1633 }
1634
1635 if (test_data->netbios_realm) {
1636 test_data->realm = test_data->real_domain;
1637 } else {
1638 test_data->realm = test_data->real_realm;
1639 }
1640
1641 if (test_data->upn) {
1642 char *p;
1643 test_data->username = talloc_strdup(test_data, upn);
1644 p = strchr(test_data->username, '@');
1645 if (p) {
1646 *p = '\0';
1647 p++;
1648 }
1649 /*
1650 * Test the UPN behaviour carefully. We can
1651 * test in two different modes, depending on
1652 * what UPN has been set up for us.
1653 *
1654 * If the UPN is in our realm, then we do all the tests with this name also.
1655 *
1656 * If the UPN is not in our realm, then we
1657 * expect the tests that replace the realm to
1658 * fail (as it won't match)
1659 */
1660 if (strcasecmp(p, test_data->real_realm) != 0) {
1661 test_data->other_upn_suffix = true;
1662 } else {
1663 test_data->other_upn_suffix = false;
1664 }
1665
1666 /*
1667 * This lets us test the combination of the UPN prefix
1668 * with a valid domain, without adding even more
1669 * combinations
1670 */
1671 if (test_data->netbios_realm == false) {
1672 test_data->realm = p;
1673 }
1674 }
1675
1676 ok = torture_krb5_init_context_canon(tctx, test_data, &test_context);
1677 torture_assert(tctx, ok, "torture_krb5_init_context failed");
1678 k5_context = test_context->smb_krb5_context->krb5_context;
1679
1680 if (test_data->upper_realm) {
1681 test_data->realm = strupper_talloc(test_data, test_data->realm);
1682 } else {
1683 test_data->realm = strlower_talloc(test_data, test_data->realm);
1684 }
1685 if (test_data->upper_username) {
1686 test_data->username = strupper_talloc(test_data, test_data->username);
1687 } else {
1688 test_data->username = talloc_strdup(test_data, test_data->username);
1689 }
1690
1691 if (test_data->removedollar) {
1692 char *p;
1693
1694 p = strchr_m(test_data->username, '$');
1695 torture_assert(tctx, p != NULL, talloc_asprintf(tctx,
1696 "username[%s] contains no '$'\n",
1697 test_data->username));
1698 *p = '\0';
1699 }
1700
1701 spn = talloc_asprintf(test_data, "%s/%s@%s",
1702 test_data->krb5_service,
1703 test_data->krb5_hostname,
1704 test_data->realm);
1705
1706 spn_real_realm = talloc_asprintf(test_data, "%s/%s@%s",
1707 test_data->krb5_service,
1708 test_data->krb5_hostname,
1709 test_data->real_realm);
1710
1711 if (test_data->as_req_spn) {
1712 if (test_data->enterprise) {
1713 torture_skip(tctx,
1714 "This test combination "
1715 "is skipped intentionally");
1716 }
1717 principal_string = spn;
1718 } else {
1719 principal_string = talloc_asprintf(test_data,
1720 "%s@%s",
1721 test_data->username,
1722 test_data->realm);
1723
1724 }
1725
1726 test_data->spn_is_upn
1727 = (strcasecmp(upn, spn) == 0);
1728
1729 /*
1730 * If we are set to canonicalize, we get back the fixed UPPER
1731 * case realm, and the real username (ie matching LDAP
1732 * samAccountName)
1733 *
1734 * Otherwise, if we are set to enterprise, we
1735 * get back the whole principal as-sent
1736 *
1737 * Finally, if we are not set to canonicalize, we get back the
1738 * fixed UPPER case realm, but the as-sent username
1739 */
1740 if (test_data->as_req_spn && !test_data->spn_is_upn) {
1741 expected_principal_string = spn;
1742 } else if (test_data->canonicalize) {
1743 expected_principal_string = talloc_asprintf(test_data,
1744 "%s@%s",
1745 test_data->real_username,
1746 test_data->real_realm);
1747 } else if (test_data->enterprise) {
1748 expected_principal_string = principal_string;
1749 } else if (test_data->as_req_spn && test_data->spn_is_upn) {
1750 expected_principal_string = spn_real_realm;
1751 } else {
1752 expected_principal_string = talloc_asprintf(test_data,
1753 "%s@%s",
1754 test_data->username,
1755 test_data->real_realm);
1756 }
1757
1758 if (test_data->enterprise) {
1759 principal_flags = KRB5_PRINCIPAL_PARSE_ENTERPRISE;
1760 } else {
1761 if (test_data->upn && test_data->other_upn_suffix) {
1762 torture_skip(tctx, "UPN test for UPN with other UPN suffix only runs with enterprise principals");
1763 }
1764 principal_flags = 0;
1765 }
1766
1767 if (test_data->canonicalize) {
1768 expected_principal_flags = 0;
1769 } else {
1770 expected_principal_flags = principal_flags;
1771 }
1772
1773 torture_assert_int_equal(tctx,
1774 krb5_parse_name_flags(k5_context,
1775 principal_string,
1776 principal_flags,
1777 &principal),
1778 0, "krb5_parse_name_flags failed");
1779 torture_assert_int_equal(tctx,
1780 krb5_parse_name_flags(k5_context,
1781 expected_principal_string,
1782 expected_principal_flags,
1783 &expected_principal),
1784 0, "krb5_parse_name_flags failed");
1785
1786 if (test_data->as_req_spn) {
1787 if (test_data->upn) {
1788 krb5_principal_set_type(k5_context,
1789 principal,
1790 KRB5_NT_PRINCIPAL);
1791 krb5_principal_set_type(k5_context,
1792 expected_principal,
1793 KRB5_NT_PRINCIPAL);
1794 } else {
1795 krb5_principal_set_type(k5_context,
1796 principal,
1797 KRB5_NT_SRV_HST);
1798 krb5_principal_set_type(k5_context,
1799 expected_principal,
1800 KRB5_NT_SRV_HST);
1801 }
1802 }
1803
1804 torture_assert_int_equal(tctx,
1805 krb5_unparse_name(k5_context,
1806 expected_principal,
1807 &expected_unparse_principal_string),
1808 0, "krb5_unparse_name failed");
1809 /*
1810 * Prepare a AS-REQ and run the TEST_AS_REQ tests
1811 *
1812 */
1813
1814 test_context->test_stage = TEST_AS_REQ;
1815 test_context->packet_count = 0;
1816
1817 /*
1818 * Set the canonicalize flag if this test requires it
1819 */
1820 torture_assert_int_equal(tctx,
1821 krb5_get_init_creds_opt_alloc(k5_context, &krb_options),
1822 0, "krb5_get_init_creds_opt_alloc failed");
1823
1824 torture_assert_int_equal(tctx,
1825 krb5_get_init_creds_opt_set_canonicalize(k5_context,
1826 krb_options,
1827 test_data->canonicalize),
1828 0, "krb5_get_init_creds_opt_set_canonicalize failed");
1829
1830 torture_assert_int_equal(tctx,
1831 krb5_get_init_creds_opt_set_win2k(k5_context,
1832 krb_options,
1833 test_data->win2k),
1834 0, "krb5_get_init_creds_opt_set_win2k failed");
1835
1836 k5ret = krb5_get_init_creds_password(k5_context, &my_creds, principal,
1837 password, NULL, NULL, 0,
1838 NULL, krb_options);
1839
1840 if (test_data->netbios_realm && test_data->upn) {
1841 torture_assert_int_equal(tctx, k5ret,
1842 KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN,
1843 "Got wrong error_code from krb5_get_init_creds_password");
1844 /* We can't proceed with more checks */
1845 return true;
1846 } else if (test_context->test_data->as_req_spn
1847 && !test_context->test_data->spn_is_upn) {
1848 torture_assert_int_equal(tctx, k5ret,
1849 KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN,
1850 "Got wrong error_code from "
1851 "krb5_get_init_creds_password");
1852 /* We can't proceed with more checks */
1853 return true;
1854 } else {
1855 assertion_message = talloc_asprintf(tctx,
1856 "krb5_get_init_creds_password for %s failed: %s",
1857 principal_string,
1858 smb_get_krb5_error_message(k5_context, k5ret, tctx));
1859 torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
1860 }
1861
1862 torture_assert(tctx,
1863 test_context->packet_count > 1,
1864 "Expected krb5_get_init_creds_password to send more packets");
1865
1866 /*
1867 * Assert that the reply was with the correct type of
1868 * principal, depending on the flags we set
1869 */
1870 if (test_data->canonicalize == false && test_data->enterprise) {
1871 torture_assert_int_equal(tctx,
1872 krb5_principal_get_type(k5_context,
1873 my_creds.client),
1874 KRB5_NT_ENTERPRISE_PRINCIPAL,
1875 "smb_krb5_init_context gave incorrect client->name.name_type");
1876 } else if (test_data->canonicalize == false && test_data->as_req_spn) {
1877 torture_assert_int_equal(tctx,
1878 krb5_principal_get_type(k5_context,
1879 my_creds.client),
1880 KRB5_NT_SRV_HST,
1881 "smb_krb5_init_context gave incorrect client->name.name_type");
1882 } else {
1883 torture_assert_int_equal(tctx,
1884 krb5_principal_get_type(k5_context,
1885 my_creds.client),
1886 KRB5_NT_PRINCIPAL,
1887 "smb_krb5_init_context gave incorrect client->name.name_type");
1888 }
1889
1890 torture_assert_int_equal(tctx,
1891 krb5_unparse_name(k5_context,
1892 my_creds.client, &got_principal_string), 0,
1893 "krb5_unparse_name failed");
1894
1895 assertion_message = talloc_asprintf(tctx,
1896 "krb5_get_init_creds_password returned a different principal %s to what was expected %s",
1897 got_principal_string, expected_principal_string);
1898 krb5_free_unparsed_name(k5_context, got_principal_string);
1899
1900 torture_assert(tctx, krb5_principal_compare(k5_context,
1901 my_creds.client, expected_principal),
1902 assertion_message);
1903
1904
1905 torture_assert_int_equal(tctx,
1906 krb5_principal_get_type(k5_context,
1907 my_creds.server), KRB5_NT_SRV_INST,
1908 "smb_krb5_init_context gave incorrect server->name.name_type");
1909
1910 torture_assert_int_equal(tctx,
1911 krb5_principal_get_num_comp(k5_context,
1912 my_creds.server), 2,
1913 "smb_krb5_init_context gave incorrect number of components in my_creds.server->name");
1914
1915 torture_assert_str_equal(tctx,
1916 krb5_principal_get_comp_string(k5_context,
1917 my_creds.server, 0),
1918 "krbtgt",
1919 "smb_krb5_init_context gave incorrect my_creds.server->name.name_string[0]");
1920
1921 if (test_data->canonicalize || test_data->enterprise) {
1922 torture_assert_str_equal(tctx,
1923 krb5_principal_get_comp_string(k5_context,
1924 my_creds.server, 1),
1925 test_data->real_realm,
1926
1927 "smb_krb5_init_context gave incorrect my_creds.server->name.name_string[1]");
1928 } else {
1929 torture_assert_str_equal(tctx,
1930 krb5_principal_get_comp_string(k5_context,
1931 my_creds.server, 1),
1932 test_data->realm,
1933
1934 "smb_krb5_init_context gave incorrect my_creds.server->name.name_string[1]");
1935 }
1936 torture_assert_str_equal(tctx,
1937 krb5_principal_get_realm(k5_context,
1938 my_creds.server),
1939 test_data->real_realm,
1940 "smb_krb5_init_context gave incorrect my_creds.server->realm");
1941
1942 /* Store the result of the 'kinit' above into a memory ccache */
1943 cc_name = talloc_asprintf(tctx, "MEMORY:%s", test_data->test_name);
1944 torture_assert_int_equal(tctx, krb5_cc_resolve(k5_context, cc_name,
1945 &ccache),
1946 0, "krb5_cc_resolve failed");
1947
1948 torture_assert_int_equal(tctx, krb5_cc_initialize(k5_context,
1949 ccache, my_creds.client),
1950 0, "krb5_cc_initialize failed");
1951
1952 torture_assert_int_equal(tctx, krb5_cc_store_cred(k5_context,
1953 ccache, &my_creds),
1954 0, "krb5_cc_store_cred failed");
1955
1956 /*
1957 * Prepare a TGS-REQ and run the TEST_TGS_REQ_KRBTGT_CANON tests
1958 *
1959 * This tests krb5_get_creds behaviour, which allows us to set
1960 * the KRB5_GC_CANONICALIZE option against the krbtgt/ principal
1961 */
1962
1963 krbtgt_other_string = talloc_asprintf(test_data, "krbtgt/%s@%s", test_data->real_domain, test_data->real_realm);
1964 torture_assert_int_equal(tctx,
1965 krb5_make_principal(k5_context, &krbtgt_other,
1966 test_data->real_realm, "krbtgt",
1967 test_data->real_domain, NULL),
1968 0, "krb5_make_principal failed");
1969
1970 test_context->test_stage = TEST_TGS_REQ_KRBTGT_CANON;
1971 test_context->packet_count = 0;
1972
1973 torture_assert_int_equal(tctx,
1974 krb5_get_creds_opt_alloc(k5_context, &opt),
1975 0, "krb5_get_creds_opt_alloc");
1976
1977 krb5_get_creds_opt_add_options(k5_context,
1978 opt,
1979 KRB5_GC_CANONICALIZE);
1980
1981 krb5_get_creds_opt_add_options(k5_context,
1982 opt,
1983 KRB5_GC_NO_STORE);
1984
1985 /* Confirm if we can get a ticket krbtgt/realm that we got back with the initial kinit */
1986 k5ret = krb5_get_creds(k5_context, opt, ccache, krbtgt_other, &server_creds);
1987
1988 if (test_data->canonicalize == false && test_data->enterprise == false
1989 && test_data->netbios_realm && test_data->upper_realm) {
1990 /*
1991 * In these situations, the code above does store a
1992 * principal in the credentials cache matching what
1993 * krb5_get_creds() needs, so the test succeds, with no packets.
1994 *
1995 */
1996 assertion_message = talloc_asprintf(tctx,
1997 "krb5_get_creds for %s failed with: %s",
1998 krbtgt_other_string,
1999 smb_get_krb5_error_message(k5_context, k5ret,
2000 tctx));
2001
2002 torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
2003 torture_assert_int_equal(tctx,
2004 test_context->packet_count,
2005 0, "Expected krb5_get_creds not to send packets");
2006 } else if (test_data->canonicalize == false && test_data->enterprise == false
2007 && (test_data->upper_realm == false || test_data->netbios_realm == true)) {
2008 torture_assert_int_equal(tctx, k5ret, KRB5_CC_NOTFOUND,
2009 "krb5_get_creds should have failed with KRB5_CC_NOTFOUND");
2010 } else {
2011
2012 /*
2013 * In these situations, the code above does not store a
2014 * principal in the credentials cache matching what
2015 * krb5_get_creds() needs without talking to the KDC, so the
2016 * test fails with looping detected because when we set
2017 * canonicalize we confuse the client libs.
2018 *
2019 */
2020 assertion_message = talloc_asprintf(tctx,
2021 "krb5_get_creds for %s should have failed with looping detected: %s",
2022 krbtgt_other_string,
2023 smb_get_krb5_error_message(k5_context, k5ret,
2024 tctx));
2025
2026 torture_assert_int_equal(tctx, k5ret, KRB5_GET_IN_TKT_LOOP, assertion_message);
2027 torture_assert_int_equal(tctx,
2028 test_context->packet_count,
2029 2, "Expected krb5_get_creds to send packets");
2030 }
2031
2032 /*
2033 * Prepare a TGS-REQ and run the TEST_TGS_REQ_CANON tests
2034 *
2035 * This tests krb5_get_creds behaviour, which allows us to set
2036 * the KRB5_GC_CANONICALIZE option
2037 */
2038
2039 test_context->test_stage = TEST_TGS_REQ_CANON;
2040 test_context->packet_count = 0;
2041
2042 torture_assert_int_equal(tctx,
2043 krb5_get_creds_opt_alloc(k5_context, &opt),
2044 0, "krb5_get_creds_opt_alloc");
2045
2046 krb5_get_creds_opt_add_options(k5_context,
2047 opt,
2048 KRB5_GC_CANONICALIZE);
2049
2050 krb5_get_creds_opt_add_options(k5_context,
2051 opt,
2052 KRB5_GC_NO_STORE);
2053
2054 if (test_data->s4u2self) {
2055 torture_assert_int_equal(tctx,
2056 krb5_get_creds_opt_set_impersonate(k5_context,
2057 opt,
2058 principal),
2059 0, "krb5_get_creds_opt_set_impersonate failed");
2060 }
2061
2062 /* Confirm if we can get a ticket to our own name */
2063 k5ret = krb5_get_creds(k5_context, opt, ccache, principal, &server_creds);
2064
2065 /*
2066 * In these situations, the code above does not store a
2067 * principal in the credentials cache matching what
2068 * krb5_get_creds() needs, so the test fails.
2069 *
2070 */
2071 if (test_data->canonicalize == false && test_data->enterprise == false
2072 && (test_data->upper_realm == false || test_data->netbios_realm == true)) {
2073 torture_assert_int_equal(tctx, k5ret, KRB5_CC_NOTFOUND,
2074 "krb5_get_creds should have failed with KRB5_CC_NOTFOUND");
2075 } else {
2076 assertion_message = talloc_asprintf(tctx,
2077 "krb5_get_creds for %s failed: %s",
2078 principal_string,
2079 smb_get_krb5_error_message(k5_context, k5ret,
2080 tctx));
2081
2082 /*
2083 * Only machine accounts (strictly, accounts with a
2084 * servicePrincipalName) can expect this test to succeed
2085 */
2086 if (torture_setting_bool(tctx, "expect_machine_account", false)
2087 && (test_data->enterprise
2088 || test_data->spn_is_upn
2089 || test_data->upn == false)) {
2090
2091 if (test_data->mitm_s4u2self) {
2092 torture_assert_int_not_equal(tctx, k5ret, 0, assertion_message);
2093 /* Done testing mitm-s4u2self */
2094 return true;
2095 }
2096
2097 torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
2098
2099 /* Check that the impersonate principal is not being canonicalized by the KDC. */
2100 if (test_data->s4u2self) {
2101 torture_assert(tctx, krb5_principal_compare(k5_context, server_creds->client,
2102 principal),
2103 "TGS-REP cname does not match requested client principal");
2104 }
2105
2106 torture_assert_int_equal(tctx, krb5_cc_store_cred(k5_context,
2107 ccache, server_creds),
2108 0, "krb5_cc_store_cred failed");
2109
2110 torture_assert_int_equal(tctx,
2111 krb5_free_creds(k5_context,
2112 server_creds),
2113 0, "krb5_free_cred_contents failed");
2114
2115 } else {
2116 torture_assert_int_equal(tctx, k5ret, KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN,
2117 assertion_message);
2118 }
2119
2120 torture_assert_int_equal(tctx,
2121 test_context->packet_count,
2122 1, "Expected krb5_get_creds to send packets");
2123 }
2124
2125 /*
2126 * Confirm gettting a ticket to pass to the server, running
2127 * either the TEST_TGS_REQ or TEST_SELF_TRUST_TGS_REQ stage.
2128 *
2129 * This triggers the client to attempt to get a
2130 * cross-realm ticket between the alternate names of
2131 * the server, and we need to confirm that behaviour.
2132 *
2133 */
2134
2135 /*
2136 * This tries to guess when the krb5 libs will ask for a
2137 * cross-realm ticket, and when they will just ask the KDC
2138 * directly.
2139 */
2140 if (test_context->test_data->canonicalize == false
2141 || test_context->test_data->enterprise
2142 || (test_context->test_data->spn_is_upn && test_context->test_data->upn)
2143 || (test_context->test_data->upper_realm
2144 && test_context->test_data->netbios_realm == false)) {
2145 test_context->test_stage = TEST_TGS_REQ;
2146 } else {
2147 test_context->test_stage = TEST_SELF_TRUST_TGS_REQ;
2148 }
2149
2150 test_context->packet_count = 0;
2151 torture_assert_int_equal(tctx, krb5_auth_con_init(k5_context, &auth_context),
2152 0, "krb5_auth_con_init failed");
2153
2154 in_data.length = 0;
2155 k5ret = krb5_mk_req_exact(k5_context,
2156 &auth_context,
2157 AP_OPTS_USE_SUBKEY,
2158 principal,
2159 &in_data, ccache,
2160 &enc_ticket);
2161 assertion_message = talloc_asprintf(tctx,
2162 "krb5_mk_req_exact for %s failed: %s",
2163 principal_string,
2164 smb_get_krb5_error_message(k5_context, k5ret, tctx));
2165
2166 /*
2167 * Only machine accounts (strictly, accounts with a
2168 * servicePrincipalName) can expect this test to succeed
2169 */
2170 if (torture_setting_bool(tctx, "expect_machine_account", false)
2171 && (test_data->enterprise ||
2172 (test_context->test_data->as_req_spn
2173 || test_context->test_data->spn_is_upn)
2174 || test_data->upn == false)) {
2175 DATA_BLOB client_to_server;
2176 torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
2177 client_to_server = data_blob_const(enc_ticket.data, enc_ticket.length);
2178
2179 /* This is very weird */
2180 if (test_data->canonicalize == false
2181 && test_context->test_data->as_req_spn
2182 && test_context->test_data->spn_is_upn
2183 && test_context->test_data->s4u2self) {
2184
2185 torture_assert(tctx,
2186 test_accept_ticket(tctx,
2187 popt_get_cmdline_credentials(),
2188 spn_real_realm,
2189 client_to_server),
2190 "test_accept_ticket failed - failed to accept the ticket we just created");
2191 } else if (test_data->canonicalize == true
2192 && test_context->test_data->as_req_spn
2193 && test_context->test_data->spn_is_upn
2194 && test_context->test_data->s4u2self) {
2195
2196 torture_assert(tctx,
2197 test_accept_ticket(tctx,
2198 popt_get_cmdline_credentials(),
2199 spn,
2200 client_to_server),
2201 "test_accept_ticket failed - failed to accept the ticket we just created");
2202 } else if (test_data->canonicalize == true
2203 && test_data->enterprise == false
2204 && test_context->test_data->upn
2205 && test_context->test_data->spn_is_upn
2206 && test_context->test_data->s4u2self) {
2207
2208 torture_assert(tctx,
2209 test_accept_ticket(tctx,
2210 popt_get_cmdline_credentials(),
2211 principal_string,
2212 client_to_server),
2213 "test_accept_ticket failed - failed to accept the ticket we just created");
2214 } else if (test_data->canonicalize == false
2215 && test_data->enterprise == false
2216 && test_context->test_data->upn
2217 && test_context->test_data->spn_is_upn
2218 && test_context->test_data->s4u2self) {
2219
2220 const char *accept_expected_principal_string
2221 = talloc_asprintf(test_data,
2222 "%s@%s",
2223 test_data->username,
2224 test_data->real_realm);
2225
2226 torture_assert(tctx,
2227 test_accept_ticket(tctx,
2228 popt_get_cmdline_credentials(),
2229 accept_expected_principal_string,
2230 client_to_server),
2231 "test_accept_ticket failed - failed to accept the ticket we just created");
2232 } else {
2233
2234 torture_assert(tctx,
2235 test_accept_ticket(tctx,
2236 popt_get_cmdline_credentials(),
2237 expected_unparse_principal_string,
2238 client_to_server),
2239 "test_accept_ticket failed - failed to accept the ticket we just created");
2240 }
2241 krb5_data_free(&enc_ticket);
2242 } else {
2243 torture_assert_int_equal(tctx, k5ret, KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN,
2244 assertion_message);
2245 }
2246
2247 /*
2248 * Only in these cases would the above code have needed to
2249 * send packets to the network
2250 */
2251 if (test_data->canonicalize == false && test_data->enterprise == false
2252 && (test_data->upper_realm == false || test_data->netbios_realm == true)) {
2253 torture_assert(tctx,
2254 test_context->packet_count > 0,
2255 "Expected krb5_mk_req_exact to send packets");
2256 }
2257
2258 /*
2259 * Confirm gettting a ticket to pass to the server, running
2260 * the TEST_TGS_REQ_HOST, TEST_TGS_REQ_HOST_SRV_INST, TEST_TGS_REQ_HOST_SRV_HST stage
2261 *
2262 * This triggers the client to attempt to get a
2263 * cross-realm ticket between the alternate names of
2264 * the server, and we need to confirm that behaviour.
2265 *
2266 */
2267
2268 if (*test_data->krb5_service && *test_data->krb5_hostname) {
2269 bool implied_canonicalize;
2270 krb5_principal host_principal_srv_inst;
2271 /*
2272 * This tries to guess when the krb5 libs will ask for a
2273 * cross-realm ticket, and when they will just ask the KDC
2274 * directly.
2275 */
2276 test_context->test_stage = TEST_TGS_REQ_HOST;
2277 test_context->packet_count = 0;
2278 torture_assert_int_equal(tctx, krb5_auth_con_init(k5_context, &auth_context),
2279 0, "krb5_auth_con_init failed");
2280
2281 in_data.length = 0;
2282 k5ret = krb5_mk_req(k5_context,
2283 &auth_context,
2284 0,
2285 test_data->krb5_service,
2286 test_data->krb5_hostname,
2287 &in_data, ccache,
2288 &enc_ticket);
2289
2290 implied_canonicalize = test_data->canonicalize;
2291 if (test_data->spn_is_upn && (test_data->upn || test_data->as_req_spn)) {
2292 implied_canonicalize = true;
2293 }
2294 if (test_data->enterprise) {
2295 implied_canonicalize = true;
2296 }
2297
2298 if (implied_canonicalize == false
2299 && (test_data->upper_realm == false || test_data->netbios_realm == true)) {
2300 torture_assert_int_equal(tctx, k5ret, KRB5_CC_NOTFOUND,
2301 "krb5_get_creds should have failed with KRB5_CC_NOTFOUND");
2302 } else if (test_data->spn_is_upn
2303 && test_data->canonicalize == false
2304 && test_data->enterprise == false
2305 && test_data->upper_realm == false
2306 && test_data->upper_username == true
2307 && test_data->upn) {
2308 torture_assert_int_equal(tctx, k5ret, KRB5_CC_NOTFOUND,
2309 "krb5_get_creds should have failed with KRB5_CC_NOTFOUND");
2310 } else {
2311 assertion_message = talloc_asprintf(tctx,
2312 "krb5_mk_req for %s/%s failed: %s",
2313 test_data->krb5_service,
2314 test_data->krb5_hostname,
2315 smb_get_krb5_error_message(k5_context, k5ret, tctx));
2316
2317 torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
2318
2319 if (test_data->spn_is_upn == false) {
2320 /*
2321 * Only in these cases would the above
2322 * code have needed to send packets to
2323 * the network
2324 */
2325 torture_assert(tctx,
2326 test_context->packet_count > 0,
2327 "Expected krb5_get_creds to send packets");
2328 }
2329 }
2330
2331
2332 test_context->test_stage = TEST_TGS_REQ_HOST_SRV_INST;
2333 test_context->packet_count = 0;
2334
2335 torture_assert_int_equal(tctx,
2336 krb5_make_principal(k5_context, &host_principal_srv_inst,
2337 test_data->real_realm,
2338 strupper_talloc(tctx, test_data->krb5_service),
2339 test_data->krb5_hostname,
2340 NULL),
2341 0, "krb5_make_principal failed");
2342
2343 krb5_principal_set_type(k5_context, host_principal_srv_inst, KRB5_NT_SRV_INST);
2344
2345 torture_assert_int_equal(tctx, krb5_auth_con_init(k5_context, &auth_context),
2346 0, "krb5_auth_con_init failed");
2347
2348 in_data.length = 0;
2349 k5ret = krb5_mk_req_exact(k5_context,
2350 &auth_context,
2351 0,
2352 host_principal_srv_inst,
2353 &in_data, ccache,
2354 &enc_ticket);
2355 krb5_free_principal(k5_context, host_principal_srv_inst);
2356 if (test_data->canonicalize == false && test_data->enterprise == false
2357 && (test_data->upper_realm == false || test_data->netbios_realm == true)) {
2358 torture_assert_int_equal(tctx, k5ret, KRB5_CC_NOTFOUND,
2359 "krb5_get_creds should have failed with KRB5_CC_NOTFOUND");
2360 } else {
2361 assertion_message = talloc_asprintf(tctx,
2362 "krb5_mk_req for %s/%s KRB5_NT_SRV_INST failed: %s",
2363 test_data->krb5_service,
2364 test_data->krb5_hostname,
2365 smb_get_krb5_error_message(k5_context, k5ret, tctx));
2366
2367 torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
2368 /*
2369 * Only in these cases would the above code have needed to
2370 * send packets to the network
2371 */
2372 torture_assert(tctx,
2373 test_context->packet_count > 0,
2374 "Expected krb5_get_creds to send packets");
2375 }
2376
2377
2378 test_context->test_stage = TEST_TGS_REQ_HOST_SRV_HST;
2379 test_context->packet_count = 0;
2380
2381 torture_assert_int_equal(tctx,
2382 krb5_make_principal(k5_context, &host_principal_srv_inst,
2383 test_data->real_realm,
2384 test_data->krb5_service,
2385 strupper_talloc(tctx, test_data->krb5_hostname),
2386 NULL),
2387 0, "krb5_make_principal failed");
2388
2389 krb5_principal_set_type(k5_context, host_principal_srv_inst, KRB5_NT_SRV_HST);
2390
2391 torture_assert_int_equal(tctx, krb5_auth_con_init(k5_context, &auth_context),
2392 0, "krb5_auth_con_init failed");
2393
2394 in_data.length = 0;
2395 k5ret = krb5_mk_req_exact(k5_context,
2396 &auth_context,
2397 0,
2398 host_principal_srv_inst,
2399 &in_data, ccache,
2400 &enc_ticket);
2401 krb5_free_principal(k5_context, host_principal_srv_inst);
2402 if (test_data->canonicalize == false && test_data->enterprise == false
2403 && (test_data->upper_realm == false || test_data->netbios_realm == true)) {
2404 torture_assert_int_equal(tctx, k5ret, KRB5_CC_NOTFOUND,
2405 "krb5_get_creds should have failed with KRB5_CC_NOTFOUND");
2406 } else {
2407 assertion_message = talloc_asprintf(tctx,
2408 "krb5_mk_req for %s/%s KRB5_NT_SRV_INST failed: %s",
2409 test_data->krb5_service,
2410 test_data->krb5_hostname,
2411 smb_get_krb5_error_message(k5_context, k5ret, tctx));
2412
2413 torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
2414 /*
2415 * Only in these cases would the above code have needed to
2416 * send packets to the network
2417 */
2418 torture_assert(tctx,
2419 test_context->packet_count > 0,
2420 "Expected krb5_get_creds to send packets");
2421 }
2422 }
2423
2424 /*
2425 * Confirm gettting a ticket for the same krbtgt/realm that we
2426 * got back with the initial ticket, running the
2427 * TEST_TGS_REQ_KRBTGT stage.
2428 *
2429 */
2430
2431 test_context->test_stage = TEST_TGS_REQ_KRBTGT;
2432 test_context->packet_count = 0;
2433
2434 in_data.length = 0;
2435 k5ret = krb5_mk_req_exact(k5_context,
2436 &auth_context,
2437 0,
2438 my_creds.server,
2439 &in_data, ccache,
2440 &enc_ticket);
2441
2442 assertion_message = talloc_asprintf(tctx,
2443 "krb5_mk_req_exact for %s failed: %s",
2444 principal_string,
2445 smb_get_krb5_error_message(k5_context, k5ret, tctx));
2446 torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
2447
2448 /*
2449 * Confirm gettting a ticket for our own principal that we
2450 * got back with the initial ticket, running the
2451 * TEST_AS_REQ_SELF stage.
2452 *
2453 */
2454 test_context->test_stage = TEST_AS_REQ_SELF;
2455 test_context->packet_count = 0;
2456
2457 k5ret = krb5_get_init_creds_password(k5_context, &my_creds, principal,
2458 password, NULL, NULL, 0,
2459 principal_string, krb_options);
2460
2461 if (torture_setting_bool(test_context->tctx, "expect_machine_account", false)
2462 && (test_data->upn == false || (test_data->enterprise == false && test_data->upn == true && test_data->spn_is_upn))) {
2463 assertion_message = talloc_asprintf(tctx,
2464 "krb5_get_init_creds_password for %s failed: %s",
2465 principal_string,
2466 smb_get_krb5_error_message(k5_context, k5ret, tctx));
2467 torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
2468 torture_assert(tctx,
2469 test_context->packet_count >= 2,
2470 "Expected krb5_get_init_creds_password to send more packets");
2471
2472 } else {
2473 assertion_message = talloc_asprintf(tctx,
2474 "Got wrong error_code from krb5_get_init_creds_password, expected KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN trying to get a ticket to %s for %s", principal_string, principal_string);
2475 torture_assert_int_equal(tctx, k5ret,
2476 KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN,
2477 assertion_message);
2478 torture_assert(tctx,
2479 test_context->packet_count >= 1,
2480 "Expected krb5_get_init_creds_password to send more packets");
2481
2482 /* We can't proceed with more checks */
2483 return true;
2484 }
2485
2486 /*
2487 * Assert that the reply was with the correct type of
2488 * principal, depending on the flags we set
2489 */
2490 if (test_data->as_req_spn && test_data->spn_is_upn && test_data->canonicalize == false) {
2491 torture_assert_int_equal(tctx,
2492 krb5_principal_get_type(k5_context,
2493 my_creds.client),
2494 KRB5_NT_SRV_HST,
2495 "smb_krb5_init_context gave incorrect client->name.name_type");
2496 torture_assert_int_equal(tctx,
2497 krb5_principal_get_type(k5_context,
2498 my_creds.server),
2499 KRB5_NT_SRV_HST,
2500 "smb_krb5_init_context gave incorrect server->name.name_type");
2501 } else if (test_data->canonicalize == false && test_data->enterprise) {
2502 torture_assert_int_equal(tctx,
2503 krb5_principal_get_type(k5_context,
2504 my_creds.client),
2505 KRB5_NT_ENTERPRISE_PRINCIPAL,
2506 "smb_krb5_init_context gave incorrect client->name.name_type");
2507 torture_assert_int_equal(tctx,
2508 krb5_principal_get_type(k5_context,
2509 my_creds.server),
2510 KRB5_NT_ENTERPRISE_PRINCIPAL,
2511 "smb_krb5_init_context gave incorrect server->name.name_type");
2512 } else {
2513 torture_assert_int_equal(tctx,
2514 krb5_principal_get_type(k5_context,
2515 my_creds.client),
2516 KRB5_NT_PRINCIPAL,
2517 "smb_krb5_init_context gave incorrect client->name.name_type");
2518 torture_assert_int_equal(tctx,
2519 krb5_principal_get_type(k5_context,
2520 my_creds.server),
2521 KRB5_NT_PRINCIPAL,
2522 "smb_krb5_init_context gave incorrect server->name.name_type");
2523 }
2524
2525 torture_assert_int_equal(tctx,
2526 krb5_unparse_name(k5_context,
2527 my_creds.client, &got_principal_string), 0,
2528 "krb5_unparse_name failed");
2529
2530 assertion_message = talloc_asprintf(tctx,
2531 "krb5_get_init_creds_password returned a different principal %s to what was expected %s",
2532 got_principal_string, expected_principal_string);
2533 krb5_free_unparsed_name(k5_context, got_principal_string);
2534
2535 torture_assert(tctx, krb5_principal_compare(k5_context,
2536 my_creds.client, expected_principal),
2537 assertion_message);
2538
2539 torture_assert_int_equal(tctx,
2540 krb5_unparse_name(k5_context,
2541 my_creds.client, &got_principal_string), 0,
2542 "krb5_unparse_name failed");
2543
2544 assertion_message = talloc_asprintf(tctx,
2545 "krb5_get_init_creds_password returned a different server principal %s to what was expected %s",
2546 got_principal_string, expected_principal_string);
2547 krb5_free_unparsed_name(k5_context, got_principal_string);
2548
2549 torture_assert(tctx, krb5_principal_compare(k5_context,
2550 my_creds.server, expected_principal),
2551 assertion_message);
2552
2553 krb5_free_principal(k5_context, principal);
2554 krb5_get_init_creds_opt_free(k5_context, krb_options);
2555
2556 torture_assert_int_equal(tctx, krb5_free_cred_contents(k5_context, &my_creds),
2557 0, "krb5_free_cred_contents failed");
2558
2559 return true;
2560 }
2561
torture_krb5_canon(TALLOC_CTX * mem_ctx)2562 struct torture_suite *torture_krb5_canon(TALLOC_CTX *mem_ctx)
2563 {
2564 unsigned int i;
2565 struct torture_suite *suite = torture_suite_create(mem_ctx, "canon");
2566 suite->description = talloc_strdup(suite, "Kerberos Canonicalisation tests");
2567
2568 for (i = 0; i < TEST_ALL; i++) {
2569 char *name = talloc_asprintf(suite, "%s.%s.%s.%s.%s.%s.%s.%s",
2570 (i & TEST_CANONICALIZE) ? "canon" : "no-canon",
2571 (i & TEST_ENTERPRISE) ? "enterprise" : "no-enterprise",
2572 (i & TEST_UPPER_REALM) ? "uc-realm" : "lc-realm",
2573 (i & TEST_UPPER_USERNAME) ? "uc-user" : "lc-user",
2574 (i & TEST_NETBIOS_REALM) ? "netbios-realm" : "krb5-realm",
2575 (i & TEST_WIN2K) ? "win2k" : "no-win2k",
2576 (i & TEST_UPN) ? "upn" :
2577 ((i & TEST_AS_REQ_SPN) ? "spn" :
2578 ((i & TEST_REMOVEDOLLAR) ? "removedollar" : "samaccountname")),
2579 (i & TEST_S4U2SELF) ? (i & TEST_MITM_S4U2SELF) ? "mitm-s4u2self" : "s4u2self" : "normal");
2580 struct torture_suite *sub_suite = torture_suite_create(mem_ctx, name);
2581
2582 struct test_data *test_data = talloc_zero(suite, struct test_data);
2583 if (i & TEST_UPN) {
2584 if (i & TEST_AS_REQ_SPN) {
2585 continue;
2586 }
2587 }
2588 if ((i & TEST_UPN) || (i & TEST_AS_REQ_SPN)) {
2589 if (i & TEST_REMOVEDOLLAR) {
2590 continue;
2591 }
2592 }
2593 if (i & TEST_MITM_S4U2SELF) {
2594 if (!(i & TEST_S4U2SELF)) {
2595 continue;
2596 }
2597 }
2598
2599 test_data->test_name = name;
2600 test_data->real_realm
2601 = strupper_talloc(test_data,
2602 cli_credentials_get_realm(
2603 popt_get_cmdline_credentials()));
2604 test_data->real_domain = cli_credentials_get_domain(
2605 popt_get_cmdline_credentials());
2606 test_data->username = cli_credentials_get_username(
2607 popt_get_cmdline_credentials());
2608 test_data->real_username = cli_credentials_get_username(
2609 popt_get_cmdline_credentials());
2610 test_data->canonicalize = (i & TEST_CANONICALIZE) != 0;
2611 test_data->enterprise = (i & TEST_ENTERPRISE) != 0;
2612 test_data->upper_realm = (i & TEST_UPPER_REALM) != 0;
2613 test_data->upper_username = (i & TEST_UPPER_USERNAME) != 0;
2614 test_data->netbios_realm = (i & TEST_NETBIOS_REALM) != 0;
2615 test_data->win2k = (i & TEST_WIN2K) != 0;
2616 test_data->upn = (i & TEST_UPN) != 0;
2617 test_data->s4u2self = (i & TEST_S4U2SELF) != 0;
2618 test_data->mitm_s4u2self = (i & TEST_MITM_S4U2SELF) != 0;
2619 test_data->removedollar = (i & TEST_REMOVEDOLLAR) != 0;
2620 test_data->as_req_spn = (i & TEST_AS_REQ_SPN) != 0;
2621 torture_suite_add_simple_tcase_const(sub_suite, name, torture_krb5_as_req_canon,
2622 test_data);
2623 torture_suite_add_suite(suite, sub_suite);
2624
2625 }
2626 return suite;
2627 }
2628