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