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