4ea779b90edb6ead5fc4a772774a0a752bd7ec03
[samba.git] / source4 / torture / krb5 / kdc-heimdal.c
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/popt_common.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 };
66
67 /*
68  * Confirm that the outgoing packet meets certain expectations.  This
69  * should be extended to further assert the correct and expected
70  * behaviour of the krb5 libs, so we know what we are sending to the
71  * server.
72  *
73  */
74
75 static bool torture_krb5_pre_send_test(struct torture_krb5_context *test_context, const krb5_data *send_buf)
76 {
77         size_t used;
78         switch (test_context->test)
79         {
80         case TORTURE_KRB5_TEST_PLAIN:
81         case TORTURE_KRB5_TEST_PAC_REQUEST:
82         case TORTURE_KRB5_TEST_BREAK_PW:
83         case TORTURE_KRB5_TEST_CLOCK_SKEW:
84         case TORTURE_KRB5_TEST_AES:
85         case TORTURE_KRB5_TEST_RC4:
86         case TORTURE_KRB5_TEST_AES_RC4:
87         case TORTURE_KRB5_TEST_CHANGE_SERVER_IN:
88                 torture_assert_int_equal(test_context->tctx,
89                                          decode_AS_REQ(send_buf->data, send_buf->length, &test_context->as_req, &used), 0,
90                                          "decode_AS_REQ failed");
91                 torture_assert_int_equal(test_context->tctx, used, send_buf->length, "length mismatch");
92                 torture_assert_int_equal(test_context->tctx, test_context->as_req.pvno, 5, "Got wrong as_req->pvno");
93                 break;
94         case TORTURE_KRB5_TEST_CHANGE_SERVER_OUT:
95         case TORTURE_KRB5_TEST_CHANGE_SERVER_BOTH:
96                 break;
97         }
98         return true;
99 }
100
101 static bool torture_check_krb5_error(struct torture_krb5_context *test_context,
102                                      const krb5_data *reply,
103                                      krb5_error_code expected_error,
104                                      bool check_pa_data)
105 {
106         KRB_ERROR error = { 0 };
107         size_t used = 0;
108         int rc;
109
110         rc = decode_KRB_ERROR(reply->data, reply->length, &error, &used);
111         torture_assert_int_equal(test_context->tctx,
112                                  rc, 0,
113                                  "decode_AS_REP failed");
114
115         torture_assert_int_equal(test_context->tctx,
116                                  used, reply->length,
117                                  "length mismatch");
118         torture_assert_int_equal(test_context->tctx,
119                                  error.pvno, 5,
120                                  "Got wrong error.pvno");
121         torture_assert_int_equal(test_context->tctx,
122                                  error.error_code, expected_error - KRB5KDC_ERR_NONE,
123                                  "Got wrong error.error_code");
124
125         if (check_pa_data) {
126                 METHOD_DATA m;
127                 size_t len;
128                 int i;
129                 bool found = false;
130                         torture_assert(test_context->tctx,
131                                        error.e_data != NULL,
132                                        "No e-data returned");
133
134                         rc = decode_METHOD_DATA(error.e_data->data,
135                                                 error.e_data->length,
136                                                 &m,
137                                                 &len);
138                         torture_assert_int_equal(test_context->tctx,
139                                                  rc, 0,
140                                                  "Got invalid method data");
141
142                         torture_assert(test_context->tctx,
143                                        m.len > 0,
144                                        "No PA_DATA given");
145                         for (i = 0; i < m.len; i++) {
146                                 if (m.val[i].padata_type == KRB5_PADATA_ENC_TIMESTAMP) {
147                                         found = true;
148                                         break;
149                                 }
150                         }
151                         torture_assert(test_context->tctx,
152                                        found,
153                                        "Encrypted timestamp not found");
154         }
155
156         free_KRB_ERROR(&error);
157
158         return true;
159 }
160
161 static bool torture_check_krb5_as_rep_enctype(struct torture_krb5_context *test_context,
162                                               const krb5_data *reply,
163                                               krb5_enctype expected_enctype)
164 {
165         ENCTYPE reply_enctype = { 0 };
166         size_t used = 0;
167         int rc;
168
169         rc = decode_AS_REP(reply->data,
170                            reply->length,
171                            &test_context->as_rep,
172                            &used);
173         torture_assert_int_equal(test_context->tctx,
174                                  rc, 0,
175                                  "decode_AS_REP failed");
176         torture_assert_int_equal(test_context->tctx,
177                                  used, reply->length,
178                                  "length mismatch");
179         torture_assert_int_equal(test_context->tctx,
180                                  test_context->as_rep.pvno, 5,
181                                  "Got wrong as_rep->pvno");
182         torture_assert_int_equal(test_context->tctx,
183                                  test_context->as_rep.ticket.tkt_vno, 5,
184                                  "Got wrong as_rep->ticket.tkt_vno");
185         torture_assert(test_context->tctx,
186                        test_context->as_rep.ticket.enc_part.kvno,
187                        "Did not get a KVNO in test_context->as_rep.ticket.enc_part.kvno");
188
189         reply_enctype = test_context->as_rep.enc_part.etype;
190
191         torture_assert_int_equal(test_context->tctx,
192                                  reply_enctype, expected_enctype,
193                                  "Ticket encrypted with invalid algorithm");
194
195         return true;
196 }
197
198 /*
199  * Confirm that the incoming packet from the KDC meets certain
200  * expectations.  This uses a switch and the packet count to work out
201  * what test we are in, and where in the test we are, so we can assert
202  * on the expected reply packets from the KDC.
203  *
204  */
205
206 static bool torture_krb5_post_recv_test(struct torture_krb5_context *test_context, krb5_data *recv_buf)
207 {
208         KRB_ERROR error;
209         size_t used;
210         bool ok;
211
212         switch (test_context->test)
213         {
214         case TORTURE_KRB5_TEST_CHANGE_SERVER_OUT:
215         case TORTURE_KRB5_TEST_PLAIN:
216                 if (test_context->packet_count == 0) {
217                         ok = torture_check_krb5_error(test_context,
218                                                       recv_buf,
219                                                       KRB5KDC_ERR_PREAUTH_REQUIRED,
220                                                       false);
221                         torture_assert(test_context->tctx,
222                                        ok,
223                                        "torture_check_krb5_error failed");
224                 } else if ((decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used) == 0)
225                            && (test_context->packet_count == 1)) {
226                         torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch");
227                         torture_assert_int_equal(test_context->tctx, error.pvno, 5, "Got wrong error.pvno");
228                         torture_assert_int_equal(test_context->tctx, error.error_code, KRB5KRB_ERR_RESPONSE_TOO_BIG - KRB5KDC_ERR_NONE,
229                                                  "Got wrong error.error_code");
230                         free_KRB_ERROR(&error);
231                 } else {
232                         torture_assert_int_equal(test_context->tctx,
233                                                  decode_AS_REP(recv_buf->data, recv_buf->length, &test_context->as_rep, &used), 0,
234                                                  "decode_AS_REP failed");
235                         torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch");
236                         torture_assert_int_equal(test_context->tctx,
237                                                  test_context->as_rep.pvno, 5,
238                                                  "Got wrong as_rep->pvno");
239                         torture_assert_int_equal(test_context->tctx,
240                                                  test_context->as_rep.ticket.tkt_vno, 5,
241                                                  "Got wrong as_rep->ticket.tkt_vno");
242                         torture_assert(test_context->tctx,
243                                        test_context->as_rep.ticket.enc_part.kvno,
244                                        "Did not get a KVNO in test_context->as_rep.ticket.enc_part.kvno");
245                         if (torture_setting_bool(test_context->tctx, "expect_cached_at_rodc", false)) {
246                                 torture_assert_int_not_equal(test_context->tctx,
247                                                              *test_context->as_rep.ticket.enc_part.kvno & 0xFFFF0000,
248                                                              0, "Did not get a RODC number in the KVNO");
249                         } else {
250                                 torture_assert_int_equal(test_context->tctx,
251                                                          *test_context->as_rep.ticket.enc_part.kvno & 0xFFFF0000,
252                                                          0, "Unexpecedly got a RODC number in the KVNO");
253                         }
254                         free_AS_REP(&test_context->as_rep);
255                 }
256                 torture_assert(test_context->tctx, test_context->packet_count < 3, "too many packets");
257                 free_AS_REQ(&test_context->as_req);
258                 break;
259
260                 /*
261                  * Confirm correct error codes when we ask for the PAC.  This behaviour is rather odd...
262                  */
263         case TORTURE_KRB5_TEST_PAC_REQUEST:
264                 if (test_context->packet_count == 0) {
265                         ok = torture_check_krb5_error(test_context,
266                                                       recv_buf,
267                                                       KRB5KRB_ERR_RESPONSE_TOO_BIG,
268                                                       false);
269                         torture_assert(test_context->tctx,
270                                        ok,
271                                        "torture_check_krb5_error failed");
272                 } else if (test_context->packet_count == 1) {
273                         ok = torture_check_krb5_error(test_context,
274                                                       recv_buf,
275                                                       KRB5KDC_ERR_PREAUTH_REQUIRED,
276                                                       false);
277                         torture_assert(test_context->tctx,
278                                        ok,
279                                        "torture_check_krb5_error failed");
280                 } else if ((decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used) == 0)
281                            && (test_context->packet_count == 2)) {
282                         torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch");
283                         torture_assert_int_equal(test_context->tctx, error.pvno, 5, "Got wrong error.pvno");
284                         torture_assert_int_equal(test_context->tctx, error.error_code, KRB5KRB_ERR_RESPONSE_TOO_BIG - KRB5KDC_ERR_NONE,
285                                                  "Got wrong error.error_code");
286                         free_KRB_ERROR(&error);
287                 } else {
288                         torture_assert_int_equal(test_context->tctx,
289                                                  decode_AS_REP(recv_buf->data, recv_buf->length, &test_context->as_rep, &used), 0,
290                                                  "decode_AS_REP failed");
291                         torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch");
292                         torture_assert_int_equal(test_context->tctx, test_context->as_rep.pvno, 5, "Got wrong as_rep->pvno");
293                         free_AS_REP(&test_context->as_rep);
294                 }
295                 torture_assert(test_context->tctx, test_context->packet_count < 3, "too many packets");
296                 free_AS_REQ(&test_context->as_req);
297                 break;
298
299                 /*
300                  * Confirm correct error codes when we deliberatly send the wrong password
301                  */
302         case TORTURE_KRB5_TEST_BREAK_PW:
303                 if (test_context->packet_count == 0) {
304                         ok = torture_check_krb5_error(test_context,
305                                                       recv_buf,
306                                                       KRB5KDC_ERR_PREAUTH_REQUIRED,
307                                                       false);
308                         torture_assert(test_context->tctx,
309                                        ok,
310                                        "torture_check_krb5_error failed");
311                 } else if (test_context->packet_count == 1) {
312                         ok = torture_check_krb5_error(test_context,
313                                                       recv_buf,
314                                                       KRB5KDC_ERR_PREAUTH_FAILED,
315                                                       true);
316                         torture_assert(test_context->tctx,
317                                        ok,
318                                        "torture_check_krb5_error failed");
319                 }
320                 torture_assert(test_context->tctx, test_context->packet_count < 2, "too many packets");
321                 free_AS_REQ(&test_context->as_req);
322                 break;
323
324                 /*
325                  * Confirm correct error codes when we deliberatly skew the client clock
326                  */
327         case TORTURE_KRB5_TEST_CLOCK_SKEW:
328                 if (test_context->packet_count == 0) {
329                         ok = torture_check_krb5_error(test_context,
330                                                       recv_buf,
331                                                       KRB5KDC_ERR_PREAUTH_REQUIRED,
332                                                       false);
333                         torture_assert(test_context->tctx,
334                                        ok,
335                                        "torture_check_krb5_error failed");
336                 } else if (test_context->packet_count == 1) {
337                         ok = torture_check_krb5_error(test_context,
338                                                       recv_buf,
339                                                       KRB5KRB_AP_ERR_SKEW,
340                                                       false);
341                         torture_assert(test_context->tctx,
342                                        ok,
343                                        "torture_check_krb5_error failed");
344                 }
345                 torture_assert(test_context->tctx, test_context->packet_count < 2, "too many packets");
346                 free_AS_REQ(&test_context->as_req);
347                 break;
348         case TORTURE_KRB5_TEST_AES:
349                 torture_comment(test_context->tctx, "TORTURE_KRB5_TEST_AES\n");
350
351                 if (test_context->packet_count == 0) {
352                         ok = torture_check_krb5_error(test_context,
353                                                       recv_buf,
354                                                       KRB5KDC_ERR_PREAUTH_REQUIRED,
355                                                       false);
356                         torture_assert(test_context->tctx,
357                                        ok,
358                                        "torture_check_krb5_error failed");
359                 } else if (krb5_is_krb_error(recv_buf)) {
360                         ok = torture_check_krb5_error(test_context,
361                                                       recv_buf,
362                                                       KRB5KRB_ERR_RESPONSE_TOO_BIG,
363                                                       false);
364                         torture_assert(test_context->tctx,
365                                        ok,
366                                        "torture_check_krb5_error failed");
367                 } else {
368                         ok = torture_check_krb5_as_rep_enctype(test_context,
369                                                                recv_buf,
370                                                                KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96);
371                         torture_assert(test_context->tctx,
372                                        ok,
373                                        "torture_check_krb5_as_rep_enctype failed");
374                 }
375
376                 torture_assert(test_context->tctx,
377                                test_context->packet_count < 3,
378                                "Too many packets");
379                 break;
380         case TORTURE_KRB5_TEST_RC4:
381                 torture_comment(test_context->tctx, "TORTURE_KRB5_TEST_RC4\n");
382
383                 if (test_context->packet_count == 0) {
384                         ok = torture_check_krb5_error(test_context,
385                                                       recv_buf,
386                                                       KRB5KDC_ERR_PREAUTH_REQUIRED,
387                                                       false);
388                         torture_assert(test_context->tctx,
389                                        ok,
390                                        "torture_check_krb5_error failed");
391                 } else if (krb5_is_krb_error(recv_buf)) {
392                         ok = torture_check_krb5_error(test_context,
393                                                       recv_buf,
394                                                       KRB5KRB_ERR_RESPONSE_TOO_BIG,
395                                                       false);
396                         torture_assert(test_context->tctx,
397                                        ok,
398                                        "torture_check_krb5_error failed");
399                 } else {
400                         ok = torture_check_krb5_as_rep_enctype(test_context,
401                                                                recv_buf,
402                                                                KRB5_ENCTYPE_ARCFOUR_HMAC_MD5);
403                         torture_assert(test_context->tctx,
404                                        ok,
405                                        "torture_check_krb5_as_rep_enctype failed");
406                 }
407
408                 torture_assert(test_context->tctx,
409                                test_context->packet_count < 3,
410                                "Too many packets");
411                 break;
412         case TORTURE_KRB5_TEST_AES_RC4:
413                 torture_comment(test_context->tctx, "TORTURE_KRB5_TEST_AES_RC4\n");
414
415                 if (test_context->packet_count == 0) {
416                         ok = torture_check_krb5_error(test_context,
417                                                       recv_buf,
418                                                       KRB5KDC_ERR_PREAUTH_REQUIRED,
419                                                       false);
420                         torture_assert(test_context->tctx,
421                                        ok,
422                                        "torture_check_krb5_error failed");
423                 } else if (krb5_is_krb_error(recv_buf)) {
424                         ok = torture_check_krb5_error(test_context,
425                                                       recv_buf,
426                                                       KRB5KRB_ERR_RESPONSE_TOO_BIG,
427                                                       false);
428                         torture_assert(test_context->tctx,
429                                        ok,
430                                        "torture_check_krb5_error failed");
431                 } else {
432                         ok = torture_check_krb5_as_rep_enctype(test_context,
433                                                                recv_buf,
434                                                                KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96);
435                         torture_assert(test_context->tctx,
436                                        ok,
437                                        "torture_check_krb5_as_rep_enctype failed");
438                 }
439
440                 torture_assert(test_context->tctx,
441                                test_context->packet_count < 3,
442                                "Too many packets");
443                 break;
444         case TORTURE_KRB5_TEST_CHANGE_SERVER_IN:
445         case TORTURE_KRB5_TEST_CHANGE_SERVER_BOTH:
446         {
447                 AS_REP mod_as_rep;
448                 krb5_error_code k5ret;
449                 krb5_data modified_recv_buf;
450                 if (test_context->packet_count == 0) {
451                         ok = torture_check_krb5_error(test_context,
452                                                       recv_buf,
453                                                       KRB5KDC_ERR_PREAUTH_REQUIRED,
454                                                       false);
455                         torture_assert(test_context->tctx,
456                                        ok,
457                                        "torture_check_krb5_error failed");
458                 } else if ((decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used) == 0)
459                            && (test_context->packet_count == 1)) {
460                         torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch");
461                         torture_assert_int_equal(test_context->tctx, error.pvno, 5, "Got wrong error.pvno");
462                         torture_assert_int_equal(test_context->tctx, error.error_code, KRB5KRB_ERR_RESPONSE_TOO_BIG - KRB5KDC_ERR_NONE,
463                                                  "Got wrong error.error_code");
464                         free_KRB_ERROR(&error);
465                 } else {
466                         torture_assert_int_equal(test_context->tctx,
467                                                  decode_AS_REP(recv_buf->data, recv_buf->length, &test_context->as_rep, &used), 0,
468                                                  "decode_AS_REP failed");
469                         torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch");
470                         torture_assert_int_equal(test_context->tctx,
471                                                  test_context->as_rep.pvno, 5,
472                                                  "Got wrong as_rep->pvno");
473                         torture_assert_int_equal(test_context->tctx,
474                                                  test_context->as_rep.ticket.tkt_vno, 5,
475                                                  "Got wrong as_rep->ticket.tkt_vno");
476                         torture_assert_int_equal(test_context->tctx,
477                                                  test_context->as_rep.ticket.sname.name_string.len, 2,
478                                                  "Got wrong as_rep->ticket.sname.name_string.len");
479                         free(test_context->as_rep.ticket.sname.name_string.val[0]);
480                         free(test_context->as_rep.ticket.sname.name_string.val[1]);
481                         test_context->as_rep.ticket.sname.name_string.val[0] = strdup("bad");
482                         test_context->as_rep.ticket.sname.name_string.val[1] = strdup("mallory");
483
484                         mod_as_rep = test_context->as_rep;
485
486                         ASN1_MALLOC_ENCODE(AS_REP, modified_recv_buf.data, modified_recv_buf.length,
487                                            &mod_as_rep, &used, k5ret);
488                         torture_assert_int_equal(test_context->tctx,
489                                                  k5ret, 0,
490                                                  "encode_AS_REQ failed");
491                         krb5_data_free(recv_buf);
492
493                         *recv_buf = modified_recv_buf;
494                         free_AS_REQ(&test_context->as_req);
495                 }
496                 torture_assert(test_context->tctx, test_context->packet_count < 3, "too many packets");
497
498                 break;
499         }
500         }
501
502
503         return true;
504 }
505
506
507 /*
508  * This function is set in torture_krb5_init_context as krb5
509  * send_and_recv function.  This allows us to override what server the
510  * test is aimed at, and to inspect the packets just before they are
511  * sent to the network, and before they are processed on the recv
512  * side.
513  *
514  * The torture_krb5_pre_send_test() and torture_krb5_post_recv_test()
515  * functions are implement the actual tests.
516  *
517  * When this asserts, the caller will get a spurious 'cannot contact
518  * any KDC' message.
519  *
520  */
521 static krb5_error_code smb_krb5_send_and_recv_func_override(krb5_context context,
522                                                     void *data, /* struct torture_krb5_context */
523                                                     krb5_krbhst_info *hi,
524                                                     time_t timeout,
525                                                     const krb5_data *send_buf,
526                                                     krb5_data *recv_buf)
527 {
528         krb5_error_code k5ret;
529         bool ok;
530
531         struct torture_krb5_context *test_context
532                 = talloc_get_type_abort(data, struct torture_krb5_context);
533
534         ok = torture_krb5_pre_send_test(test_context, send_buf);
535         if (ok == false) {
536                 return EINVAL;
537         }
538
539         k5ret = smb_krb5_send_and_recv_func_forced(context, test_context->server,
540                                                     hi, timeout, send_buf, recv_buf);
541         if (k5ret != 0) {
542                 return k5ret;
543         }
544         ok = torture_krb5_post_recv_test(test_context, recv_buf);
545         if (ok == false) {
546                 return EINVAL;
547         }
548
549         test_context->packet_count++;
550
551         return k5ret;
552 }
553
554 static int test_context_destructor(struct torture_krb5_context *test_context)
555 {
556         freeaddrinfo(test_context->server);
557         return 0;
558 }
559
560
561 static bool torture_krb5_init_context(struct torture_context *tctx,
562                                       enum torture_krb5_test test,
563                                       struct smb_krb5_context **smb_krb5_context)
564 {
565         const char *host = torture_setting_string(tctx, "host", NULL);
566         krb5_error_code k5ret;
567         bool ok;
568
569         struct torture_krb5_context *test_context = talloc_zero(tctx, struct torture_krb5_context);
570         torture_assert(tctx, test_context != NULL, "Failed to allocate");
571
572         test_context->test = test;
573         test_context->tctx = tctx;
574
575         k5ret = smb_krb5_init_context(tctx, tctx->lp_ctx, smb_krb5_context);
576         torture_assert_int_equal(tctx, k5ret, 0, "smb_krb5_init_context failed");
577
578         ok = interpret_string_addr_internal(&test_context->server, host, AI_NUMERICHOST);
579         torture_assert(tctx, ok, "Failed to parse target server");
580
581         talloc_set_destructor(test_context, test_context_destructor);
582
583         set_sockaddr_port(test_context->server->ai_addr, 88);
584
585         k5ret = krb5_set_send_to_kdc_func((*smb_krb5_context)->krb5_context,
586                                           smb_krb5_send_and_recv_func_override,
587                                           test_context);
588         torture_assert_int_equal(tctx, k5ret, 0, "krb5_set_send_to_kdc_func failed");
589         return true;
590 }
591
592 static bool torture_krb5_as_req_creds(struct torture_context *tctx,
593                                       struct cli_credentials *credentials,
594                                       enum torture_krb5_test test)
595 {
596         krb5_error_code k5ret;
597         bool ok;
598         krb5_creds my_creds;
599         krb5_principal principal;
600         struct smb_krb5_context *smb_krb5_context;
601         krb5_context k5_context;
602         enum credentials_obtained obtained;
603         const char *error_string;
604         const char *password = cli_credentials_get_password(credentials);
605         const char *expected_principal_string;
606         krb5_get_init_creds_opt *krb_options = NULL;
607         const char *realm;
608
609         ok = torture_krb5_init_context(tctx, test, &smb_krb5_context);
610         torture_assert(tctx, ok, "torture_krb5_init_context failed");
611         k5_context = smb_krb5_context->krb5_context;
612
613         expected_principal_string
614                 = cli_credentials_get_principal(credentials,
615                                                 tctx);
616
617         realm = strupper_talloc(tctx, cli_credentials_get_realm(credentials));
618         k5ret = principal_from_credentials(tctx, credentials, smb_krb5_context,
619                                            &principal, &obtained,  &error_string);
620         torture_assert_int_equal(tctx, k5ret, 0, error_string);
621
622         switch (test)
623         {
624         case TORTURE_KRB5_TEST_PLAIN:
625         case TORTURE_KRB5_TEST_CHANGE_SERVER_OUT:
626         case TORTURE_KRB5_TEST_CHANGE_SERVER_IN:
627         case TORTURE_KRB5_TEST_CHANGE_SERVER_BOTH:
628                 break;
629
630         case TORTURE_KRB5_TEST_PAC_REQUEST:
631                 torture_assert_int_equal(tctx,
632                                          krb5_get_init_creds_opt_alloc(smb_krb5_context->krb5_context, &krb_options),
633                                          0, "krb5_get_init_creds_opt_alloc failed");
634
635                 torture_assert_int_equal(tctx,
636                                          krb5_get_init_creds_opt_set_pac_request(smb_krb5_context->krb5_context, krb_options, true),
637                                          0, "krb5_get_init_creds_opt_set_pac_request failed");
638                 break;
639
640         case TORTURE_KRB5_TEST_BREAK_PW:
641                 password = "NOT the password";
642                 break;
643
644         case TORTURE_KRB5_TEST_CLOCK_SKEW:
645                 torture_assert_int_equal(tctx,
646                                          krb5_set_real_time(smb_krb5_context->krb5_context, time(NULL) + 3600, 0),
647                                          0, "krb5_set_real_time failed");
648                 break;
649
650         case TORTURE_KRB5_TEST_AES: {
651                 krb5_enctype etype_list[] = { KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96 };
652
653                 k5ret = krb5_get_init_creds_opt_alloc(smb_krb5_context->krb5_context,
654                                                       &krb_options);
655                 torture_assert_int_equal(tctx,
656                                          k5ret, 0,
657                                          "krb5_get_init_creds_opt_alloc failed");
658
659                 krb5_get_init_creds_opt_set_etype_list(krb_options,
660                                                        etype_list,
661                                                        1);
662                 break;
663         }
664         case TORTURE_KRB5_TEST_RC4: {
665                 krb5_enctype etype_list[] = { KRB5_ENCTYPE_ARCFOUR_HMAC_MD5 };
666
667                 k5ret = krb5_get_init_creds_opt_alloc(smb_krb5_context->krb5_context,
668                                                       &krb_options);
669                 torture_assert_int_equal(tctx,
670                                          k5ret, 0,
671                                          "krb5_get_init_creds_opt_alloc failed");
672
673                 krb5_get_init_creds_opt_set_etype_list(krb_options,
674                                                        etype_list,
675                                                        1);
676                 break;
677         }
678         case TORTURE_KRB5_TEST_AES_RC4: {
679                 krb5_enctype etype_list[] = { KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96,
680                                               KRB5_ENCTYPE_ARCFOUR_HMAC_MD5 };
681
682                 k5ret = krb5_get_init_creds_opt_alloc(smb_krb5_context->krb5_context,
683                                                       &krb_options);
684                 torture_assert_int_equal(tctx,
685                                          k5ret, 0,
686                                          "krb5_get_init_creds_opt_alloc failed");
687
688                 krb5_get_init_creds_opt_set_etype_list(krb_options,
689                                                        etype_list,
690                                                        2);
691                 break;
692         }
693
694         } /* end switch */
695
696         k5ret = krb5_get_init_creds_password(smb_krb5_context->krb5_context, &my_creds, principal,
697                                              password, NULL, NULL, 0,
698                                              NULL, krb_options);
699         krb5_get_init_creds_opt_free(smb_krb5_context->krb5_context, krb_options);
700
701         switch (test)
702         {
703         case TORTURE_KRB5_TEST_PLAIN:
704         case TORTURE_KRB5_TEST_CHANGE_SERVER_IN:
705         case TORTURE_KRB5_TEST_PAC_REQUEST:
706         case TORTURE_KRB5_TEST_AES:
707         case TORTURE_KRB5_TEST_RC4:
708         case TORTURE_KRB5_TEST_AES_RC4:
709         {
710                 char *got_principal_string;
711                 char *assertion_message;
712                 torture_assert_int_equal(tctx, k5ret, 0, "krb5_get_init_creds_password failed");
713
714                 torture_assert_int_equal(tctx,
715                                          krb5_principal_get_type(k5_context,
716                                                                  my_creds.client),
717                                          KRB5_NT_PRINCIPAL,
718                                          "smb_krb5_init_context gave incorrect client->name.name_type");
719
720                 torture_assert_int_equal(tctx,
721                                          krb5_unparse_name(k5_context,
722                                                            my_creds.client,
723                                                            &got_principal_string), 0,
724                                          "krb5_unparse_name failed");
725
726                 assertion_message = talloc_asprintf(tctx,
727                                                     "krb5_get_init_creds_password returned a different principal %s to what was expected %s",
728                                                     got_principal_string, expected_principal_string);
729                 krb5_free_unparsed_name(k5_context, got_principal_string);
730
731                 torture_assert(tctx, krb5_principal_compare(k5_context,
732                                                             my_creds.client,
733                                                             principal),
734                                assertion_message);
735
736
737                 torture_assert_str_equal(tctx,
738                                          my_creds.server->name.name_string.val[0],
739                                          "krbtgt",
740                                          "Mismatch in name between AS_REP and expected response, expected krbtgt");
741                 torture_assert_str_equal(tctx,
742                                          my_creds.server->name.name_string.val[1],
743                                          realm,
744                                          "Mismatch in realm part of krbtgt/ in AS_REP, expected krbtgt/REALM@REALM");
745
746                 torture_assert_str_equal(tctx,
747                                          my_creds.server->realm,
748                                          realm,
749                                          "Mismatch in server realm in AS_REP, expected krbtgt/REALM@REALM");
750
751                 break;
752         }
753         case TORTURE_KRB5_TEST_BREAK_PW:
754                 torture_assert_int_equal(tctx, k5ret, KRB5KDC_ERR_PREAUTH_FAILED, "krb5_get_init_creds_password should have failed");
755                 return true;
756
757         case TORTURE_KRB5_TEST_CLOCK_SKEW:
758                 torture_assert_int_equal(tctx, k5ret, KRB5KRB_AP_ERR_SKEW, "krb5_get_init_creds_password should have failed");
759                 return true;
760
761         case TORTURE_KRB5_TEST_CHANGE_SERVER_OUT:
762         case TORTURE_KRB5_TEST_CHANGE_SERVER_BOTH:
763                 torture_assert_int_equal(tctx, k5ret, 0, "krb5_get_init_creds_password failed");
764                 break;
765         }
766
767         k5ret = krb5_free_cred_contents(smb_krb5_context->krb5_context, &my_creds);
768         torture_assert_int_equal(tctx, k5ret, 0, "krb5_free_creds failed");
769
770         return true;
771 }
772
773 static bool torture_krb5_as_req_cmdline(struct torture_context *tctx)
774 {
775         return torture_krb5_as_req_creds(tctx, popt_get_cmdline_credentials(),
776                         TORTURE_KRB5_TEST_PLAIN);
777 }
778
779 static bool torture_krb5_as_req_pac_request(struct torture_context *tctx)
780 {
781         if (torture_setting_bool(tctx, "expect_rodc", false)) {
782                 torture_skip(tctx, "This test needs further investigation in the RODC case against a Windows DC, in particular with non-cached users");
783         }
784         return torture_krb5_as_req_creds(tctx, popt_get_cmdline_credentials(),
785                         TORTURE_KRB5_TEST_PAC_REQUEST);
786 }
787
788 static bool torture_krb5_as_req_break_pw(struct torture_context *tctx)
789 {
790         return torture_krb5_as_req_creds(tctx, popt_get_cmdline_credentials(),
791                         TORTURE_KRB5_TEST_BREAK_PW);
792 }
793
794 static bool torture_krb5_as_req_clock_skew(struct torture_context *tctx)
795 {
796         return torture_krb5_as_req_creds(tctx, popt_get_cmdline_credentials(),
797                         TORTURE_KRB5_TEST_CLOCK_SKEW);
798 }
799
800 static bool torture_krb5_as_req_aes(struct torture_context *tctx)
801 {
802         return torture_krb5_as_req_creds(tctx,
803                                          popt_get_cmdline_credentials(),
804                                          TORTURE_KRB5_TEST_AES);
805 }
806
807 static bool torture_krb5_as_req_rc4(struct torture_context *tctx)
808 {
809         return torture_krb5_as_req_creds(tctx,
810                                          popt_get_cmdline_credentials(),
811                                          TORTURE_KRB5_TEST_RC4);
812 }
813
814 static bool torture_krb5_as_req_aes_rc4(struct torture_context *tctx)
815 {
816         return torture_krb5_as_req_creds(tctx,
817                                          popt_get_cmdline_credentials(),
818                                          TORTURE_KRB5_TEST_AES_RC4);
819 }
820
821 /* Checking for the "Orpheus' Lyre" attack */
822 static bool torture_krb5_as_req_change_server_out(struct torture_context *tctx)
823 {
824         return torture_krb5_as_req_creds(tctx,
825                                          popt_get_cmdline_credentials(),
826                                          TORTURE_KRB5_TEST_CHANGE_SERVER_OUT);
827 }
828
829 static bool torture_krb5_as_req_change_server_in(struct torture_context *tctx)
830 {
831         return torture_krb5_as_req_creds(tctx,
832                                          popt_get_cmdline_credentials(),
833                                          TORTURE_KRB5_TEST_CHANGE_SERVER_IN);
834 }
835
836 static bool torture_krb5_as_req_change_server_both(struct torture_context *tctx)
837 {
838         return torture_krb5_as_req_creds(tctx,
839                                          popt_get_cmdline_credentials(),
840                                          TORTURE_KRB5_TEST_CHANGE_SERVER_BOTH);
841 }
842
843 NTSTATUS torture_krb5_init(TALLOC_CTX *ctx)
844 {
845         struct torture_suite *suite = torture_suite_create(ctx, "krb5");
846         struct torture_suite *kdc_suite = torture_suite_create(suite, "kdc");
847         suite->description = talloc_strdup(suite, "Kerberos tests");
848         kdc_suite->description = talloc_strdup(kdc_suite, "Kerberos KDC tests");
849
850         torture_suite_add_simple_test(kdc_suite, "as-req-cmdline",
851                                       torture_krb5_as_req_cmdline);
852
853         torture_suite_add_simple_test(kdc_suite, "as-req-pac-request",
854                                       torture_krb5_as_req_pac_request);
855
856         torture_suite_add_simple_test(kdc_suite, "as-req-break-pw",
857                                       torture_krb5_as_req_break_pw);
858
859         torture_suite_add_simple_test(kdc_suite, "as-req-clock-skew",
860                                       torture_krb5_as_req_clock_skew);
861
862         torture_suite_add_simple_test(kdc_suite,
863                                       "as-req-aes",
864                                       torture_krb5_as_req_aes);
865
866         torture_suite_add_simple_test(kdc_suite,
867                                       "as-req-rc4",
868                                       torture_krb5_as_req_rc4);
869
870         torture_suite_add_simple_test(kdc_suite,
871                                       "as-req-aes-rc4",
872                                       torture_krb5_as_req_aes_rc4);
873
874         /* 
875          * This is in and out of the client. 
876          * Out refers to requests, in refers to replies
877          */
878         torture_suite_add_simple_test(kdc_suite,
879                                       "as-req-change-server-in",
880                                       torture_krb5_as_req_change_server_in);
881
882         torture_suite_add_simple_test(kdc_suite,
883                                       "as-req-change-server-out",
884                                       torture_krb5_as_req_change_server_out);
885
886         torture_suite_add_simple_test(kdc_suite,
887                                       "as-req-change-server-both",
888                                       torture_krb5_as_req_change_server_both);
889
890         torture_suite_add_suite(kdc_suite, torture_krb5_canon(kdc_suite));
891         torture_suite_add_suite(suite, kdc_suite);
892
893         torture_register_suite(ctx, suite);
894         return NT_STATUS_OK;
895 }