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