Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : Validate the krb5 pac generation routines
5 :
6 : Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2015
7 :
8 : This program is free software; you can redistribute it and/or modify
9 : it under the terms of the GNU General Public License as published by
10 : the Free Software Foundation; either version 3 of the License, or
11 : (at your option) any later version.
12 :
13 : This program is distributed in the hope that it will be useful,
14 : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : GNU General Public License for more details.
17 :
18 :
19 : You should have received a copy of the GNU General Public License
20 : along with this program. If not, see <http://www.gnu.org/licenses/>.
21 : */
22 :
23 : #include "includes.h"
24 : #include "system/kerberos.h"
25 : #include "torture/smbtorture.h"
26 : #include "torture/winbind/proto.h"
27 : #include "torture/krb5/proto.h"
28 : #include "auth/credentials/credentials.h"
29 : #include "lib/cmdline/cmdline.h"
30 : #include "source4/auth/kerberos/kerberos.h"
31 : #include "source4/auth/kerberos/kerberos_util.h"
32 : #include "lib/util/util_net.h"
33 :
34 : #define krb5_is_app_tag(dat,tag) \
35 : ((dat != NULL) && (dat)->length && \
36 : (((((char *)(dat)->data)[0] & ~0x20) == ((tag) | 0x40))))
37 :
38 : #define krb5_is_krb_error(dat) krb5_is_app_tag(dat, 30)
39 :
40 : enum torture_krb5_test {
41 : TORTURE_KRB5_TEST_PLAIN,
42 : TORTURE_KRB5_TEST_PAC_REQUEST,
43 : TORTURE_KRB5_TEST_BREAK_PW,
44 : TORTURE_KRB5_TEST_CLOCK_SKEW,
45 : TORTURE_KRB5_TEST_AES,
46 : TORTURE_KRB5_TEST_RC4,
47 : TORTURE_KRB5_TEST_AES_RC4,
48 :
49 : /*
50 : * This is in and out of the client.
51 : * Out refers to requests, in refers to replies
52 : */
53 : TORTURE_KRB5_TEST_CHANGE_SERVER_OUT,
54 : TORTURE_KRB5_TEST_CHANGE_SERVER_IN,
55 : TORTURE_KRB5_TEST_CHANGE_SERVER_BOTH,
56 : };
57 :
58 : struct torture_krb5_context {
59 : struct torture_context *tctx;
60 : struct addrinfo *server;
61 : enum torture_krb5_test test;
62 : int packet_count;
63 : AS_REQ as_req;
64 : AS_REP as_rep;
65 : const char *krb5_service;
66 : const char *krb5_hostname;
67 : };
68 :
69 : /*
70 : * Confirm that the outgoing packet meets certain expectations. This
71 : * should be extended to further assert the correct and expected
72 : * behaviour of the krb5 libs, so we know what we are sending to the
73 : * server.
74 : *
75 : */
76 :
77 153 : static bool torture_krb5_pre_send_test(struct torture_krb5_context *test_context, krb5_data *send_buf)
78 : {
79 0 : size_t used;
80 153 : switch (test_context->test)
81 : {
82 121 : case TORTURE_KRB5_TEST_PLAIN:
83 : case TORTURE_KRB5_TEST_PAC_REQUEST:
84 : case TORTURE_KRB5_TEST_BREAK_PW:
85 : case TORTURE_KRB5_TEST_CLOCK_SKEW:
86 : case TORTURE_KRB5_TEST_AES:
87 : case TORTURE_KRB5_TEST_RC4:
88 : case TORTURE_KRB5_TEST_AES_RC4:
89 : case TORTURE_KRB5_TEST_CHANGE_SERVER_IN:
90 121 : torture_assert_int_equal(test_context->tctx,
91 : decode_AS_REQ(send_buf->data, send_buf->length, &test_context->as_req, &used), 0,
92 : "decode_AS_REQ failed");
93 121 : torture_assert_int_equal(test_context->tctx, used, send_buf->length, "length mismatch");
94 121 : torture_assert_int_equal(test_context->tctx, test_context->as_req.pvno, 5, "Got wrong as_req->pvno");
95 121 : break;
96 32 : case TORTURE_KRB5_TEST_CHANGE_SERVER_OUT:
97 : case TORTURE_KRB5_TEST_CHANGE_SERVER_BOTH:
98 : {
99 0 : AS_REQ mod_as_req;
100 0 : krb5_error_code k5ret;
101 0 : krb5_data modified_send_buf;
102 32 : torture_assert_int_equal(test_context->tctx,
103 : decode_AS_REQ(send_buf->data, send_buf->length, &test_context->as_req, &used), 0,
104 : "decode_AS_REQ failed");
105 32 : torture_assert_int_equal(test_context->tctx, used, send_buf->length, "length mismatch");
106 32 : torture_assert_int_equal(test_context->tctx, test_context->as_req.pvno, 5, "Got wrong as_req->pvno");
107 :
108 : /* Only change it if configured with --option=torture:krb5-hostname= */
109 32 : if (test_context->krb5_hostname[0] == '\0') {
110 32 : break;
111 : }
112 :
113 24 : mod_as_req = test_context->as_req;
114 :
115 24 : torture_assert_int_equal(test_context->tctx,
116 : mod_as_req.req_body.sname->name_string.len, 2,
117 : "Sending wrong mod_as_req.req_body->sname.name_string.len");
118 24 : free(mod_as_req.req_body.sname->name_string.val[0]);
119 24 : free(mod_as_req.req_body.sname->name_string.val[1]);
120 24 : mod_as_req.req_body.sname->name_string.val[0] = strdup(test_context->krb5_service);
121 24 : mod_as_req.req_body.sname->name_string.val[1] = strdup(test_context->krb5_hostname);
122 :
123 24 : ASN1_MALLOC_ENCODE(AS_REQ, modified_send_buf.data, modified_send_buf.length,
124 : &mod_as_req, &used, k5ret);
125 24 : torture_assert_int_equal(test_context->tctx,
126 : k5ret, 0,
127 : "encode_AS_REQ failed");
128 :
129 24 : *send_buf = modified_send_buf;
130 24 : break;
131 : }
132 : }
133 153 : return true;
134 : }
135 :
136 93 : static bool torture_check_krb5_error(struct torture_krb5_context *test_context,
137 : const krb5_data *reply,
138 : krb5_error_code expected_error,
139 : bool check_pa_data)
140 : {
141 93 : KRB_ERROR error = { 0 };
142 93 : size_t used = 0;
143 0 : int rc;
144 :
145 93 : rc = decode_KRB_ERROR(reply->data, reply->length, &error, &used);
146 93 : torture_assert_int_equal(test_context->tctx,
147 : rc, 0,
148 : "decode_KRB_ERROR failed");
149 :
150 93 : torture_assert_int_equal(test_context->tctx,
151 : used, reply->length,
152 : "length mismatch");
153 93 : torture_assert_int_equal(test_context->tctx,
154 : error.pvno, 5,
155 : "Got wrong error.pvno");
156 93 : torture_assert_int_equal(test_context->tctx,
157 : error.error_code, expected_error - KRB5KDC_ERR_NONE,
158 : "Got wrong error.error_code");
159 :
160 92 : if (check_pa_data) {
161 0 : METHOD_DATA m;
162 0 : size_t len;
163 0 : int i;
164 8 : bool found_enc_ts = false;
165 8 : bool found_etype_info2 = false;
166 8 : torture_assert(test_context->tctx,
167 : error.e_data != NULL,
168 : "No e-data returned");
169 :
170 8 : rc = decode_METHOD_DATA(error.e_data->data,
171 8 : error.e_data->length,
172 : &m,
173 : &len);
174 8 : torture_assert_int_equal(test_context->tctx,
175 : rc, 0,
176 : "Got invalid method data");
177 :
178 8 : torture_assert(test_context->tctx,
179 : m.len > 0,
180 : "No PA_DATA given");
181 17 : for (i = 0; i < m.len; i++) {
182 9 : if (m.val[i].padata_type == KRB5_PADATA_ENC_TIMESTAMP) {
183 0 : found_enc_ts = true;
184 : }
185 9 : else if (m.val[i].padata_type == KRB5_PADATA_ETYPE_INFO2) {
186 8 : found_etype_info2 = true;
187 : }
188 : }
189 8 : torture_assert(test_context->tctx,
190 : found_etype_info2,
191 : "PADATA_ETYPE_INFO2 not found");
192 8 : if (expected_error != KRB5KDC_ERR_PREAUTH_FAILED)
193 0 : torture_assert(test_context->tctx,
194 : found_enc_ts,
195 : "Encrypted timestamp not found");
196 : }
197 :
198 92 : free_KRB_ERROR(&error);
199 :
200 92 : return true;
201 : }
202 :
203 23 : static bool torture_check_krb5_as_rep_enctype(struct torture_krb5_context *test_context,
204 : const krb5_data *reply,
205 : const krb5_enctype* allowed_enctypes)
206 : {
207 23 : ENCTYPE reply_enctype = { 0 };
208 23 : size_t used = 0;
209 0 : int rc;
210 23 : int expected_enctype = ETYPE_NULL;
211 :
212 23 : rc = decode_AS_REP(reply->data,
213 23 : reply->length,
214 : &test_context->as_rep,
215 : &used);
216 23 : torture_assert_int_equal(test_context->tctx,
217 : rc, 0,
218 : "decode_AS_REP failed");
219 23 : torture_assert_int_equal(test_context->tctx,
220 : used, reply->length,
221 : "length mismatch");
222 23 : torture_assert_int_equal(test_context->tctx,
223 : test_context->as_rep.pvno, 5,
224 : "Got wrong as_rep->pvno");
225 23 : torture_assert_int_equal(test_context->tctx,
226 : test_context->as_rep.ticket.tkt_vno, 5,
227 : "Got wrong as_rep->ticket.tkt_vno");
228 23 : torture_assert(test_context->tctx,
229 : test_context->as_rep.ticket.enc_part.kvno,
230 : "Did not get a KVNO in test_context->as_rep.ticket.enc_part.kvno");
231 :
232 23 : if (test_context->as_req.padata) {
233 : /*
234 : * If the AS-REQ contains a PA-ENC-TIMESTAMP, then
235 : * that encryption type is used to determine the reply
236 : * enctype.
237 : */
238 23 : int i = 0;
239 23 : const PA_DATA *pa = krb5_find_padata(test_context->as_req.padata->val,
240 23 : test_context->as_req.padata->len,
241 : KRB5_PADATA_ENC_TIMESTAMP,
242 : &i);
243 23 : if (pa) {
244 0 : EncryptedData ed;
245 0 : size_t len;
246 23 : krb5_error_code ret = decode_EncryptedData(pa->padata_value.data,
247 23 : pa->padata_value.length,
248 : &ed, &len);
249 23 : torture_assert_int_equal(test_context->tctx,
250 : ret,
251 : 0,
252 : "decode_EncryptedData failed");
253 23 : expected_enctype = ed.etype;
254 23 : free_EncryptedData(&ed);
255 : }
256 : }
257 23 : if (expected_enctype == ETYPE_NULL) {
258 : /*
259 : * Otherwise, find the strongest enctype contained in
260 : * the AS-REQ supported enctypes list.
261 : */
262 0 : const krb5_enctype *p = NULL;
263 :
264 0 : for (p = krb5_kerberos_enctypes(NULL); *p != (krb5_enctype)ETYPE_NULL; ++p) {
265 0 : int j;
266 :
267 0 : if ((*p == (krb5_enctype)ETYPE_AES256_CTS_HMAC_SHA1_96 ||
268 0 : *p == (krb5_enctype)ETYPE_AES128_CTS_HMAC_SHA1_96) &&
269 0 : !test_context->as_req.req_body.kdc_options.canonicalize)
270 : {
271 : /*
272 : * AES encryption types are only used here when
273 : * we set the canonicalize flag, as the salt
274 : * needs to match.
275 : */
276 0 : continue;
277 : }
278 :
279 0 : for (j = 0; j < test_context->as_req.req_body.etype.len; ++j) {
280 0 : krb5_enctype etype = test_context->as_req.req_body.etype.val[j];
281 0 : if (*p == etype) {
282 0 : expected_enctype = etype;
283 0 : break;
284 : }
285 : }
286 :
287 0 : if (expected_enctype != (krb5_enctype)ETYPE_NULL) {
288 0 : break;
289 : }
290 : }
291 : }
292 :
293 : {
294 : /* Ensure the enctype to check against is an expected type. */
295 23 : const krb5_enctype *p = NULL;
296 23 : bool found = false;
297 24 : for (p = allowed_enctypes; *p != (krb5_enctype)ETYPE_NULL; ++p) {
298 24 : if (*p == expected_enctype) {
299 23 : found = true;
300 23 : break;
301 : }
302 : }
303 :
304 23 : torture_assert(test_context->tctx,
305 : found,
306 : "Calculated enctype not in allowed list");
307 : }
308 :
309 23 : reply_enctype = test_context->as_rep.enc_part.etype;
310 23 : torture_assert_int_equal(test_context->tctx,
311 : reply_enctype, expected_enctype,
312 : "Ticket encrypted with invalid algorithm");
313 :
314 23 : return true;
315 : }
316 :
317 : /*
318 : * Confirm that the incoming packet from the KDC meets certain
319 : * expectations. This uses a switch and the packet count to work out
320 : * what test we are in, and where in the test we are, so we can assert
321 : * on the expected reply packets from the KDC.
322 : *
323 : */
324 :
325 153 : static bool torture_krb5_post_recv_test(struct torture_krb5_context *test_context, krb5_data *recv_buf)
326 : {
327 0 : KRB_ERROR error;
328 0 : size_t used;
329 0 : bool ok;
330 :
331 153 : switch (test_context->test)
332 : {
333 32 : case TORTURE_KRB5_TEST_CHANGE_SERVER_OUT:
334 : case TORTURE_KRB5_TEST_PLAIN:
335 32 : if (test_context->packet_count == 0) {
336 16 : ok = torture_check_krb5_error(test_context,
337 : recv_buf,
338 : KRB5KDC_ERR_PREAUTH_REQUIRED,
339 : false);
340 16 : torture_assert(test_context->tctx,
341 : ok,
342 : "torture_check_krb5_error failed");
343 16 : } else if ((decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used) == 0)
344 0 : && (test_context->packet_count == 1)) {
345 0 : torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch");
346 0 : torture_assert_int_equal(test_context->tctx, error.pvno, 5, "Got wrong error.pvno");
347 0 : torture_assert_int_equal(test_context->tctx, error.error_code, KRB5KRB_ERR_RESPONSE_TOO_BIG - KRB5KDC_ERR_NONE,
348 : "Got wrong error.error_code");
349 0 : free_KRB_ERROR(&error);
350 : } else {
351 16 : torture_assert_int_equal(test_context->tctx,
352 : decode_AS_REP(recv_buf->data, recv_buf->length, &test_context->as_rep, &used), 0,
353 : "decode_AS_REP failed");
354 16 : torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch");
355 16 : torture_assert_int_equal(test_context->tctx,
356 : test_context->as_rep.pvno, 5,
357 : "Got wrong as_rep->pvno");
358 16 : torture_assert_int_equal(test_context->tctx,
359 : test_context->as_rep.ticket.tkt_vno, 5,
360 : "Got wrong as_rep->ticket.tkt_vno");
361 16 : torture_assert(test_context->tctx,
362 : test_context->as_rep.ticket.enc_part.kvno,
363 : "Did not get a KVNO in test_context->as_rep.ticket.enc_part.kvno");
364 16 : if (test_context->test == TORTURE_KRB5_TEST_PLAIN) {
365 8 : if (torture_setting_bool(test_context->tctx, "expect_cached_at_rodc", false)) {
366 2 : torture_assert_int_not_equal(test_context->tctx,
367 : *test_context->as_rep.ticket.enc_part.kvno & 0xFFFF0000,
368 : 0, "Did not get a RODC number in the KVNO");
369 : } else {
370 6 : torture_assert_int_equal(test_context->tctx,
371 : *test_context->as_rep.ticket.enc_part.kvno & 0xFFFF0000,
372 : 0, "Unexpecedly got a RODC number in the KVNO");
373 : }
374 : }
375 16 : free_AS_REP(&test_context->as_rep);
376 : }
377 32 : torture_assert(test_context->tctx, test_context->packet_count < 3, "too many packets");
378 32 : free_AS_REQ(&test_context->as_req);
379 32 : break;
380 :
381 : /*
382 : * Confirm correct error codes when we ask for the PAC. This behaviour is rather odd...
383 : */
384 10 : case TORTURE_KRB5_TEST_PAC_REQUEST:
385 10 : if (test_context->packet_count == 0) {
386 5 : ok = torture_check_krb5_error(test_context,
387 : recv_buf,
388 : KRB5KDC_ERR_PREAUTH_REQUIRED,
389 : false);
390 5 : torture_assert(test_context->tctx,
391 : ok,
392 : "torture_check_krb5_error failed");
393 5 : } else if ((decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used) == 0)
394 0 : && (test_context->packet_count == 1)) {
395 0 : torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch");
396 0 : torture_assert_int_equal(test_context->tctx, error.pvno, 5, "Got wrong error.pvno");
397 0 : torture_assert_int_equal(test_context->tctx, error.error_code, KRB5KRB_ERR_RESPONSE_TOO_BIG - KRB5KDC_ERR_NONE,
398 : "Got wrong error.error_code");
399 0 : free_KRB_ERROR(&error);
400 : } else {
401 5 : torture_assert_int_equal(test_context->tctx,
402 : decode_AS_REP(recv_buf->data, recv_buf->length, &test_context->as_rep, &used), 0,
403 : "decode_AS_REP failed");
404 5 : torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch");
405 5 : torture_assert_int_equal(test_context->tctx, test_context->as_rep.pvno, 5, "Got wrong as_rep->pvno");
406 5 : free_AS_REP(&test_context->as_rep);
407 : }
408 10 : torture_assert(test_context->tctx, test_context->packet_count < 2, "too many packets");
409 10 : free_AS_REQ(&test_context->as_req);
410 10 : break;
411 :
412 : /*
413 : * Confirm correct error codes when we deliberately send the wrong password
414 : */
415 16 : case TORTURE_KRB5_TEST_BREAK_PW:
416 16 : if (test_context->packet_count == 0) {
417 8 : ok = torture_check_krb5_error(test_context,
418 : recv_buf,
419 : KRB5KDC_ERR_PREAUTH_REQUIRED,
420 : false);
421 8 : torture_assert(test_context->tctx,
422 : ok,
423 : "torture_check_krb5_error failed");
424 8 : } else if (test_context->packet_count == 1) {
425 8 : ok = torture_check_krb5_error(test_context,
426 : recv_buf,
427 : KRB5KDC_ERR_PREAUTH_FAILED,
428 : true);
429 8 : torture_assert(test_context->tctx,
430 : ok,
431 : "torture_check_krb5_error failed");
432 : }
433 16 : torture_assert(test_context->tctx, test_context->packet_count < 2, "too many packets");
434 16 : free_AS_REQ(&test_context->as_req);
435 16 : break;
436 :
437 : /*
438 : * Confirm correct error codes when we deliberately skew the client clock
439 : */
440 16 : case TORTURE_KRB5_TEST_CLOCK_SKEW:
441 16 : if (test_context->packet_count == 0) {
442 8 : ok = torture_check_krb5_error(test_context,
443 : recv_buf,
444 : KRB5KDC_ERR_PREAUTH_REQUIRED,
445 : false);
446 8 : torture_assert(test_context->tctx,
447 : ok,
448 : "torture_check_krb5_error failed");
449 8 : } else if (test_context->packet_count == 1) {
450 8 : ok = torture_check_krb5_error(test_context,
451 : recv_buf,
452 : KRB5KRB_AP_ERR_SKEW,
453 : false);
454 8 : torture_assert(test_context->tctx,
455 : ok,
456 : "torture_check_krb5_error failed");
457 : }
458 16 : torture_assert(test_context->tctx, test_context->packet_count < 2, "too many packets");
459 16 : free_AS_REQ(&test_context->as_req);
460 16 : break;
461 15 : case TORTURE_KRB5_TEST_AES:
462 15 : torture_comment(test_context->tctx, "TORTURE_KRB5_TEST_AES\n");
463 :
464 15 : if (test_context->packet_count == 0) {
465 8 : ok = torture_check_krb5_error(test_context,
466 : recv_buf,
467 : KRB5KDC_ERR_PREAUTH_REQUIRED,
468 : false);
469 8 : torture_assert(test_context->tctx,
470 : ok,
471 : "torture_check_krb5_error failed");
472 7 : } else if (krb5_is_krb_error(recv_buf)) {
473 0 : ok = torture_check_krb5_error(test_context,
474 : recv_buf,
475 : KRB5KRB_ERR_RESPONSE_TOO_BIG,
476 : false);
477 0 : torture_assert(test_context->tctx,
478 : ok,
479 : "torture_check_krb5_error failed");
480 : } else {
481 7 : const krb5_enctype allowed_enctypes[] = {
482 : KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96,
483 : ETYPE_NULL
484 : };
485 7 : ok = torture_check_krb5_as_rep_enctype(test_context,
486 : recv_buf,
487 : allowed_enctypes);
488 7 : torture_assert(test_context->tctx,
489 : ok,
490 : "torture_check_krb5_as_rep_enctype failed");
491 : }
492 :
493 14 : torture_assert(test_context->tctx,
494 : test_context->packet_count < 3,
495 : "Too many packets");
496 14 : break;
497 16 : case TORTURE_KRB5_TEST_RC4:
498 16 : torture_comment(test_context->tctx, "TORTURE_KRB5_TEST_RC4\n");
499 :
500 16 : if (test_context->packet_count == 0) {
501 8 : ok = torture_check_krb5_error(test_context,
502 : recv_buf,
503 : KRB5KDC_ERR_PREAUTH_REQUIRED,
504 : false);
505 8 : torture_assert(test_context->tctx,
506 : ok,
507 : "torture_check_krb5_error failed");
508 8 : } else if (krb5_is_krb_error(recv_buf)) {
509 0 : ok = torture_check_krb5_error(test_context,
510 : recv_buf,
511 : KRB5KRB_ERR_RESPONSE_TOO_BIG,
512 : false);
513 0 : torture_assert(test_context->tctx,
514 : ok,
515 : "torture_check_krb5_error failed");
516 : } else {
517 8 : const krb5_enctype allowed_enctypes[] = {
518 : KRB5_ENCTYPE_ARCFOUR_HMAC_MD5,
519 : ETYPE_NULL
520 : };
521 8 : ok = torture_check_krb5_as_rep_enctype(test_context,
522 : recv_buf,
523 : allowed_enctypes);
524 8 : torture_assert(test_context->tctx,
525 : ok,
526 : "torture_check_krb5_as_rep_enctype failed");
527 : }
528 :
529 16 : torture_assert(test_context->tctx,
530 : test_context->packet_count < 3,
531 : "Too many packets");
532 16 : break;
533 16 : case TORTURE_KRB5_TEST_AES_RC4:
534 16 : torture_comment(test_context->tctx, "TORTURE_KRB5_TEST_AES_RC4\n");
535 :
536 16 : if (test_context->packet_count == 0) {
537 8 : ok = torture_check_krb5_error(test_context,
538 : recv_buf,
539 : KRB5KDC_ERR_PREAUTH_REQUIRED,
540 : false);
541 8 : torture_assert(test_context->tctx,
542 : ok,
543 : "torture_check_krb5_error failed");
544 8 : } else if (krb5_is_krb_error(recv_buf)) {
545 0 : ok = torture_check_krb5_error(test_context,
546 : recv_buf,
547 : KRB5KRB_ERR_RESPONSE_TOO_BIG,
548 : false);
549 0 : torture_assert(test_context->tctx,
550 : ok,
551 : "torture_check_krb5_error failed");
552 : } else {
553 8 : const krb5_enctype allowed_enctypes[] = {
554 : KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96,
555 : KRB5_ENCTYPE_ARCFOUR_HMAC_MD5,
556 : ETYPE_NULL
557 : };
558 8 : ok = torture_check_krb5_as_rep_enctype(test_context,
559 : recv_buf,
560 : allowed_enctypes);
561 8 : torture_assert(test_context->tctx,
562 : ok,
563 : "torture_check_krb5_as_rep_enctype failed");
564 : }
565 :
566 16 : torture_assert(test_context->tctx,
567 : test_context->packet_count < 3,
568 : "Too many packets");
569 16 : break;
570 32 : case TORTURE_KRB5_TEST_CHANGE_SERVER_IN:
571 : case TORTURE_KRB5_TEST_CHANGE_SERVER_BOTH:
572 : {
573 0 : AS_REP mod_as_rep;
574 0 : krb5_error_code k5ret;
575 0 : krb5_data modified_recv_buf;
576 32 : if (test_context->packet_count == 0) {
577 16 : ok = torture_check_krb5_error(test_context,
578 : recv_buf,
579 : KRB5KDC_ERR_PREAUTH_REQUIRED,
580 : false);
581 16 : torture_assert(test_context->tctx,
582 : ok,
583 : "torture_check_krb5_error failed");
584 16 : } else if ((decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used) == 0)
585 0 : && (test_context->packet_count == 1)) {
586 0 : torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch");
587 0 : torture_assert_int_equal(test_context->tctx, error.pvno, 5, "Got wrong error.pvno");
588 0 : torture_assert_int_equal(test_context->tctx, error.error_code, KRB5KRB_ERR_RESPONSE_TOO_BIG - KRB5KDC_ERR_NONE,
589 : "Got wrong error.error_code");
590 0 : free_KRB_ERROR(&error);
591 : } else {
592 16 : torture_assert_int_equal(test_context->tctx,
593 : decode_AS_REP(recv_buf->data, recv_buf->length, &test_context->as_rep, &used), 0,
594 : "decode_AS_REP failed");
595 16 : torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch");
596 16 : torture_assert_int_equal(test_context->tctx,
597 : test_context->as_rep.pvno, 5,
598 : "Got wrong as_rep->pvno");
599 16 : torture_assert_int_equal(test_context->tctx,
600 : test_context->as_rep.ticket.tkt_vno, 5,
601 : "Got wrong as_rep->ticket.tkt_vno");
602 16 : torture_assert_int_equal(test_context->tctx,
603 : test_context->as_rep.ticket.sname.name_string.len, 2,
604 : "Got wrong as_rep->ticket.sname.name_string.len");
605 16 : free(test_context->as_rep.ticket.sname.name_string.val[0]);
606 16 : free(test_context->as_rep.ticket.sname.name_string.val[1]);
607 16 : test_context->as_rep.ticket.sname.name_string.val[0] = strdup("bad");
608 16 : test_context->as_rep.ticket.sname.name_string.val[1] = strdup("mallory");
609 :
610 16 : mod_as_rep = test_context->as_rep;
611 :
612 16 : ASN1_MALLOC_ENCODE(AS_REP, modified_recv_buf.data, modified_recv_buf.length,
613 : &mod_as_rep, &used, k5ret);
614 16 : torture_assert_int_equal(test_context->tctx,
615 : k5ret, 0,
616 : "encode_AS_REQ failed");
617 16 : krb5_data_free(recv_buf);
618 :
619 16 : *recv_buf = modified_recv_buf;
620 16 : free_AS_REQ(&test_context->as_req);
621 : }
622 32 : torture_assert(test_context->tctx, test_context->packet_count < 3, "too many packets");
623 :
624 32 : break;
625 : }
626 : }
627 :
628 :
629 152 : return true;
630 : }
631 :
632 :
633 : /*
634 : * This function is set in torture_krb5_init_context as krb5
635 : * send_and_recv function. This allows us to override what server the
636 : * test is aimed at, and to inspect the packets just before they are
637 : * sent to the network, and before they are processed on the recv
638 : * side.
639 : *
640 : * The torture_krb5_pre_send_test() and torture_krb5_post_recv_test()
641 : * functions are implement the actual tests.
642 : *
643 : * When this asserts, the caller will get a spurious 'cannot contact
644 : * any KDC' message.
645 : *
646 : */
647 153 : static krb5_error_code test_krb5_send_to_realm_override(
648 : struct smb_krb5_context *smb_krb5_context,
649 : void *data, /* struct torture_krb5_context */
650 : krb5_const_realm realm,
651 : time_t timeout,
652 : const krb5_data *send_buf,
653 : krb5_data *recv_buf)
654 : {
655 0 : krb5_error_code k5ret;
656 0 : bool ok;
657 153 : krb5_data modified_send_buf = *send_buf;
658 :
659 0 : struct torture_krb5_context *test_context
660 153 : = talloc_get_type_abort(data, struct torture_krb5_context);
661 :
662 153 : ok = torture_krb5_pre_send_test(test_context, &modified_send_buf);
663 153 : if (ok == false) {
664 0 : return EINVAL;
665 : }
666 :
667 153 : k5ret = smb_krb5_send_and_recv_func_forced_tcp(smb_krb5_context,
668 : test_context->server,
669 : timeout,
670 : &modified_send_buf,
671 : recv_buf);
672 153 : if (k5ret != 0) {
673 0 : return k5ret;
674 : }
675 153 : ok = torture_krb5_post_recv_test(test_context, recv_buf);
676 153 : if (ok == false) {
677 1 : return EINVAL;
678 : }
679 :
680 152 : test_context->packet_count++;
681 :
682 152 : return k5ret;
683 : }
684 :
685 77 : static int test_context_destructor(struct torture_krb5_context *test_context)
686 : {
687 77 : freeaddrinfo(test_context->server);
688 77 : return 0;
689 : }
690 :
691 :
692 77 : static bool torture_krb5_init_context(struct torture_context *tctx,
693 : enum torture_krb5_test test,
694 : struct smb_krb5_context **smb_krb5_context)
695 : {
696 77 : const char *host = torture_setting_string(tctx, "host", NULL);
697 0 : krb5_error_code k5ret;
698 0 : bool ok;
699 :
700 77 : struct torture_krb5_context *test_context = talloc_zero(tctx, struct torture_krb5_context);
701 77 : torture_assert(tctx, test_context != NULL, "Failed to allocate");
702 :
703 77 : test_context->test = test;
704 77 : test_context->tctx = tctx;
705 :
706 77 : test_context->krb5_service = torture_setting_string(tctx, "krb5-service", "host");
707 77 : test_context->krb5_hostname = torture_setting_string(tctx, "krb5-hostname", "");
708 :
709 77 : k5ret = smb_krb5_init_context(tctx, tctx->lp_ctx, smb_krb5_context);
710 77 : torture_assert_int_equal(tctx, k5ret, 0, "smb_krb5_init_context failed");
711 :
712 77 : ok = interpret_string_addr_internal(&test_context->server, host, AI_NUMERICHOST);
713 77 : torture_assert(tctx, ok, "Failed to parse target server");
714 :
715 77 : talloc_set_destructor(test_context, test_context_destructor);
716 :
717 77 : set_sockaddr_port(test_context->server->ai_addr, 88);
718 :
719 77 : k5ret = smb_krb5_set_send_to_kdc_func((*smb_krb5_context),
720 : test_krb5_send_to_realm_override,
721 : NULL, /* send_to_kdc */
722 : test_context);
723 77 : torture_assert_int_equal(tctx, k5ret, 0, "krb5_set_send_to_kdc_func failed");
724 77 : return true;
725 : }
726 :
727 77 : static bool torture_krb5_as_req_creds(struct torture_context *tctx,
728 : struct cli_credentials *credentials,
729 : enum torture_krb5_test test)
730 : {
731 0 : krb5_error_code k5ret;
732 0 : bool ok;
733 0 : krb5_creds my_creds;
734 0 : krb5_principal principal;
735 0 : struct smb_krb5_context *smb_krb5_context;
736 0 : krb5_context k5_context;
737 0 : enum credentials_obtained obtained;
738 0 : const char *error_string;
739 77 : const char *password = cli_credentials_get_password(credentials);
740 0 : const char *expected_principal_string;
741 77 : krb5_get_init_creds_opt *krb_options = NULL;
742 0 : const char *realm;
743 77 : const char *krb5_hostname = torture_setting_string(tctx, "krb5-hostname", "");
744 :
745 :
746 77 : ok = torture_krb5_init_context(tctx, test, &smb_krb5_context);
747 77 : torture_assert(tctx, ok, "torture_krb5_init_context failed");
748 77 : k5_context = smb_krb5_context->krb5_context;
749 :
750 0 : expected_principal_string
751 77 : = cli_credentials_get_principal(credentials,
752 : tctx);
753 :
754 77 : realm = strupper_talloc(tctx, cli_credentials_get_realm(credentials));
755 77 : k5ret = principal_from_credentials(tctx, credentials, smb_krb5_context,
756 : &principal, &obtained, &error_string);
757 77 : torture_assert_int_equal(tctx, k5ret, 0, error_string);
758 :
759 77 : switch (test)
760 : {
761 32 : case TORTURE_KRB5_TEST_PLAIN:
762 : case TORTURE_KRB5_TEST_CHANGE_SERVER_OUT:
763 : case TORTURE_KRB5_TEST_CHANGE_SERVER_IN:
764 : case TORTURE_KRB5_TEST_CHANGE_SERVER_BOTH:
765 32 : break;
766 :
767 5 : case TORTURE_KRB5_TEST_PAC_REQUEST:
768 5 : torture_assert_int_equal(tctx,
769 : krb5_get_init_creds_opt_alloc(smb_krb5_context->krb5_context, &krb_options),
770 : 0, "krb5_get_init_creds_opt_alloc failed");
771 :
772 5 : torture_assert_int_equal(tctx,
773 : krb5_get_init_creds_opt_set_pac_request(smb_krb5_context->krb5_context, krb_options, true),
774 : 0, "krb5_get_init_creds_opt_set_pac_request failed");
775 5 : break;
776 :
777 8 : case TORTURE_KRB5_TEST_BREAK_PW:
778 8 : password = "NOT the password";
779 8 : break;
780 :
781 8 : case TORTURE_KRB5_TEST_CLOCK_SKEW:
782 8 : torture_assert_int_equal(tctx,
783 : krb5_set_real_time(smb_krb5_context->krb5_context, time(NULL) + 3600, 0),
784 : 0, "krb5_set_real_time failed");
785 8 : break;
786 :
787 8 : case TORTURE_KRB5_TEST_AES: {
788 0 : static krb5_enctype etype_list[] = { KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96 };
789 :
790 8 : k5ret = krb5_get_init_creds_opt_alloc(smb_krb5_context->krb5_context,
791 : &krb_options);
792 8 : torture_assert_int_equal(tctx,
793 : k5ret, 0,
794 : "krb5_get_init_creds_opt_alloc failed");
795 :
796 8 : krb5_get_init_creds_opt_set_etype_list(krb_options,
797 : etype_list,
798 : 1);
799 8 : break;
800 : }
801 8 : case TORTURE_KRB5_TEST_RC4: {
802 0 : static krb5_enctype etype_list[] = { KRB5_ENCTYPE_ARCFOUR_HMAC_MD5 };
803 :
804 8 : k5ret = krb5_get_init_creds_opt_alloc(smb_krb5_context->krb5_context,
805 : &krb_options);
806 8 : torture_assert_int_equal(tctx,
807 : k5ret, 0,
808 : "krb5_get_init_creds_opt_alloc failed");
809 :
810 8 : krb5_get_init_creds_opt_set_etype_list(krb_options,
811 : etype_list,
812 : 1);
813 8 : break;
814 : }
815 8 : case TORTURE_KRB5_TEST_AES_RC4: {
816 0 : static krb5_enctype etype_list[] = { KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96,
817 : KRB5_ENCTYPE_ARCFOUR_HMAC_MD5 };
818 :
819 8 : k5ret = krb5_get_init_creds_opt_alloc(smb_krb5_context->krb5_context,
820 : &krb_options);
821 8 : torture_assert_int_equal(tctx,
822 : k5ret, 0,
823 : "krb5_get_init_creds_opt_alloc failed");
824 :
825 8 : krb5_get_init_creds_opt_set_etype_list(krb_options,
826 : etype_list,
827 : 2);
828 8 : break;
829 : }
830 :
831 : } /* end switch */
832 :
833 77 : k5ret = krb5_get_init_creds_password(smb_krb5_context->krb5_context, &my_creds, principal,
834 : password, NULL, NULL, 0,
835 : NULL, krb_options);
836 77 : krb5_get_init_creds_opt_free(smb_krb5_context->krb5_context, krb_options);
837 :
838 77 : switch (test)
839 : {
840 45 : case TORTURE_KRB5_TEST_PLAIN:
841 : case TORTURE_KRB5_TEST_CHANGE_SERVER_IN:
842 : case TORTURE_KRB5_TEST_PAC_REQUEST:
843 : case TORTURE_KRB5_TEST_AES:
844 : case TORTURE_KRB5_TEST_RC4:
845 : case TORTURE_KRB5_TEST_AES_RC4:
846 : {
847 0 : char *got_principal_string;
848 0 : char *assertion_message;
849 45 : torture_assert_int_equal(tctx, k5ret, 0, "krb5_get_init_creds_password failed");
850 :
851 44 : torture_assert_int_equal(tctx,
852 : krb5_principal_get_type(k5_context,
853 : my_creds.client),
854 : KRB5_NT_PRINCIPAL,
855 : "smb_krb5_init_context gave incorrect client->name.name_type");
856 :
857 44 : torture_assert_int_equal(tctx,
858 : krb5_unparse_name(k5_context,
859 : my_creds.client,
860 : &got_principal_string), 0,
861 : "krb5_unparse_name failed");
862 :
863 44 : assertion_message = talloc_asprintf(tctx,
864 : "krb5_get_init_creds_password returned a different principal %s to what was expected %s",
865 : got_principal_string, expected_principal_string);
866 44 : krb5_xfree(got_principal_string);
867 :
868 44 : torture_assert(tctx, krb5_principal_compare(k5_context,
869 : my_creds.client,
870 : principal),
871 : assertion_message);
872 :
873 :
874 44 : torture_assert_str_equal(tctx,
875 : my_creds.server->name.name_string.val[0],
876 : "krbtgt",
877 : "Mismatch in name between AS_REP and expected response, expected krbtgt");
878 44 : torture_assert_str_equal(tctx,
879 : my_creds.server->name.name_string.val[1],
880 : realm,
881 : "Mismatch in realm part of krbtgt/ in AS_REP, expected krbtgt/REALM@REALM");
882 :
883 44 : torture_assert_str_equal(tctx,
884 : my_creds.server->realm,
885 : realm,
886 : "Mismatch in server realm in AS_REP, expected krbtgt/REALM@REALM");
887 :
888 44 : break;
889 : }
890 8 : case TORTURE_KRB5_TEST_BREAK_PW:
891 8 : torture_assert_int_equal(tctx, k5ret, KRB5KDC_ERR_PREAUTH_FAILED, "krb5_get_init_creds_password should have failed");
892 8 : return true;
893 :
894 8 : case TORTURE_KRB5_TEST_CLOCK_SKEW:
895 8 : torture_assert_int_equal(tctx, k5ret, KRB5KRB_AP_ERR_SKEW, "krb5_get_init_creds_password should have failed");
896 8 : return true;
897 :
898 16 : case TORTURE_KRB5_TEST_CHANGE_SERVER_OUT:
899 : case TORTURE_KRB5_TEST_CHANGE_SERVER_BOTH:
900 : {
901 0 : char *got_principal_string;
902 0 : char *assertion_message;
903 :
904 16 : if (krb5_hostname[0] != '\0') {
905 24 : torture_assert_int_equal(tctx, k5ret, KRB5KRB_AP_ERR_BAD_INTEGRITY, "krb5_get_init_creds_password should have failed");
906 12 : return true;
907 : }
908 :
909 4 : torture_assert_int_equal(tctx, k5ret, 0, "krb5_get_init_creds_password failed");
910 :
911 4 : torture_assert_int_equal(tctx,
912 : krb5_principal_get_type(k5_context,
913 : my_creds.client),
914 : KRB5_NT_PRINCIPAL,
915 : "smb_krb5_init_context gave incorrect client->name.name_type");
916 :
917 4 : torture_assert_int_equal(tctx,
918 : krb5_unparse_name(k5_context,
919 : my_creds.client,
920 : &got_principal_string), 0,
921 : "krb5_unparse_name failed");
922 :
923 4 : assertion_message = talloc_asprintf(tctx,
924 : "krb5_get_init_creds_password returned a different principal %s to what was expected %s",
925 : got_principal_string, expected_principal_string);
926 4 : krb5_xfree(got_principal_string);
927 :
928 4 : torture_assert(tctx, krb5_principal_compare(k5_context,
929 : my_creds.client,
930 : principal),
931 : assertion_message);
932 :
933 4 : break;
934 : }
935 : }
936 :
937 48 : k5ret = krb5_free_cred_contents(smb_krb5_context->krb5_context, &my_creds);
938 48 : torture_assert_int_equal(tctx, k5ret, 0, "krb5_free_creds failed");
939 :
940 48 : return true;
941 : }
942 :
943 8 : static bool torture_krb5_as_req_cmdline(struct torture_context *tctx)
944 : {
945 8 : return torture_krb5_as_req_creds(tctx, samba_cmdline_get_creds(),
946 : TORTURE_KRB5_TEST_PLAIN);
947 : }
948 :
949 8 : static bool torture_krb5_as_req_pac_request(struct torture_context *tctx)
950 : {
951 8 : if (torture_setting_bool(tctx, "expect_rodc", false)) {
952 3 : torture_skip(tctx, "This test needs further investigation in the RODC case against a Windows DC, in particular with non-cached users");
953 : }
954 5 : return torture_krb5_as_req_creds(tctx, samba_cmdline_get_creds(),
955 : TORTURE_KRB5_TEST_PAC_REQUEST);
956 : }
957 :
958 8 : static bool torture_krb5_as_req_break_pw(struct torture_context *tctx)
959 : {
960 8 : return torture_krb5_as_req_creds(tctx, samba_cmdline_get_creds(),
961 : TORTURE_KRB5_TEST_BREAK_PW);
962 : }
963 :
964 8 : static bool torture_krb5_as_req_clock_skew(struct torture_context *tctx)
965 : {
966 8 : return torture_krb5_as_req_creds(tctx, samba_cmdline_get_creds(),
967 : TORTURE_KRB5_TEST_CLOCK_SKEW);
968 : }
969 :
970 8 : static bool torture_krb5_as_req_aes(struct torture_context *tctx)
971 : {
972 8 : return torture_krb5_as_req_creds(tctx,
973 : samba_cmdline_get_creds(),
974 : TORTURE_KRB5_TEST_AES);
975 : }
976 :
977 8 : static bool torture_krb5_as_req_rc4(struct torture_context *tctx)
978 : {
979 8 : return torture_krb5_as_req_creds(tctx,
980 : samba_cmdline_get_creds(),
981 : TORTURE_KRB5_TEST_RC4);
982 : }
983 :
984 8 : static bool torture_krb5_as_req_aes_rc4(struct torture_context *tctx)
985 : {
986 8 : return torture_krb5_as_req_creds(tctx,
987 : samba_cmdline_get_creds(),
988 : TORTURE_KRB5_TEST_AES_RC4);
989 : }
990 :
991 : /* Checking for the "Orpheus' Lyre" attack */
992 8 : static bool torture_krb5_as_req_change_server_out(struct torture_context *tctx)
993 : {
994 8 : return torture_krb5_as_req_creds(tctx,
995 : samba_cmdline_get_creds(),
996 : TORTURE_KRB5_TEST_CHANGE_SERVER_OUT);
997 : }
998 :
999 8 : static bool torture_krb5_as_req_change_server_in(struct torture_context *tctx)
1000 : {
1001 8 : return torture_krb5_as_req_creds(tctx,
1002 : samba_cmdline_get_creds(),
1003 : TORTURE_KRB5_TEST_CHANGE_SERVER_IN);
1004 : }
1005 :
1006 8 : static bool torture_krb5_as_req_change_server_both(struct torture_context *tctx)
1007 : {
1008 8 : return torture_krb5_as_req_creds(tctx,
1009 : samba_cmdline_get_creds(),
1010 : TORTURE_KRB5_TEST_CHANGE_SERVER_BOTH);
1011 : }
1012 :
1013 1077 : NTSTATUS torture_krb5_init(TALLOC_CTX *ctx)
1014 : {
1015 1077 : struct torture_suite *suite = torture_suite_create(ctx, "krb5");
1016 1077 : struct torture_suite *kdc_suite = torture_suite_create(suite, "kdc");
1017 1077 : suite->description = talloc_strdup(suite, "Kerberos tests");
1018 1077 : kdc_suite->description = talloc_strdup(kdc_suite, "Kerberos KDC tests");
1019 :
1020 1077 : torture_suite_add_simple_test(kdc_suite, "as-req-cmdline",
1021 : torture_krb5_as_req_cmdline);
1022 :
1023 1077 : torture_suite_add_simple_test(kdc_suite, "as-req-pac-request",
1024 : torture_krb5_as_req_pac_request);
1025 :
1026 1077 : torture_suite_add_simple_test(kdc_suite, "as-req-break-pw",
1027 : torture_krb5_as_req_break_pw);
1028 :
1029 1077 : torture_suite_add_simple_test(kdc_suite, "as-req-clock-skew",
1030 : torture_krb5_as_req_clock_skew);
1031 :
1032 1077 : torture_suite_add_simple_test(kdc_suite,
1033 : "as-req-aes",
1034 : torture_krb5_as_req_aes);
1035 :
1036 1077 : torture_suite_add_simple_test(kdc_suite,
1037 : "as-req-rc4",
1038 : torture_krb5_as_req_rc4);
1039 :
1040 1077 : torture_suite_add_simple_test(kdc_suite,
1041 : "as-req-aes-rc4",
1042 : torture_krb5_as_req_aes_rc4);
1043 :
1044 : /*
1045 : * This is in and out of the client.
1046 : * Out refers to requests, in refers to replies
1047 : */
1048 1077 : torture_suite_add_simple_test(kdc_suite,
1049 : "as-req-change-server-in",
1050 : torture_krb5_as_req_change_server_in);
1051 :
1052 1077 : torture_suite_add_simple_test(kdc_suite,
1053 : "as-req-change-server-out",
1054 : torture_krb5_as_req_change_server_out);
1055 :
1056 1077 : torture_suite_add_simple_test(kdc_suite,
1057 : "as-req-change-server-both",
1058 : torture_krb5_as_req_change_server_both);
1059 :
1060 1077 : torture_suite_add_suite(kdc_suite, torture_krb5_canon(kdc_suite));
1061 1077 : torture_suite_add_suite(suite, kdc_suite);
1062 :
1063 1077 : torture_register_suite(ctx, suite);
1064 1077 : return NT_STATUS_OK;
1065 : }
|