dffebd7403807084ee9d2b95ff56cc845b6f2d57
[gd/samba-autobuild/.git] / source4 / torture / krb5 / kdc-canon-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/krb5/proto.h"
27 #include "auth/credentials/credentials.h"
28 #include "lib/cmdline/popt_common.h"
29 #include "source4/auth/kerberos/kerberos.h"
30 #include "source4/auth/kerberos/kerberos_util.h"
31 #include "lib/util/util_net.h"
32 #include "auth/auth.h"
33 #include "auth/auth_sam_reply.h"
34 #include "auth/gensec/gensec.h"
35 #include "param/param.h"
36
37 #define TEST_CANONICALIZE     0x0000001
38 #define TEST_ENTERPRISE       0x0000002
39 #define TEST_UPPER_REALM      0x0000004
40 #define TEST_UPPER_USERNAME   0x0000008
41 #define TEST_NETBIOS_REALM    0x0000010
42 #define TEST_WIN2K            0x0000020
43 #define TEST_UPN              0x0000040
44 #define TEST_S4U2SELF         0x0000080
45 #define TEST_REMOVEDOLLAR     0x0000100
46 #define TEST_AS_REQ_SPN       0x0000200
47 #define TEST_MITM_S4U2SELF    0x0000400
48 #define TEST_ALL              0x00007FF
49
50 struct test_data {
51         const char *test_name;
52         const char *realm;
53         const char *real_realm;
54         const char *real_domain;
55         const char *username;
56         const char *real_username;
57         bool canonicalize;
58         bool enterprise;
59         bool upper_realm;
60         bool upper_username;
61         bool netbios_realm;
62         bool win2k;
63         bool upn;
64         bool other_upn_suffix;
65         bool s4u2self;
66         bool mitm_s4u2self;
67         bool removedollar;
68         bool as_req_spn;
69         bool spn_is_upn;
70         const char *krb5_service;
71         const char *krb5_hostname;
72 };
73
74 enum test_stage {
75         TEST_AS_REQ = 0,
76         TEST_TGS_REQ_KRBTGT_CANON = 1,
77         TEST_TGS_REQ_CANON = 2,
78         TEST_SELF_TRUST_TGS_REQ = 3,
79         TEST_TGS_REQ = 4,
80         TEST_TGS_REQ_KRBTGT = 5,
81         TEST_TGS_REQ_HOST = 6,
82         TEST_TGS_REQ_HOST_SRV_INST = 7,
83         TEST_TGS_REQ_HOST_SRV_HST = 8,
84         TEST_AS_REQ_SELF = 9,
85         TEST_DONE = 10
86 };
87
88 struct torture_krb5_context {
89         struct smb_krb5_context *smb_krb5_context;
90         struct torture_context *tctx;
91         struct addrinfo *server;
92         struct test_data *test_data;
93         int packet_count;
94         enum test_stage test_stage;
95         AS_REQ as_req;
96         AS_REP as_rep;
97         TGS_REQ tgs_req;
98         TGS_REP tgs_rep;
99 };
100
101 struct pac_data {
102         const char *principal_name;
103 };
104
105 /*
106  * A helper function which avoids touching the local databases to
107  * generate the session info, as we just want to verify the principal
108  * name that we found in the ticket not the full local token
109  */
110 static NTSTATUS test_generate_session_info_pac(struct auth4_context *auth_ctx,
111                                                TALLOC_CTX *mem_ctx,
112                                                struct smb_krb5_context *smb_krb5_context,
113                                                DATA_BLOB *pac_blob,
114                                                const char *principal_name,
115                                                const struct tsocket_address *remote_address,
116                                                uint32_t session_info_flags,
117                                                struct auth_session_info **session_info)
118 {
119         NTSTATUS nt_status;
120         struct auth_user_info_dc *user_info_dc;
121         TALLOC_CTX *tmp_ctx;
122         struct pac_data *pac_data;
123
124         tmp_ctx = talloc_named(mem_ctx, 0, "gensec_gssapi_session_info context");
125         NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
126
127         auth_ctx->private_data = pac_data = talloc_zero(auth_ctx, struct pac_data);
128
129         pac_data->principal_name = talloc_strdup(pac_data, principal_name);
130         if (!pac_data->principal_name) {
131                 talloc_free(tmp_ctx);
132                 return NT_STATUS_NO_MEMORY;
133         }
134
135         nt_status = kerberos_pac_blob_to_user_info_dc(tmp_ctx,
136                                                       *pac_blob,
137                                                       smb_krb5_context->krb5_context,
138                                                       &user_info_dc, NULL, NULL);
139         if (!NT_STATUS_IS_OK(nt_status)) {
140                 talloc_free(tmp_ctx);
141                 return nt_status;
142         }
143
144         if (user_info_dc->info->authenticated) {
145                 session_info_flags |= AUTH_SESSION_INFO_AUTHENTICATED;
146         }
147
148         session_info_flags |= AUTH_SESSION_INFO_SIMPLE_PRIVILEGES;
149         nt_status = auth_generate_session_info(mem_ctx,
150                                                NULL,
151                                                NULL,
152                                                user_info_dc, session_info_flags,
153                                                session_info);
154         if (!NT_STATUS_IS_OK(nt_status)) {
155                 talloc_free(tmp_ctx);
156                 return nt_status;
157         }
158
159         talloc_free(tmp_ctx);
160         return NT_STATUS_OK;
161 }
162
163 /* Check to see if we can pass the PAC across to the NETLOGON server for validation */
164
165 /* Also happens to be a really good one-step verfication of our Kerberos stack */
166
167 static bool test_accept_ticket(struct torture_context *tctx,
168                                struct cli_credentials *credentials,
169                                const char *principal,
170                                DATA_BLOB client_to_server)
171 {
172         NTSTATUS status;
173         struct gensec_security *gensec_server_context;
174         DATA_BLOB server_to_client;
175         struct auth4_context *auth_context;
176         struct auth_session_info *session_info;
177         struct pac_data *pac_data;
178         TALLOC_CTX *tmp_ctx = talloc_new(tctx);
179
180         torture_assert(tctx, tmp_ctx != NULL, "talloc_new() failed");
181
182         auth_context = talloc_zero(tmp_ctx, struct auth4_context);
183         torture_assert(tctx, auth_context != NULL, "talloc_new() failed");
184
185         auth_context->generate_session_info_pac = test_generate_session_info_pac;
186
187         status = gensec_server_start(tctx,
188                                      lpcfg_gensec_settings(tctx, tctx->lp_ctx),
189                                      auth_context, &gensec_server_context);
190         torture_assert_ntstatus_ok(tctx, status, "gensec_server_start (server) failed");
191
192         status = gensec_set_credentials(gensec_server_context, credentials);
193         torture_assert_ntstatus_ok(tctx, status, "gensec_set_credentials (server) failed");
194
195         status = gensec_start_mech_by_name(gensec_server_context, "krb5");
196         torture_assert_ntstatus_ok(tctx, status, "gensec_start_mech_by_name (server) failed");
197
198         server_to_client = data_blob(NULL, 0);
199
200         /* Do a client-server update dance */
201         status = gensec_update(gensec_server_context, tmp_ctx, client_to_server, &server_to_client);
202         torture_assert_ntstatus_ok(tctx, status, "gensec_update (server) failed");
203
204         /* Extract the PAC using Samba's code */
205
206         status = gensec_session_info(gensec_server_context, gensec_server_context, &session_info);
207         torture_assert_ntstatus_ok(tctx, status, "gensec_session_info failed");
208
209         pac_data = talloc_get_type(auth_context->private_data, struct pac_data);
210
211         torture_assert(tctx, pac_data != NULL, "gensec_update failed to fill in pac_data in auth_context");
212         torture_assert(tctx, pac_data->principal_name != NULL, "principal_name not present");
213         torture_assert_str_equal(tctx, pac_data->principal_name, principal, "wrong principal name");
214         return true;
215 }
216
217 krb5_error_code
218 _krb5_s4u2self_to_checksumdata(krb5_context context,
219                                const PA_S4U2Self *self,
220                                krb5_data *data);
221
222 /* Helper function to modify the principal in PA_FOR_USER padata */
223 static bool change_for_user_principal(struct torture_krb5_context *test_context,
224                                       krb5_data *modified_send_buf)
225 {
226         PA_DATA *for_user;
227         int i = 0;
228         size_t used;
229         krb5_error_code ret;
230         PA_S4U2Self self, mod_self;
231         krb5_data cksum_data;
232         krb5_principal admin;
233         heim_octet_string orig_padata_value;
234         krb5_context k5_ctx = test_context->smb_krb5_context->krb5_context;
235
236         for_user = krb5_find_padata(test_context->tgs_req.padata->val,
237                                     test_context->tgs_req.padata->len, KRB5_PADATA_FOR_USER, &i);
238         torture_assert(test_context->tctx, for_user != NULL, "No PA_FOR_USER in s4u2self request");
239         orig_padata_value = for_user->padata_value;
240
241         torture_assert_int_equal(test_context->tctx,
242                                  krb5_make_principal(k5_ctx, &admin, test_context->test_data->realm,
243                                                      "Administrator", NULL),
244                                  0, "krb5_make_principal() failed");
245         torture_assert_int_equal(test_context->tctx,
246                                  decode_PA_S4U2Self(for_user->padata_value.data,
247                                                     for_user->padata_value.length, &self, NULL),
248                                  0, "decode_PA_S4U2Self() failed");
249         mod_self = self;
250         mod_self.name = admin->name;
251
252         torture_assert_int_equal(test_context->tctx,
253                                  _krb5_s4u2self_to_checksumdata(k5_ctx, &mod_self, &cksum_data),
254                                  0, "_krb5_s4u2self_to_checksumdata() failed");
255         torture_assert_int_equal(test_context->tctx,
256                                  krb5_create_checksum(k5_ctx, NULL, KRB5_KU_OTHER_CKSUM,
257                                                       CKSUMTYPE_CRC32, cksum_data.data,
258                                                       cksum_data.length, &mod_self.cksum),
259                                  0, "krb5_create_checksum() failed");
260
261         ASN1_MALLOC_ENCODE(PA_S4U2Self, for_user->padata_value.data, for_user->padata_value.length,
262                            &mod_self, &used, ret);
263         torture_assert(test_context->tctx, ret == 0, "Failed to encode PA_S4U2Self ASN1 struct");
264         ASN1_MALLOC_ENCODE(TGS_REQ, modified_send_buf->data, modified_send_buf->length,
265                            &test_context->tgs_req, &used, ret);
266         torture_assert(test_context->tctx, ret == 0, "Failed to encode TGS_REQ ASN1 struct");
267
268         free(for_user->padata_value.data);
269         for_user->padata_value = orig_padata_value;
270
271         free_PA_S4U2Self(&self);
272         krb5_data_free(&cksum_data);
273         free_Checksum(&mod_self.cksum);
274
275         return true;
276 }
277
278 /*
279  * TEST_AS_REQ and TEST_AS_REQ_SELF - SEND
280  *
281  * Confirm that the outgoing packet meets certain expectations.  This
282  * should be extended to further assert the correct and expected
283  * behaviour of the krb5 libs, so we know what we are sending to the
284  * server.
285  *
286  * Additionally, this CHANGES the request to remove the canonicalize
287  * flag automatically added by the krb5 libs when an enterprise
288  * principal is used, so we can test what the server does in this
289  * combination.
290  *
291  */
292
293 static bool torture_krb5_pre_send_as_req_test(struct torture_krb5_context *test_context,
294                                               const krb5_data *send_buf,
295                                               krb5_data *modified_send_buf)
296 {
297         AS_REQ mod_as_req;
298         krb5_error_code k5ret;
299         size_t used;
300         torture_assert_int_equal(test_context->tctx, decode_AS_REQ(send_buf->data, send_buf->length,
301                                                &test_context->as_req, &used),
302                                  0, "decode_AS_REQ for TEST_AS_REQ failed");
303         mod_as_req = test_context->as_req;
304         torture_assert_int_equal(test_context->tctx, used, send_buf->length, "length mismatch");
305         torture_assert_int_equal(test_context->tctx, test_context->as_req.pvno,
306                                  5, "Got wrong as_req->pvno");
307         if (test_context->test_data->canonicalize
308             || test_context->test_data->enterprise) {
309                 torture_assert(test_context->tctx,
310                                test_context->as_req.req_body.kdc_options.canonicalize,
311                                "krb5 libs did not set canonicalize!");
312         } else {
313                 torture_assert_int_equal(test_context->tctx,
314                                          test_context->as_req.req_body.kdc_options.canonicalize,
315                                          false,
316                                          "krb5 libs unexpectedly set canonicalize!");
317         }
318
319         if (test_context->test_data->as_req_spn) {
320                 if (test_context->test_data->upn) {
321                         torture_assert_int_equal(test_context->tctx,
322                                                  test_context->as_req.req_body.cname->name_type,
323                                                  KRB5_NT_PRINCIPAL,
324                                                  "krb5 libs unexpectedly "
325                                                  "did not set principal "
326                                                  "as NT_SRV_HST!");
327                 } else {
328                         torture_assert_int_equal(test_context->tctx,
329                                                  test_context->as_req.req_body.cname->name_type,
330                                                  KRB5_NT_SRV_HST,
331                                                  "krb5 libs unexpectedly "
332                                                  "did not set principal "
333                                                  "as NT_SRV_HST!");
334                 }
335         } else if (test_context->test_data->enterprise) {
336                 torture_assert_int_equal(test_context->tctx,
337                                          test_context->as_req.req_body.cname->name_type,
338                                          KRB5_NT_ENTERPRISE_PRINCIPAL,
339                                          "krb5 libs did not pass principal as enterprise!");
340         } else {
341                 torture_assert_int_equal(test_context->tctx,
342                                          test_context->as_req.req_body.cname->name_type,
343                                          KRB5_NT_PRINCIPAL,
344                                          "krb5 libs unexpectedly set principal as enterprise!");
345         }
346
347         /* Force off canonicalize that was forced on by the krb5 libs */
348         if (test_context->test_data->canonicalize == false && test_context->test_data->enterprise) {
349                 mod_as_req.req_body.kdc_options.canonicalize = false;
350         }
351
352         if (test_context->test_stage == TEST_AS_REQ_SELF) {
353                 /*
354                  * Force the server name to match the client name,
355                  * including the name type.  This isn't possible with
356                  * the krb5 client libs alone
357                  */
358                 mod_as_req.req_body.sname = test_context->as_req.req_body.cname;
359         }
360
361         ASN1_MALLOC_ENCODE(AS_REQ, modified_send_buf->data, modified_send_buf->length,
362                            &mod_as_req, &used, k5ret);
363         torture_assert_int_equal(test_context->tctx,
364                                  k5ret, 0,
365                                  "encode_AS_REQ failed");
366
367         if (test_context->test_stage != TEST_AS_REQ_SELF) {
368                 torture_assert_int_equal(test_context->tctx, used, send_buf->length,
369                                          "re-encode length mismatch");
370         }
371         return true;
372 }
373
374 /*
375  * TEST_AS_REQ - RECV
376  *
377  * Confirm that the reply packet from the KDC meets certain
378  * expectations as part of TEST_AS_REQ.  This uses a packet count to
379  * work out what packet we are up to in the multiple exchanged
380  * triggerd by krb5_get_init_creds_password().
381  *
382  */
383
384 static bool torture_krb5_post_recv_as_req_test(struct torture_krb5_context *test_context,
385                                                const krb5_data *recv_buf)
386 {
387         KRB_ERROR error;
388         size_t used;
389         if (test_context->packet_count == 0) {
390                 krb5_error_code k5ret;
391                 /*
392                  * The client libs obtain the salt by attempting to
393                  * authenticate without pre-authentication and getting
394                  * the correct salt with the
395                  * KRB5KDC_ERR_PREAUTH_REQUIRED error.  If we are in
396                  * the test (netbios_realm && upn) that deliberatly
397                  * has an incorrect principal, we check we get the
398                  * correct error.
399                  */
400                 k5ret = decode_KRB_ERROR(recv_buf->data, recv_buf->length,
401                                          &error, &used);
402                 if (k5ret != 0) {
403                         AS_REP as_rep;
404                         k5ret = decode_AS_REP(recv_buf->data, recv_buf->length,
405                                       &as_rep, &used);
406                         if (k5ret == 0) {
407                                 if (test_context->test_data->netbios_realm && test_context->test_data->upn) {
408                                         torture_assert(test_context->tctx, false,
409                                                        "expected to get a KRB_ERROR packet with "
410                                                        "KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN, got valid AS-REP");
411                                 } else {
412                                         torture_assert(test_context->tctx, false,
413                                                        "expected to get a KRB_ERROR packet with "
414                                                        "KRB5KDC_ERR_PREAUTH_REQUIRED, got valid AS-REP");
415                                 }
416                         } else {
417                                 if (test_context->test_data->netbios_realm && test_context->test_data->upn) {
418                                         torture_assert(test_context->tctx, false,
419                                                        "unable to decode as KRB-ERROR or AS-REP, "
420                                                        "expected to get a KRB_ERROR packet with KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN");
421                                 } else {
422                                         torture_assert(test_context->tctx, false,
423                                                        "unable to decode as KRB-ERROR or AS-REP, "
424                                                        "expected to get a KRB_ERROR packet with KRB5KDC_ERR_PREAUTH_REQUIRED");
425                                 }
426                         }
427                 }
428                 torture_assert_int_equal(test_context->tctx, used, recv_buf->length,
429                                          "length mismatch");
430                 torture_assert_int_equal(test_context->tctx, error.pvno, 5,
431                                          "Got wrong error.pvno");
432                 if (test_context->test_data->netbios_realm && test_context->test_data->upn) {
433                         torture_assert_int_equal(test_context->tctx,
434                                                  error.error_code,
435                                                  KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN - KRB5KDC_ERR_NONE,
436                                                  "Got wrong error.error_code");
437                 } else if (test_context->test_data->as_req_spn && !test_context->test_data->spn_is_upn) {
438                         torture_assert_int_equal(test_context->tctx,
439                                                  error.error_code,
440                                                  KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN - KRB5KDC_ERR_NONE,
441                                                  "Got wrong error.error_code");
442                 } else {
443                         torture_assert_int_equal(test_context->tctx,
444                                                  error.error_code,
445                                                  KRB5KDC_ERR_PREAUTH_REQUIRED - KRB5KDC_ERR_NONE,
446                                                  "Got wrong error.error_code");
447                 }
448
449                 free_KRB_ERROR(&error);
450         } else if ((decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used) == 0)
451                    && (test_context->packet_count == 1)) {
452                 /*
453                  * The Windows 2012R2 KDC will always respond with
454                  * KRB5KRB_ERR_RESPONSE_TOO_BIG over UDP as the ticket
455                  * won't fit, because of the PAC.  (It appears to do
456                  * this always, even if it will).  This triggers the
457                  * client to try again over TCP.
458                  */
459                 torture_assert_int_equal(test_context->tctx,
460                                          used, recv_buf->length,
461                                          "length mismatch");
462                 torture_assert_int_equal(test_context->tctx,
463                                          error.pvno, 5,
464                                          "Got wrong error.pvno");
465                 torture_assert_int_equal(test_context->tctx,
466                                          error.error_code,
467                                          KRB5KRB_ERR_RESPONSE_TOO_BIG - KRB5KDC_ERR_NONE,
468                                          "Got wrong error.error_code");
469                 free_KRB_ERROR(&error);
470         } else {
471                 /*
472                  * Finally the successful packet.
473                  */
474                 torture_assert_int_equal(test_context->tctx,
475                                          decode_AS_REP(recv_buf->data, recv_buf->length,
476                                                        &test_context->as_rep, &used), 0,
477                                          "decode_AS_REP failed");
478                 torture_assert_int_equal(test_context->tctx, used, recv_buf->length,
479                                          "length mismatch");
480                 torture_assert_int_equal(test_context->tctx,
481                                          test_context->as_rep.pvno, 5,
482                                          "Got wrong as_rep->pvno");
483                 torture_assert_int_equal(test_context->tctx,
484                                          test_context->as_rep.ticket.tkt_vno, 5,
485                                          "Got wrong as_rep->ticket.tkt_vno");
486                 torture_assert(test_context->tctx,
487                                test_context->as_rep.ticket.enc_part.kvno,
488                                "Did not get a KVNO in test_context->as_rep.ticket.enc_part.kvno");
489
490                 /*
491                  * We can confirm that the correct proxy behaviour is
492                  * in use on the KDC by checking the KVNO of the
493                  * krbtgt account returned in the reply.
494                  *
495                  * A packet passed to the full RW DC will not have a
496                  * KVNO in the upper bits, while a packet processed
497                  * locally on the RODC will have these bits filled in
498                  * the msDS-SecondaryKrbTgtNumber
499                  */
500                 if (torture_setting_bool(test_context->tctx, "expect_cached_at_rodc", false)) {
501                         torture_assert_int_not_equal(test_context->tctx,
502                                                      *test_context->as_rep.ticket.enc_part.kvno & 0xFFFF0000,
503                                                      0, "Did not get a RODC number in the KVNO");
504                 } else {
505                         torture_assert_int_equal(test_context->tctx,
506                                                  *test_context->as_rep.ticket.enc_part.kvno & 0xFFFF0000,
507                                                  0, "Unexpecedly got a RODC number in the KVNO");
508                 }
509                 free_AS_REP(&test_context->as_rep);
510         }
511         torture_assert(test_context->tctx, test_context->packet_count < 3, "too many packets");
512         free_AS_REQ(&test_context->as_req);
513         return true;
514 }
515
516 /*
517  * TEST_TGS_REQ_KRBTGT_CANON
518  *
519  *
520  * Confirm that the outgoing TGS-REQ packet from krb5_get_creds()
521  * for the krbtgt/realm principal meets certain expectations, like
522  * that the canonicalize bit is not set
523  *
524  */
525
526 static bool torture_krb5_pre_send_tgs_req_krbtgt_canon_test(struct torture_krb5_context *test_context, const krb5_data *send_buf, krb5_data *modified_send_buf)
527 {
528         size_t used;
529         torture_assert_int_equal(test_context->tctx,
530                                  decode_TGS_REQ(send_buf->data, send_buf->length,
531                                                 &test_context->tgs_req, &used),
532                                  0, "decode_TGS_REQ for TEST_TGS_REQ test failed");
533         torture_assert_int_equal(test_context->tctx,
534                                  used, send_buf->length,
535                                  "length mismatch");
536         torture_assert_int_equal(test_context->tctx,
537                                  test_context->tgs_req.pvno, 5,
538                                  "Got wrong as_req->pvno");
539         torture_assert_int_equal(test_context->tctx,
540                                  test_context->tgs_req.req_body.kdc_options.canonicalize,
541                                  true,
542                                  "krb5 libs unexpectedly did not set canonicalize!");
543
544         torture_assert_int_equal(test_context->tctx,
545                                  test_context->tgs_req.req_body.sname->name_type,
546                                  KRB5_NT_PRINCIPAL,
547                                  "Mismatch in name_type between request and expected request");
548
549         torture_assert_str_equal(test_context->tctx,
550                                  test_context->tgs_req.req_body.realm,
551                                  test_context->test_data->real_realm,
552                                  "Mismatch in realm between request and expected request");
553
554         *modified_send_buf = *send_buf;
555         return true;
556 }
557
558 /*
559  * TEST_TGS_REQ_KRBTGT_CANON
560  *
561  * Confirm that the reply TGS-REP packet for krb5_get_creds()
562  * where the client is behaving as if this is a cross-realm trust due
563  * to case or netbios vs dns name differences meets certain
564  * expectations, while canonicalize is set
565  *
566  */
567
568 static bool torture_krb5_post_recv_tgs_req_krbtgt_canon_test(struct torture_krb5_context *test_context, const krb5_data *recv_buf)
569 {
570         size_t used;
571         torture_assert_int_equal(test_context->tctx,
572                                  decode_TGS_REP(recv_buf->data, recv_buf->length,
573                                                 &test_context->tgs_rep, &used),
574                                  0,
575                                  "decode_TGS_REP failed");
576         torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch");
577         torture_assert_int_equal(test_context->tctx,
578                                  test_context->tgs_rep.pvno, 5,
579                                  "Got wrong as_rep->pvno");
580         torture_assert_int_equal(test_context->tctx,
581                                  test_context->tgs_rep.ticket.tkt_vno, 5,
582                                  "Got wrong as_rep->ticket.tkt_vno");
583         torture_assert(test_context->tctx,
584                        test_context->tgs_rep.ticket.enc_part.kvno,
585                        "Did not get a KVNO in test_context->as_rep.ticket.enc_part.kvno");
586         torture_assert_str_equal(test_context->tctx,
587                                  test_context->tgs_req.req_body.realm,
588                                  test_context->tgs_rep.ticket.realm,
589                                  "Mismatch in realm between request and ticket response");
590         torture_assert_str_equal(test_context->tctx,
591                                  test_context->tgs_rep.ticket.realm,
592                                  test_context->test_data->real_realm,
593                                  "Mismatch in realm between ticket response and expected ticket response");
594         torture_assert_int_equal(test_context->tctx,
595                                  test_context->tgs_rep.ticket.sname.name_type,
596                                  KRB5_NT_SRV_INST,
597                                  "Mismatch in name_type between ticket response and expected value of KRB5_NT_SRV_INST");
598
599         torture_assert_int_equal(test_context->tctx,
600                                  test_context->tgs_rep.ticket.sname.name_string.len,
601                                  2,
602                                  "Mismatch in name_type between ticket response and expected value, expected krbtgt/REALM@REALM");
603
604         torture_assert_str_equal(test_context->tctx,
605                                  test_context->tgs_rep.ticket.sname.name_string.val[0], "krbtgt",
606                                  "Mismatch in name between response and expected response, expected krbtgt");
607         torture_assert_str_equal(test_context->tctx,
608                                  test_context->tgs_rep.ticket.sname.name_string.val[1], test_context->test_data->real_realm,
609                                  "Mismatch in realm part of krbtgt/ in expected response, expected krbtgt/REALM@REALM");
610
611         /*
612          * We can confirm that the correct proxy behaviour is
613          * in use on the KDC by checking the KVNO of the
614          * krbtgt account returned in the reply.
615          *
616          * A packet passed to the full RW DC will not have a
617          * KVNO in the upper bits, while a packet processed
618          * locally on the RODC will have these bits filled in
619          * the msDS-SecondaryKrbTgtNumber
620          */
621         if (torture_setting_bool(test_context->tctx, "expect_cached_at_rodc", false)) {
622                 torture_assert_int_not_equal(test_context->tctx,
623                                              *test_context->tgs_rep.ticket.enc_part.kvno & 0xFFFF0000,
624                                              0, "Did not get a RODC number in the KVNO");
625         } else {
626                 torture_assert_int_equal(test_context->tctx,
627                                          *test_context->tgs_rep.ticket.enc_part.kvno & 0xFFFF0000,
628                                          0, "Unexpecedly got a RODC number in the KVNO");
629         }
630         free_TGS_REP(&test_context->tgs_rep);
631         torture_assert(test_context->tctx,
632                        test_context->packet_count < 2,
633                        "too many packets");
634         free_TGS_REQ(&test_context->tgs_req);
635         return true;
636 }
637
638 /*
639  * TEST_TGS_REQ_CANON
640  *
641  * Confirm that the outgoing TGS-REQ packet from krb5_get_creds
642  * certain expectations, like that the canonicalize bit is set (this
643  * test is to force that handling) and that if an enterprise name was
644  * requested, that it was sent.
645  *
646  */
647
648 static bool torture_krb5_pre_send_tgs_req_canon_test(struct torture_krb5_context *test_context,
649                                                      const krb5_data *send_buf,
650                                                      krb5_data *modified_send_buf)
651 {
652         size_t used;
653         torture_assert_int_equal(test_context->tctx,
654                                  decode_TGS_REQ(send_buf->data, send_buf->length,
655                                                 &test_context->tgs_req, &used),
656                                  0, "decode_TGS_REQ for TEST_TGS_REQ_CANON test failed");
657         torture_assert_int_equal(test_context->tctx, used, send_buf->length, "length mismatch");
658         torture_assert_int_equal(test_context->tctx, test_context->tgs_req.pvno, 5, "Got wrong as_req->pvno");
659         torture_assert_int_equal(test_context->tctx,
660                                  test_context->tgs_req.req_body.kdc_options.canonicalize,
661                                  true, "krb5 libs unexpectedly did not set canonicalize!");
662
663         if (test_context->test_data->enterprise) {
664                 torture_assert_int_equal(test_context->tctx,
665                                          test_context->tgs_req.req_body.sname->name_type, KRB5_NT_ENTERPRISE_PRINCIPAL,
666                                  "Mismatch in name type between request and expected request, expected  KRB5_NT_ENTERPRISE_PRINCIPAL");
667                 torture_assert_str_equal(test_context->tctx,
668                                          test_context->tgs_req.req_body.realm, test_context->test_data->real_realm,
669                                  "Mismatch in realm between request and expected request");
670
671         } else if (test_context->test_data->as_req_spn) {
672                 torture_assert_int_equal(test_context->tctx,
673                                          test_context->tgs_req.req_body.sname->name_type, KRB5_NT_SRV_HST,
674                                          "Mismatch in name type between request and expected request, expected  KRB5_NT_SRV_HST");
675                 torture_assert_str_equal(test_context->tctx,
676                                          test_context->tgs_req.req_body.realm, test_context->test_data->real_realm,
677                                  "Mismatch in realm between request and expected request");
678
679         } else if (test_context->test_data->canonicalize) {
680                 torture_assert_int_equal(test_context->tctx,
681                                          test_context->tgs_req.req_body.sname->name_type, KRB5_NT_PRINCIPAL,
682                                          "Mismatch in name type between request and expected request, expected  KRB5_NT_PRINCIPAL");
683                 torture_assert_str_equal(test_context->tctx,
684                                          test_context->tgs_req.req_body.realm, test_context->test_data->real_realm,
685                                  "Mismatch in realm between request and expected request");
686
687         } else {
688                 torture_assert_int_equal(test_context->tctx,
689                                          test_context->tgs_req.req_body.sname->name_type, KRB5_NT_PRINCIPAL,
690                                          "Mismatch in name type between request and expected request, expected  KRB5_NT_PRINCIPAL");
691                 torture_assert_str_equal(test_context->tctx,
692                                          test_context->tgs_req.req_body.realm, test_context->test_data->realm,
693                                  "Mismatch in realm between request and expected request");
694
695         }
696
697         if (test_context->test_data->mitm_s4u2self) {
698                 torture_assert(test_context->tctx, change_for_user_principal(test_context, modified_send_buf),
699                                "Failed to modify PA_FOR_USER principal name");
700         } else {
701                 *modified_send_buf = *send_buf;
702         }
703
704         return true;
705 }
706
707 /*
708  * TEST_TGS_REQ_CANON - RECV
709  *
710  * Confirm that the reply TGS-REP or error packet from the KDC meets
711  * certain expectations as part of TEST_TGS_REQ_CANON.
712  *
713  * This is triggered by krb5_get_creds()
714  *
715  */
716
717 static bool torture_krb5_post_recv_tgs_req_canon_test(struct torture_krb5_context *test_context, const krb5_data *recv_buf)
718 {
719         KRB_ERROR error;
720         size_t used;
721         krb5_error_code expected_error;
722
723         /*
724          * If this account did not have a servicePrincipalName, then
725          * we expect a errro packet, not a TGS-REQ
726          */
727         if (decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used) == 0) {
728                 torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch");
729                 torture_assert_int_equal(test_context->tctx,
730                                          error.pvno, 5,
731                                          "Got wrong error.pvno");
732                 expected_error = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN - KRB5KDC_ERR_NONE;
733                 if (error.error_code != expected_error && test_context->test_data->mitm_s4u2self) {
734                         expected_error = KRB5KRB_AP_ERR_INAPP_CKSUM - KRB5KDC_ERR_NONE;
735                 }
736                 torture_assert_int_equal(test_context->tctx,
737                                          error.error_code,
738                                          expected_error,
739                                          "Got wrong error.error_code");
740         } else {
741                 torture_assert_int_equal(test_context->tctx,
742                                          decode_TGS_REP(recv_buf->data, recv_buf->length,
743                                                         &test_context->tgs_rep,
744                                                         &used),
745                                          0,
746                                          "decode_TGS_REP failed");
747                 torture_assert_int_equal(test_context->tctx,
748                                          used, recv_buf->length,
749                                          "length mismatch");
750                 torture_assert_int_equal(test_context->tctx,
751                                          test_context->tgs_rep.pvno, 5,
752                                          "Got wrong as_rep->pvno");
753                 torture_assert_int_equal(test_context->tctx,
754                                          test_context->tgs_rep.ticket.tkt_vno, 5,
755                                          "Got wrong as_rep->ticket.tkt_vno");
756                 torture_assert(test_context->tctx,
757                                test_context->tgs_rep.ticket.enc_part.kvno,
758                                "Did not get a KVNO in test_context->as_rep.ticket.enc_part.kvno");
759                 torture_assert_str_equal(test_context->tctx,
760                                          test_context->tgs_rep.ticket.realm,
761                                          test_context->test_data->real_realm,
762                                          "Mismatch in realm between ticket response and expected upper case REALM");
763                 torture_assert_int_equal(test_context->tctx,
764                                          test_context->tgs_rep.ticket.sname.name_type,
765                                          test_context->tgs_req.req_body.sname->name_type,
766                                          "Mismatch in name_type between request and ticket response");
767                 torture_assert_int_equal(test_context->tctx,
768                                          test_context->tgs_rep.ticket.sname.name_string.len,
769                                          test_context->tgs_req.req_body.sname->name_string.len,
770                                          "Mismatch in name_string.len between request and ticket response");
771                 torture_assert(test_context->tctx,
772                                test_context->tgs_rep.ticket.sname.name_string.len >= 1,
773                                "name_string.len should be >=1 in ticket response");
774                 torture_assert_str_equal(test_context->tctx,
775                                          test_context->tgs_rep.ticket.sname.name_string.val[0],
776                                          test_context->tgs_req.req_body.sname->name_string.val[0],
777                                          "Mismatch in name between request and expected request");
778                 torture_assert_int_equal(test_context->tctx,
779                                          *test_context->tgs_rep.ticket.enc_part.kvno & 0xFFFF0000,
780                                          0, "Unexpecedly got a RODC number in the KVNO, should just be principal KVNO");
781                 torture_assert(test_context->tctx, test_context->test_data->mitm_s4u2self == false,
782                                "KDC accepted PA_S4U2Self with unkeyed checksum!");
783                 free_TGS_REP(&test_context->tgs_rep);
784         }
785         torture_assert(test_context->tctx, test_context->packet_count == 0, "too many packets");
786         free_TGS_REQ(&test_context->tgs_req);
787
788         return true;
789 }
790
791 /*
792  * TEST_SELF_TRUST_TGS_REQ
793  *
794  * Confirm that the outgoing TGS-REQ packet from krb5_mk_req_exact()
795  * certain expectations, like that the canonicalize bit is set (this
796  * test is to force that handling).
797  *
798  * This test is for the case where the name we ask for, while a valid
799  * alternate name for our own realm is used.  The client acts as if
800  * this is cross-realm trust.
801  *
802  */
803
804 static bool torture_krb5_pre_send_self_trust_tgs_req_test(struct torture_krb5_context *test_context,
805                                                           const krb5_data *send_buf,
806                                                           krb5_data *modified_send_buf)
807 {
808         size_t used;
809         torture_assert_int_equal(test_context->tctx,
810                                  decode_TGS_REQ(send_buf->data, send_buf->length,
811                                                 &test_context->tgs_req, &used),
812                                  0, "decode_TGS_REQ for TEST_SELF_TRUST_TGS_REQ test failed");
813         torture_assert_int_equal(test_context->tctx, used, send_buf->length, "length mismatch");
814         torture_assert_int_equal(test_context->tctx, test_context->tgs_req.pvno, 5, "Got wrong as_req->pvno");
815
816         if (test_context->test_data->enterprise
817             || (test_context->test_data->spn_is_upn && test_context->test_data->upn)) {
818                 torture_assert_int_equal(test_context->tctx,
819                                          test_context->tgs_req.req_body.kdc_options.canonicalize,
820                                          true,
821                                          "krb5 libs unexpectedly"
822                                          " did not set canonicalize!");
823         } else {
824                 torture_assert_int_equal(test_context->tctx,
825                                          test_context->tgs_req.req_body.kdc_options.canonicalize,
826                                          false,
827                                          "krb5 libs unexpectedly"
828                                          " set canonicalize!");
829         }
830         
831
832         if (test_context->test_data->canonicalize) {
833                 torture_assert_str_equal(test_context->tctx,
834                                          test_context->tgs_req.req_body.realm,
835                                          test_context->test_data->real_realm,
836                                          "Mismatch in realm between request and expected request");
837         } else {
838                 torture_assert_str_equal(test_context->tctx,
839                                          test_context->tgs_req.req_body.realm,
840                                          test_context->test_data->realm,
841                                          "Mismatch in realm between request and expected request");
842         }
843         torture_assert_int_equal(test_context->tctx,
844                                  test_context->tgs_req.req_body.sname->name_type, KRB5_NT_PRINCIPAL,
845                                  "Mismatch in name type between request and expected request, expected  KRB5_NT_PRINCIPAL");
846         torture_assert_int_equal(test_context->tctx,
847                                  test_context->tgs_req.req_body.sname->name_string.len, 2,
848                                  "Mismatch in name between request and expected request, expected krbtgt/realm");
849         torture_assert_str_equal(test_context->tctx,
850                                  test_context->tgs_req.req_body.sname->name_string.val[0], "krbtgt",
851                                  "Mismatch in name between request and expected request, expected krbtgt");
852         torture_assert_str_equal(test_context->tctx,
853                                  test_context->tgs_req.req_body.sname->name_string.val[1], test_context->test_data->realm,
854                                  "Mismatch in realm part of cross-realm request principal between request and expected request");
855         *modified_send_buf = *send_buf;
856
857         return true;
858 }
859
860 /*
861  * TEST_SELF_TRUST_TGS_REQ and TEST_TGS_REQ_KRBTGT - RECV
862  *
863  * Confirm that the reply TGS-REP packet for krb5_mk_req_exact(),
864  * where the client is behaving as if this is a cross-realm trust due
865  * to case or netbios vs dns name differences meets certain
866  * expectations.
867  *
868  */
869
870 static bool torture_krb5_post_recv_self_trust_tgs_req_test(struct torture_krb5_context *test_context, const krb5_data *recv_buf)
871 {
872         size_t used;
873         torture_assert_int_equal(test_context->tctx,
874                                  decode_TGS_REP(recv_buf->data, recv_buf->length,
875                                                 &test_context->tgs_rep, &used),
876                                  0,
877                                  "decode_TGS_REP failed");
878         torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch");
879         torture_assert_int_equal(test_context->tctx,
880                                  test_context->tgs_rep.pvno, 5,
881                                  "Got wrong as_rep->pvno");
882         torture_assert_int_equal(test_context->tctx,
883                                  test_context->tgs_rep.ticket.tkt_vno, 5,
884                                  "Got wrong as_rep->ticket.tkt_vno");
885         torture_assert(test_context->tctx,
886                        test_context->tgs_rep.ticket.enc_part.kvno,
887                        "Did not get a KVNO in test_context->as_rep.ticket.enc_part.kvno");
888         torture_assert_str_equal(test_context->tctx,
889                                  test_context->tgs_req.req_body.realm,
890                                  test_context->tgs_rep.ticket.realm,
891                                  "Mismatch in realm between request and ticket response");
892         torture_assert_int_equal(test_context->tctx,
893                                  test_context->tgs_rep.ticket.sname.name_type,
894                                  test_context->tgs_req.req_body.sname->name_type,
895                                  "Mismatch in name_type between request and ticket response");
896
897         torture_assert_int_equal(test_context->tctx,
898                                  test_context->tgs_rep.ticket.sname.name_string.len, 2,
899                                  "Mismatch in name between request and expected request, expected krbtgt/realm");
900         torture_assert_str_equal(test_context->tctx,
901                                  test_context->tgs_rep.ticket.sname.name_string.val[0], "krbtgt",
902                                  "Mismatch in name between request and expected request, expected krbtgt");
903         torture_assert_str_equal(test_context->tctx,
904                                  test_context->tgs_rep.ticket.sname.name_string.val[1], test_context->test_data->realm,
905                                  "Mismatch in realm part of cross-realm request principal between response and expected request");
906         /*
907          * We can confirm that the correct proxy behaviour is
908          * in use on the KDC by checking the KVNO of the
909          * krbtgt account returned in the reply.
910          *
911          * A packet passed to the full RW DC will not have a
912          * KVNO in the upper bits, while a packet processed
913          * locally on the RODC will have these bits filled in
914          * the msDS-SecondaryKrbTgtNumber
915          */
916         if (torture_setting_bool(test_context->tctx, "expect_cached_at_rodc", false)) {
917                 torture_assert_int_not_equal(test_context->tctx,
918                                              *test_context->tgs_rep.ticket.enc_part.kvno & 0xFFFF0000,
919                                              0, "Did not get a RODC number in the KVNO");
920         } else {
921                 torture_assert_int_equal(test_context->tctx,
922                                          *test_context->tgs_rep.ticket.enc_part.kvno & 0xFFFF0000,
923                                          0, "Unexpecedly got a RODC number in the KVNO");
924         }
925         free_TGS_REP(&test_context->tgs_rep);
926         torture_assert_int_equal(test_context->tctx,
927                                  test_context->packet_count, 0,
928                                  "too many packets");
929         test_context->packet_count = 0;
930         test_context->test_stage = TEST_TGS_REQ;
931         free_TGS_REQ(&test_context->tgs_req);
932         return true;
933 }
934
935 /*
936  * TEST_TGS_REQ
937  *
938  * Confirm that the outgoing TGS-REQ packet from krb5_mk_req_exact()
939  * certain expectations, like that the canonicalize bit is set (this
940  * test is to force that handling) and that if an enterprise name was
941  * requested, that it was sent.
942  *
943  */
944
945 static bool torture_krb5_pre_send_tgs_req_test(struct torture_krb5_context *test_context, const krb5_data *send_buf, krb5_data *modified_send_buf)
946 {
947         size_t used;
948         torture_assert_int_equal(test_context->tctx,
949                                  decode_TGS_REQ(send_buf->data, send_buf->length,
950                                                 &test_context->tgs_req, &used),
951                                  0, "decode_TGS_REQ for TEST_TGS_REQ test failed");
952         torture_assert_int_equal(test_context->tctx, used, send_buf->length, "length mismatch");
953         torture_assert_int_equal(test_context->tctx, test_context->tgs_req.pvno, 5,
954                                  "Got wrong as_req->pvno");
955
956         if (test_context->test_data->enterprise
957             && test_context->test_data->s4u2self == false
958             && test_context->test_data->spn_is_upn) {
959                 torture_assert_int_equal(test_context->tctx,
960                                          test_context->tgs_req.req_body.kdc_options.canonicalize,
961                                          true,
962                                          "krb5 libs unexpectedly"
963                                          " did not set canonicalize!");
964         } else {
965                 torture_assert_int_equal(test_context->tctx,
966                                          test_context->tgs_req.req_body.kdc_options.canonicalize,
967                                          false,
968                                          "krb5 libs unexpectedly"
969                                          " set canonicalize!");
970         }
971         
972         if (test_context->test_data->enterprise) {
973                 torture_assert_int_equal(test_context->tctx,
974                                          test_context->tgs_req.req_body.sname->name_type,
975                                          KRB5_NT_ENTERPRISE_PRINCIPAL,
976                                          "Mismatch in name type between request and expected request, expected  KRB5_NT_ENTERPRISE_PRINCIPAL");
977                 torture_assert_str_equal(test_context->tctx,
978                                          test_context->tgs_req.req_body.realm,
979                                          test_context->test_data->real_realm,
980                                          "Mismatch in realm between request and expected request");
981
982         } else if (test_context->test_data->spn_is_upn && test_context->test_data->upn && test_context->test_data->canonicalize) {
983                 torture_assert_int_equal(test_context->tctx,
984                                          test_context->tgs_req.req_body.sname->name_type,
985                                          KRB5_NT_PRINCIPAL,
986                                          "Mismatch in name type between request and expected request, expected  KRB5_NT_PRINCIPAL");
987                 torture_assert_str_equal(test_context->tctx,
988                                          test_context->tgs_req.req_body.realm,
989                                          test_context->test_data->real_realm,
990                                          "Mismatch in realm between request and expected request");
991
992         } else if (test_context->test_data->spn_is_upn
993                    && test_context->test_data->as_req_spn
994                    && test_context->test_data->canonicalize == false) {
995                 torture_assert_int_equal(test_context->tctx,
996                                          test_context->tgs_req.req_body.sname->name_type,
997                                          KRB5_NT_SRV_HST,
998                                          "Mismatch in name type between request and expected request, expected  KRB5_NT_SRV_HST");
999                 torture_assert_str_equal(test_context->tctx,
1000                                          test_context->tgs_req.req_body.realm,
1001                                          test_context->test_data->realm,
1002                                          "Mismatch in realm between request and expected request");
1003
1004         } else {
1005                 torture_assert_int_equal(test_context->tctx,
1006                                          test_context->tgs_req.req_body.sname->name_type,
1007                                          KRB5_NT_PRINCIPAL,
1008                                          "Mismatch in name type between request and expected request, expected  KRB5_NT_PRINCIPAL");
1009                 torture_assert_str_equal(test_context->tctx,
1010                                          test_context->tgs_req.req_body.realm,
1011                                          test_context->test_data->realm,
1012                                          "Mismatch in realm between request and expected request");
1013
1014         }
1015
1016         *modified_send_buf = *send_buf;
1017
1018         return true;
1019 }
1020
1021 /*
1022  * TEST_TGS_REQ - RECV
1023  *
1024  * Confirm that the reply TGS-REP packet for krb5_mk_req_exact(), for
1025  * the actual target service.
1026  *
1027  */
1028
1029 static bool torture_krb5_post_recv_tgs_req_test(struct torture_krb5_context *test_context, const krb5_data *recv_buf)
1030 {
1031         KRB_ERROR error;
1032         size_t used;
1033         /*
1034          * If this account did not have a servicePrincipalName, then
1035          * we expect a errro packet, not a TGS-REQ
1036          */
1037         if (decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used) == 0) {
1038                 torture_assert_int_equal(test_context->tctx,
1039                                          used, recv_buf->length,
1040                                          "length mismatch");
1041                 torture_assert_int_equal(test_context->tctx,
1042                                          error.pvno, 5,
1043                                          "Got wrong error.pvno");
1044                 torture_assert_int_equal(test_context->tctx,
1045                                          error.error_code,
1046                                          KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN - KRB5KDC_ERR_NONE,
1047                                          "Got wrong error.error_code");
1048         } else {
1049                 torture_assert_int_equal(test_context->tctx,
1050                                          decode_TGS_REP(recv_buf->data, recv_buf->length,
1051                                                         &test_context->tgs_rep, &used),
1052                                          0,
1053                                          "decode_TGS_REP failed");
1054                 torture_assert_int_equal(test_context->tctx, used, recv_buf->length,
1055                                          "length mismatch");
1056                 torture_assert_int_equal(test_context->tctx,
1057                                          test_context->tgs_rep.pvno, 5,
1058                                          "Got wrong as_rep->pvno");
1059                 torture_assert_int_equal(test_context->tctx,
1060                                          test_context->tgs_rep.ticket.tkt_vno, 5,
1061                                          "Got wrong as_rep->ticket.tkt_vno");
1062                 torture_assert(test_context->tctx,
1063                                test_context->tgs_rep.ticket.enc_part.kvno,
1064                                "Did not get a KVNO in test_context->as_rep.ticket.enc_part.kvno");
1065                 torture_assert_str_equal(test_context->tctx,
1066                                          test_context->tgs_rep.ticket.realm,
1067                                          test_context->test_data->real_realm,
1068                                          "Mismatch in realm between ticket response and expected upper case REALM");
1069                 torture_assert_int_equal(test_context->tctx,
1070                                          test_context->tgs_req.req_body.sname->name_type,
1071                                          test_context->tgs_rep.ticket.sname.name_type, "Mismatch in name_type between request and ticket response");
1072                 torture_assert_int_equal(test_context->tctx,
1073                                          *test_context->tgs_rep.ticket.enc_part.kvno & 0xFFFF0000,
1074                                          0, "Unexpecedly got a RODC number in the KVNO, should just be principal KVNO");
1075                 free_TGS_REP(&test_context->tgs_rep);
1076         }
1077         torture_assert(test_context->tctx, test_context->packet_count < 3, "too many packets");
1078         free_TGS_REQ(&test_context->tgs_req);
1079         test_context->test_stage = TEST_DONE;
1080         return true;
1081 }
1082
1083 /*
1084  * TEST_TGS_REQ_KRBTGT
1085  *
1086  *
1087  * Confirm that the outgoing TGS-REQ packet from krb5_mk_req_exact()
1088  * for the krbtgt/realm principal meets certain expectations, like
1089  * that the canonicalize bit is not set
1090  *
1091  */
1092
1093 static bool torture_krb5_pre_send_tgs_req_krbtgt_test(struct torture_krb5_context *test_context, const krb5_data *send_buf, krb5_data *modified_send_buf)
1094 {
1095         size_t used;
1096         torture_assert_int_equal(test_context->tctx,
1097                                  decode_TGS_REQ(send_buf->data, send_buf->length,
1098                                                 &test_context->tgs_req, &used),
1099                                  0, "decode_TGS_REQ for TEST_TGS_REQ test failed");
1100         torture_assert_int_equal(test_context->tctx,
1101                                  used, send_buf->length,
1102                                  "length mismatch");
1103         torture_assert_int_equal(test_context->tctx,
1104                                  test_context->tgs_req.pvno, 5,
1105                                  "Got wrong as_req->pvno");
1106         torture_assert_int_equal(test_context->tctx,
1107                                  test_context->tgs_req.req_body.kdc_options.canonicalize,
1108                                  false,
1109                                  "krb5 libs unexpectedly set canonicalize!");
1110
1111         torture_assert_str_equal(test_context->tctx,
1112                                  test_context->tgs_req.req_body.realm,
1113                                  test_context->test_data->realm,
1114                                  "Mismatch in realm between request and expected request");
1115
1116         *modified_send_buf = *send_buf;
1117         test_context->test_stage = TEST_DONE;
1118         return true;
1119 }
1120
1121 /*
1122  * TEST_TGS_REQ_HOST, TEST_TGS_REQ_HOST_SRV_INST and TEST_TGS_REQ_HOST_SRV_HST
1123  *
1124  *
1125  * Confirm that the outgoing TGS-REQ packet from krb5_mk_req_exact()
1126  * for the krbtgt/realm principal meets certain expectations, like
1127  * that the canonicalize bit is not set
1128  *
1129  */
1130
1131 static bool torture_krb5_pre_send_tgs_req_host_test(struct torture_krb5_context *test_context, const krb5_data *send_buf, krb5_data *modified_send_buf)
1132 {
1133         size_t used;
1134         torture_assert_int_equal(test_context->tctx,
1135                                  decode_TGS_REQ(send_buf->data, send_buf->length,
1136                                                 &test_context->tgs_req, &used),
1137                                  0, "decode_TGS_REQ for TEST_TGS_REQ test failed");
1138         torture_assert_int_equal(test_context->tctx,
1139                                  used, send_buf->length,
1140                                  "length mismatch");
1141         torture_assert_int_equal(test_context->tctx,
1142                                  test_context->tgs_req.pvno, 5,
1143                                  "Got wrong as_req->pvno");
1144         torture_assert_int_equal(test_context->tctx,
1145                                  test_context->tgs_req.req_body.sname->name_string.len, 2,
1146                                  "Mismatch in name between request and expected request, expected krbtgt/realm");
1147                 torture_assert_int_equal(test_context->tctx,
1148                                          test_context->tgs_req.req_body.kdc_options.canonicalize,
1149                                          true,
1150                                          "krb5 libs unexpectedly did not set canonicalize!");
1151
1152         if (test_context->test_stage == TEST_TGS_REQ_HOST_SRV_INST) {
1153                 torture_assert_int_equal(test_context->tctx,
1154                                          test_context->tgs_req.req_body.sname->name_type, KRB5_NT_SRV_INST,
1155                                          "Mismatch in name type between request and expected request, expected KRB5_NT_SRV_INST");
1156                 torture_assert_str_equal(test_context->tctx,
1157                                          test_context->tgs_req.req_body.sname->name_string.val[0],
1158                                          strupper_talloc(test_context, test_context->test_data->krb5_service),
1159                                          "Mismatch in name between request and expected request, expected service");
1160                 torture_assert_str_equal(test_context->tctx,
1161                                          test_context->tgs_req.req_body.sname->name_string.val[1],
1162                                          test_context->test_data->krb5_hostname,
1163                                          "Mismatch in hostname part between request and expected request");
1164
1165         } else if (test_context->test_stage == TEST_TGS_REQ_HOST_SRV_HST) {
1166
1167                 torture_assert_int_equal(test_context->tctx,
1168                                          test_context->tgs_req.req_body.sname->name_type, KRB5_NT_SRV_HST,
1169                                          "Mismatch in name type between request and expected request, expected KRB5_NT_SRV_HST");
1170                 torture_assert_str_equal(test_context->tctx,
1171                                          test_context->tgs_req.req_body.sname->name_string.val[0],
1172                                          test_context->test_data->krb5_service,
1173                                          "Mismatch in name between request and expected request, expected service");
1174                 torture_assert_str_equal(test_context->tctx,
1175                                          test_context->tgs_req.req_body.sname->name_string.val[1],
1176                                          strupper_talloc(test_context, test_context->test_data->krb5_hostname),
1177                                          "Mismatch in hostname part between request and expected request");
1178
1179         } else {
1180                 torture_assert_int_equal(test_context->tctx,
1181                                          test_context->tgs_req.req_body.sname->name_type, KRB5_NT_PRINCIPAL,
1182                                          "Mismatch in name type between request and expected request, expected  KRB5_NT_PRINCIPAL");
1183                 torture_assert_str_equal(test_context->tctx,
1184                                          test_context->tgs_req.req_body.sname->name_string.val[0],
1185                                          test_context->test_data->krb5_service,
1186                                          "Mismatch in name between request and expected request, expected service");
1187                 torture_assert_str_equal(test_context->tctx,
1188                                          test_context->tgs_req.req_body.sname->name_string.val[1],
1189                                          test_context->test_data->krb5_hostname,
1190                                          "Mismatch in hostname part between request and expected request");
1191
1192         }
1193         torture_assert_str_equal(test_context->tctx,
1194                                  test_context->tgs_req.req_body.realm,
1195                                  test_context->test_data->real_realm,
1196                                  "Mismatch in realm between request and expected request");
1197
1198         *modified_send_buf = *send_buf;
1199         return true;
1200 }
1201
1202 /*
1203  * TEST_TGS_REQ_HOST, TEST_TGS_REQ_HOST_SRV_INST, TEST_TGS_REQ_HOST_SRV_HST - RECV
1204  *
1205  * Confirm that the reply TGS-REP packet for krb5_mk_req(), for
1206  * the actual target service, as a SPN, not a any other name type.
1207  *
1208  */
1209
1210 static bool torture_krb5_post_recv_tgs_req_host_test(struct torture_krb5_context *test_context, const krb5_data *recv_buf)
1211 {
1212         size_t used;
1213         torture_assert_int_equal(test_context->tctx,
1214                                  decode_TGS_REP(recv_buf->data, recv_buf->length,
1215                                                 &test_context->tgs_rep, &used),
1216                                  0,
1217                                  "decode_TGS_REP failed");
1218         torture_assert_int_equal(test_context->tctx, used, recv_buf->length,
1219                                  "length mismatch");
1220         torture_assert_int_equal(test_context->tctx,
1221                                  test_context->tgs_rep.pvno, 5,
1222                                  "Got wrong as_rep->pvno");
1223         torture_assert_int_equal(test_context->tctx,
1224                                  test_context->tgs_rep.ticket.tkt_vno, 5,
1225                                  "Got wrong as_rep->ticket.tkt_vno");
1226         torture_assert(test_context->tctx,
1227                        test_context->tgs_rep.ticket.enc_part.kvno,
1228                        "Did not get a KVNO in test_context->as_rep.ticket.enc_part.kvno");
1229         torture_assert_str_equal(test_context->tctx,
1230                                  test_context->tgs_rep.ticket.realm,
1231                                  test_context->test_data->real_realm,
1232                                  "Mismatch in realm between ticket response and expected upper case REALM");
1233         torture_assert_int_equal(test_context->tctx,
1234                                  test_context->tgs_req.req_body.sname->name_type,
1235                                  test_context->tgs_rep.ticket.sname.name_type, "Mismatch in name_type between request and ticket response");
1236         torture_assert_int_equal(test_context->tctx,
1237                                  test_context->tgs_rep.ticket.sname.name_string.len, 2,
1238                                  "Mismatch in name between request and expected request, expected service/hostname");
1239         torture_assert_str_equal(test_context->tctx,
1240                                  test_context->tgs_rep.ticket.sname.name_string.val[0],
1241                                  test_context->tgs_req.req_body.sname->name_string.val[0],
1242                                  "Mismatch in name between request and expected request, expected service/hostname");
1243         torture_assert_str_equal(test_context->tctx,
1244                                  test_context->tgs_rep.ticket.sname.name_string.val[1],
1245                                  test_context->tgs_req.req_body.sname->name_string.val[1],
1246                                  "Mismatch in name between request and expected request, expected service/hostname");
1247
1248         torture_assert_int_equal(test_context->tctx,
1249                                  *test_context->tgs_rep.ticket.enc_part.kvno & 0xFFFF0000,
1250                                  0, "Unexpecedly got a RODC number in the KVNO, should just be principal KVNO");
1251         free_TGS_REP(&test_context->tgs_rep);
1252
1253         torture_assert(test_context->tctx, test_context->packet_count < 2, "too many packets");
1254         return true;
1255 }
1256
1257 /*
1258  * TEST_AS_REQ_SELF - RECV
1259  *
1260  * Confirm that the reply packet from the KDC meets certain
1261  * expectations as part of TEST_AS_REQ.  This uses a packet count to
1262  * work out what packet we are up to in the multiple exchanged
1263  * triggerd by krb5_get_init_creds_password().
1264  *
1265  */
1266
1267 static bool torture_krb5_post_recv_as_req_self_test(struct torture_krb5_context *test_context,
1268                                                     const krb5_data *recv_buf)
1269 {
1270         KRB_ERROR error;
1271         size_t used;
1272         if (test_context->packet_count == 0) {
1273                 krb5_error_code k5ret;
1274                 /*
1275                  * The client libs obtain the salt by attempting to
1276                  * authenticate without pre-authentication and getting
1277                  * the correct salt with the
1278                  * KRB5KDC_ERR_PREAUTH_REQUIRED error.  If we are in
1279                  * the test (netbios_realm && upn) that deliberatly
1280                  * has an incorrect principal, we check we get the
1281                  * correct error.
1282                  */
1283                 k5ret = decode_KRB_ERROR(recv_buf->data, recv_buf->length,
1284                                          &error, &used);
1285                 if (k5ret != 0) {
1286                         AS_REP as_rep;
1287                         k5ret = decode_AS_REP(recv_buf->data, recv_buf->length,
1288                                       &as_rep, &used);
1289                         if (k5ret == 0) {
1290                                 if (torture_setting_bool(test_context->tctx, "expect_machine_account", false) == false
1291                                     || (test_context->test_data->upn == true)) {
1292                                         torture_assert(test_context->tctx, false,
1293                                                        "expected to get a KRB_ERROR packet with "
1294                                                        "KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN or KRB5KDC_ERR_PREAUTH_REQUIRED, got valid AS-REP");
1295                                 } else {
1296                                         torture_assert(test_context->tctx, false,
1297                                                        "expected to get a KRB_ERROR packet with "
1298                                                        "KRB5KDC_ERR_PREAUTH_REQUIRED, got valid AS-REP");
1299                                 }
1300                         } else {
1301                                 if (torture_setting_bool(test_context->tctx, "expect_machine_account", false) == false
1302                                     || (test_context->test_data->upn == true)) {
1303                                         torture_assert(test_context->tctx, false,
1304                                                        "unable to decode as KRB-ERROR or AS-REP, "
1305                                                        "expected to get a KRB_ERROR packet with KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN or KRB5KDC_ERR_PREAUTH_REQUIRED");
1306                                 } else {
1307                                         torture_assert(test_context->tctx, false,
1308                                                        "unable to decode as KRB-ERROR or AS-REP, "
1309                                                        "expected to get a KRB_ERROR packet with KRB5KDC_ERR_PREAUTH_REQUIRED");
1310                                 }
1311                         }
1312                 }
1313                 torture_assert_int_equal(test_context->tctx, used, recv_buf->length,
1314                                          "length mismatch");
1315                 torture_assert_int_equal(test_context->tctx, error.pvno, 5,
1316                                          "Got wrong error.pvno");
1317                 if ((torture_setting_bool(test_context->tctx, "expect_machine_account", false) == false
1318                      || (test_context->test_data->upn == true))
1319                     && error.error_code == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN - KRB5KDC_ERR_NONE) {
1320                         /*
1321                          * IGNORE
1322                          *
1323                          * This case is because Samba's Heimdal KDC
1324                          * checks server and client accounts before
1325                          * checking for pre-authentication.
1326                          */
1327                 } else {
1328                         torture_assert_int_equal(test_context->tctx,
1329                                                  error.error_code,
1330                                                  KRB5KDC_ERR_PREAUTH_REQUIRED - KRB5KDC_ERR_NONE,
1331                                                  "Got wrong error.error_code");
1332                 }
1333
1334                 free_KRB_ERROR(&error);
1335         } else if ((decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used) == 0)
1336                    && (test_context->packet_count == 1)) {
1337                 /*
1338                  * The Windows 2012R2 KDC will always respond with
1339                  * KRB5KRB_ERR_RESPONSE_TOO_BIG over UDP as the ticket
1340                  * won't fit, because of the PAC.  (It appears to do
1341                  * this always, even if it will).  This triggers the
1342                  * client to try again over TCP.
1343                  */
1344                 torture_assert_int_equal(test_context->tctx,
1345                                          used, recv_buf->length,
1346                                          "length mismatch");
1347                 torture_assert_int_equal(test_context->tctx,
1348                                          error.pvno, 5,
1349                                          "Got wrong error.pvno");
1350                 if ((torture_setting_bool(test_context->tctx, "expect_machine_account", false)
1351                      && ((test_context->test_data->upn == false)
1352                          || (test_context->test_data->as_req_spn &&
1353                              test_context->test_data->spn_is_upn)
1354                          || (test_context->test_data->enterprise == false &&
1355                              test_context->test_data->upn &&
1356                             test_context->test_data->spn_is_upn)))) {
1357                         torture_assert_int_equal(test_context->tctx,
1358                                                  error.error_code,
1359                                                  KRB5KRB_ERR_RESPONSE_TOO_BIG - KRB5KDC_ERR_NONE,
1360                                                  "Got wrong error.error_code");
1361                 } else {
1362                         torture_assert_int_equal(test_context->tctx,
1363                                                  error.error_code,
1364                                                  KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN - KRB5KDC_ERR_NONE,
1365                                                  "Got wrong error.error_code");
1366                 }
1367                 free_KRB_ERROR(&error);
1368         } else {
1369                 /*
1370                  * Finally the successful packet.
1371                  */
1372                 torture_assert_int_equal(test_context->tctx,
1373                                          decode_AS_REP(recv_buf->data, recv_buf->length,
1374                                                        &test_context->as_rep, &used), 0,
1375                                          "decode_AS_REP failed");
1376                 torture_assert_int_equal(test_context->tctx, used, recv_buf->length,
1377                                          "length mismatch");
1378                 torture_assert_int_equal(test_context->tctx,
1379                                          test_context->as_rep.pvno, 5,
1380                                          "Got wrong as_rep->pvno");
1381                 torture_assert_int_equal(test_context->tctx,
1382                                          test_context->as_rep.ticket.tkt_vno, 5,
1383                                          "Got wrong as_rep->ticket.tkt_vno");
1384                 torture_assert(test_context->tctx,
1385                                test_context->as_rep.ticket.enc_part.kvno,
1386                                "Did not get a KVNO in test_context->as_rep.ticket.enc_part.kvno");
1387
1388                 /*
1389                  * We do not expect an RODC number here in the KVNO,
1390                  * as this is a ticket to the user's own account.
1391                  */
1392                 torture_assert_int_equal(test_context->tctx,
1393                                          *test_context->as_rep.ticket.enc_part.kvno & 0xFFFF0000,
1394                                          0, "Unexpecedly got a RODC number in the KVNO");
1395                 free_AS_REP(&test_context->as_rep);
1396         }
1397         torture_assert(test_context->tctx, test_context->packet_count < 3, "too many packets");
1398         free_AS_REQ(&test_context->as_req);
1399         return true;
1400 }
1401
1402 /*
1403  * This function is set in torture_krb5_init_context_canon as krb5
1404  * send_and_recv function.  This allows us to override what server the
1405  * test is aimed at, and to inspect the packets just before they are
1406  * sent to the network, and before they are processed on the recv
1407  * side.
1408  *
1409  * The torture_krb5_pre_send_test() and torture_krb5_post_recv_test()
1410  * functions are implement the actual tests.
1411  *
1412  * When this asserts, the caller will get a spurious 'cannot contact
1413  * any KDC' message.
1414  *
1415  */
1416 static krb5_error_code smb_krb5_send_and_recv_func_canon_override(krb5_context context,
1417                                                                    void *data, /* struct torture_krb5_context */
1418                                                                    krb5_krbhst_info *hi,
1419                                                                    time_t timeout,
1420                                                                    const krb5_data *send_buf,
1421                                                                    krb5_data *recv_buf)
1422 {
1423         krb5_error_code k5ret;
1424         bool ok = false;
1425         krb5_data modified_send_buf;
1426
1427         struct torture_krb5_context *test_context
1428                 = talloc_get_type_abort(data, struct torture_krb5_context);
1429
1430         switch (test_context->test_stage) {
1431         case TEST_DONE:
1432                 torture_warning(test_context->tctx, "Unexpected outgoing packet from krb5 libs");
1433                 return EINVAL;
1434         case TEST_AS_REQ:
1435                 ok = torture_krb5_pre_send_as_req_test(test_context, send_buf,
1436                                                               &modified_send_buf);
1437                 break;
1438         case TEST_TGS_REQ_KRBTGT_CANON:
1439                 ok = torture_krb5_pre_send_tgs_req_krbtgt_canon_test(test_context, send_buf,
1440                                                                      &modified_send_buf);
1441                 break;
1442         case TEST_TGS_REQ_CANON:
1443                 ok = torture_krb5_pre_send_tgs_req_canon_test(test_context, send_buf,
1444                                                               &modified_send_buf);
1445                 break;
1446         case TEST_SELF_TRUST_TGS_REQ:
1447                 ok = torture_krb5_pre_send_self_trust_tgs_req_test(test_context, send_buf,
1448                                                                    &modified_send_buf);
1449                 break;
1450         case TEST_TGS_REQ:
1451                 ok = torture_krb5_pre_send_tgs_req_test(test_context, send_buf,
1452                                                         &modified_send_buf);
1453                 break;
1454         case TEST_TGS_REQ_KRBTGT:
1455                 ok = torture_krb5_pre_send_tgs_req_krbtgt_test(test_context, send_buf,
1456                                                                &modified_send_buf);
1457                 break;
1458         case TEST_TGS_REQ_HOST:
1459         case TEST_TGS_REQ_HOST_SRV_INST:
1460         case TEST_TGS_REQ_HOST_SRV_HST:
1461                 ok = torture_krb5_pre_send_tgs_req_host_test(test_context, send_buf,
1462                                                              &modified_send_buf);
1463                 break;
1464         case TEST_AS_REQ_SELF:
1465                 ok = torture_krb5_pre_send_as_req_test(test_context, send_buf,
1466                                                        &modified_send_buf);
1467                 break;
1468         }
1469         if (ok == false) {
1470                 return EINVAL;
1471         }
1472
1473         k5ret = smb_krb5_send_and_recv_func_forced(context, test_context->server,
1474                                                    hi, timeout, &modified_send_buf,
1475                                                    recv_buf);
1476         if (k5ret != 0) {
1477                 return k5ret;
1478         }
1479
1480         switch (test_context->test_stage) {
1481         case TEST_DONE:
1482                 torture_warning(test_context->tctx, "Unexpected outgoing packet from krb5 libs");
1483                 return EINVAL;
1484         case TEST_AS_REQ:
1485                 ok = torture_krb5_post_recv_as_req_test(test_context, recv_buf);
1486                 break;
1487         case TEST_TGS_REQ_KRBTGT_CANON:
1488                 ok = torture_krb5_post_recv_tgs_req_krbtgt_canon_test(test_context, recv_buf);
1489                 break;
1490         case TEST_TGS_REQ_CANON:
1491                 ok = torture_krb5_post_recv_tgs_req_canon_test(test_context, recv_buf);
1492                 break;
1493         case TEST_SELF_TRUST_TGS_REQ:
1494                 ok = torture_krb5_post_recv_self_trust_tgs_req_test(test_context, recv_buf);
1495                 break;
1496         case TEST_TGS_REQ:
1497                 ok = torture_krb5_post_recv_tgs_req_test(test_context, recv_buf);
1498                 break;
1499         case TEST_TGS_REQ_KRBTGT:
1500                 ok = torture_krb5_post_recv_self_trust_tgs_req_test(test_context, recv_buf);
1501                 break;
1502         case TEST_TGS_REQ_HOST:
1503         case TEST_TGS_REQ_HOST_SRV_INST:
1504         case TEST_TGS_REQ_HOST_SRV_HST:
1505                 ok = torture_krb5_post_recv_tgs_req_host_test(test_context, recv_buf);
1506                 break;
1507         case TEST_AS_REQ_SELF:
1508                 ok = torture_krb5_post_recv_as_req_self_test(test_context, recv_buf);
1509                 break;
1510         }
1511         if (ok == false) {
1512                 KRB_ERROR error;
1513                 size_t used;
1514                 torture_warning(test_context->tctx, "Packet of length %llu failed post-recv checks in test stage %d", (unsigned long long)recv_buf->length, test_context->test_stage);
1515                 if (decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used) == 0) {
1516                         torture_warning(test_context->tctx,
1517                                         "STAGE: %d Unexpectedly got a KRB-ERROR packet "
1518                                         "with error code %d (%s)",
1519                                         test_context->test_stage,
1520                                         error.error_code,
1521                                         error_message(error.error_code + KRB5KDC_ERR_NONE));
1522                         free_KRB_ERROR(&error);
1523                 }
1524                 return EINVAL;
1525         }
1526
1527         test_context->packet_count++;
1528
1529         return k5ret;
1530 }
1531
1532 static int test_context_destructor(struct torture_krb5_context *test_context)
1533 {
1534         freeaddrinfo(test_context->server);
1535         return 0;
1536 }
1537
1538
1539 static bool torture_krb5_init_context_canon(struct torture_context *tctx,
1540                                              struct test_data *test_data,
1541                                              struct torture_krb5_context **torture_krb5_context)
1542 {
1543         const char *host = torture_setting_string(tctx, "host", NULL);
1544         krb5_error_code k5ret;
1545         bool ok;
1546
1547         struct torture_krb5_context *test_context = talloc_zero(tctx, struct torture_krb5_context);
1548         torture_assert(tctx, test_context != NULL, "Failed to allocate");
1549
1550         test_context->test_data = test_data;
1551         test_context->tctx = tctx;
1552
1553         k5ret = smb_krb5_init_context(test_context, tctx->lp_ctx, &test_context->smb_krb5_context);
1554         torture_assert_int_equal(tctx, k5ret, 0, "smb_krb5_init_context failed");
1555
1556         ok = interpret_string_addr_internal(&test_context->server, host, AI_NUMERICHOST);
1557         torture_assert(tctx, ok, "Failed to parse target server");
1558
1559         talloc_set_destructor(test_context, test_context_destructor);
1560
1561         set_sockaddr_port(test_context->server->ai_addr, 88);
1562
1563         k5ret = krb5_set_send_to_kdc_func(test_context->smb_krb5_context->krb5_context,
1564                                           smb_krb5_send_and_recv_func_canon_override,
1565                                           test_context);
1566         torture_assert_int_equal(tctx, k5ret, 0, "krb5_set_send_to_kdc_func failed");
1567         *torture_krb5_context = test_context;
1568         return true;
1569 }
1570
1571
1572 static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void *tcase_data)
1573 {
1574         krb5_error_code k5ret;
1575         krb5_get_init_creds_opt *krb_options = NULL;
1576         struct test_data *test_data = talloc_get_type_abort(tcase_data, struct test_data);
1577         krb5_principal principal;
1578         krb5_principal krbtgt_other;
1579         krb5_principal expected_principal;
1580         const char *principal_string = NULL;
1581         char *krbtgt_other_string;
1582         int principal_flags;
1583         const char *expected_principal_string = NULL;
1584         char *expected_unparse_principal_string;
1585         int expected_principal_flags;
1586         char *got_principal_string;
1587         char *assertion_message;
1588         const char *password = cli_credentials_get_password(
1589                         popt_get_cmdline_credentials());
1590         krb5_context k5_context;
1591         struct torture_krb5_context *test_context;
1592         bool ok;
1593         krb5_creds my_creds;
1594         krb5_creds *server_creds;
1595         krb5_ccache ccache;
1596         krb5_auth_context auth_context;
1597         char *cc_name;
1598         krb5_data in_data, enc_ticket;
1599         krb5_get_creds_opt opt;
1600
1601         const char *spn = NULL;
1602         const char *spn_real_realm = NULL;
1603         const char *upn = torture_setting_string(tctx, "krb5-upn", "");
1604         test_data->krb5_service = torture_setting_string(tctx, "krb5-service", "host");
1605         test_data->krb5_hostname = torture_setting_string(tctx, "krb5-hostname", "");
1606
1607         /*
1608          * If we have not passed a UPN on the command line,
1609          * then skip the UPN tests.
1610          */
1611         if (test_data->upn && upn[0] == '\0') {
1612                 torture_skip(tctx, "This test needs a UPN specified as --option=torture:krb5-upn=user@example.com to run");
1613         }
1614
1615         /*
1616          * If we have not passed a SPN on the command line,
1617          * then skip the SPN tests.
1618          */
1619         if (test_data->as_req_spn && test_data->krb5_hostname[0] == '\0') {
1620                 torture_skip(tctx, "This test needs a hostname specified as --option=torture:krb5-hostname=hostname.example.com and optionally --option=torture:krb5-service=service (defaults to host) to run");
1621         }
1622
1623         if (test_data->removedollar &&
1624             !torture_setting_bool(tctx, "run_removedollar_test", false))
1625         {
1626                 torture_skip(tctx, "--option=torture:run_removedollar_test=true not specified");
1627         }
1628
1629         if (test_data->netbios_realm) {
1630                 test_data->realm = test_data->real_domain;
1631         } else {
1632                 test_data->realm = test_data->real_realm;
1633         }
1634
1635         if (test_data->upn) {
1636                 char *p;
1637                 test_data->username = talloc_strdup(test_data, upn);
1638                 p = strchr(test_data->username, '@');
1639                 if (p) {
1640                         *p = '\0';
1641                         p++;
1642                 }
1643                 /*
1644                  * Test the UPN behaviour carefully.  We can
1645                  * test in two different modes, depending on
1646                  * what UPN has been set up for us.
1647                  *
1648                  * If the UPN is in our realm, then we do all the tests with this name also.
1649                  *
1650                  * If the UPN is not in our realm, then we
1651                  * expect the tests that replace the realm to
1652                  * fail (as it won't match)
1653                  */
1654                 if (strcasecmp(p, test_data->real_realm) != 0) {
1655                         test_data->other_upn_suffix = true;
1656                 } else {
1657                         test_data->other_upn_suffix = false;
1658                 }
1659
1660                 /*
1661                  * This lets us test the combination of the UPN prefix
1662                  * with a valid domain, without adding even more
1663                  * combinations
1664                  */
1665                 if (test_data->netbios_realm == false) {
1666                         test_data->realm = p;
1667                 }
1668         }
1669
1670         ok = torture_krb5_init_context_canon(tctx, test_data, &test_context);
1671         torture_assert(tctx, ok, "torture_krb5_init_context failed");
1672         k5_context = test_context->smb_krb5_context->krb5_context;
1673
1674         if (test_data->upper_realm) {
1675                 test_data->realm = strupper_talloc(test_data, test_data->realm);
1676         } else {
1677                 test_data->realm = strlower_talloc(test_data, test_data->realm);
1678         }
1679         if (test_data->upper_username) {
1680                 test_data->username = strupper_talloc(test_data, test_data->username);
1681         } else {
1682                 test_data->username = talloc_strdup(test_data, test_data->username);
1683         }
1684
1685         if (test_data->removedollar) {
1686                 char *p;
1687
1688                 p = strchr_m(test_data->username, '$');
1689                 torture_assert(tctx, p != NULL, talloc_asprintf(tctx,
1690                                "username[%s] contains no '$'\n",
1691                                test_data->username));
1692                 *p = '\0';
1693         }
1694
1695         spn = talloc_asprintf(test_data, "%s/%s@%s",
1696                               test_data->krb5_service,
1697                               test_data->krb5_hostname,
1698                               test_data->realm);
1699
1700         spn_real_realm = talloc_asprintf(test_data, "%s/%s@%s",
1701                                          test_data->krb5_service,
1702                                          test_data->krb5_hostname,
1703                                          test_data->real_realm);
1704
1705         if (test_data->as_req_spn) {
1706                 if (test_data->enterprise) {
1707                         torture_skip(tctx,
1708                                      "This test combination "
1709                                      "is skipped intentionally");
1710                 }
1711                 principal_string = spn;
1712         } else {
1713                 principal_string = talloc_asprintf(test_data,
1714                                                    "%s@%s",
1715                                                    test_data->username,
1716                                                    test_data->realm);
1717                 
1718         }
1719
1720         test_data->spn_is_upn
1721                 = (strcasecmp(upn, spn) == 0);
1722                                 
1723         /*
1724          * If we are set to canonicalize, we get back the fixed UPPER
1725          * case realm, and the real username (ie matching LDAP
1726          * samAccountName)
1727          *
1728          * Otherwise, if we are set to enterprise, we
1729          * get back the whole principal as-sent
1730          *
1731          * Finally, if we are not set to canonicalize, we get back the
1732          * fixed UPPER case realm, but the as-sent username
1733          */
1734         if (test_data->as_req_spn && !test_data->spn_is_upn) {
1735                 expected_principal_string = spn;
1736         } else if (test_data->canonicalize) {
1737                 expected_principal_string = talloc_asprintf(test_data,
1738                                                             "%s@%s",
1739                                                             test_data->real_username,
1740                                                             test_data->real_realm);
1741         } else if (test_data->enterprise) {
1742                 expected_principal_string = principal_string;
1743         } else if (test_data->as_req_spn && test_data->spn_is_upn) {
1744                 expected_principal_string = spn_real_realm;
1745         } else {
1746                 expected_principal_string = talloc_asprintf(test_data,
1747                                                             "%s@%s",
1748                                                             test_data->username,
1749                                                             test_data->real_realm);
1750         }
1751
1752         if (test_data->enterprise) {
1753                 principal_flags = KRB5_PRINCIPAL_PARSE_ENTERPRISE;
1754         } else {
1755                 if (test_data->upn && test_data->other_upn_suffix) {
1756                         torture_skip(tctx, "UPN test for UPN with other UPN suffix only runs with enterprise principals");
1757                 }
1758                 principal_flags = 0;
1759         }
1760
1761         if (test_data->canonicalize) {
1762                 expected_principal_flags = 0;
1763         } else {
1764                 expected_principal_flags = principal_flags;
1765         }
1766
1767         torture_assert_int_equal(tctx,
1768                                  krb5_parse_name_flags(k5_context,
1769                                                        principal_string,
1770                                                        principal_flags,
1771                                                        &principal),
1772                                          0, "krb5_parse_name_flags failed");
1773         torture_assert_int_equal(tctx,
1774                                  krb5_parse_name_flags(k5_context,
1775                                                        expected_principal_string,
1776                                                        expected_principal_flags,
1777                                                        &expected_principal),
1778                                  0, "krb5_parse_name_flags failed");
1779         
1780         if (test_data->as_req_spn) {
1781                 if (test_data->upn) {
1782                         krb5_principal_set_type(k5_context,
1783                                                 principal,
1784                                                 KRB5_NT_PRINCIPAL);
1785                         krb5_principal_set_type(k5_context,
1786                                                 expected_principal,
1787                                                 KRB5_NT_PRINCIPAL);
1788                 } else {
1789                         krb5_principal_set_type(k5_context,
1790                                                 principal,
1791                                                 KRB5_NT_SRV_HST);
1792                         krb5_principal_set_type(k5_context,
1793                                                 expected_principal,
1794                                                 KRB5_NT_SRV_HST);
1795                 }
1796         }
1797         
1798         torture_assert_int_equal(tctx,
1799                                  krb5_unparse_name(k5_context,
1800                                                    expected_principal,
1801                                                    &expected_unparse_principal_string),
1802                                  0, "krb5_unparse_name failed");
1803         /*
1804          * Prepare a AS-REQ and run the TEST_AS_REQ tests
1805          *
1806          */
1807
1808         test_context->test_stage = TEST_AS_REQ;
1809         test_context->packet_count = 0;
1810
1811         /*
1812          * Set the canonicalize flag if this test requires it
1813          */
1814         torture_assert_int_equal(tctx,
1815                                  krb5_get_init_creds_opt_alloc(k5_context, &krb_options),
1816                                  0, "krb5_get_init_creds_opt_alloc failed");
1817
1818         torture_assert_int_equal(tctx,
1819                                  krb5_get_init_creds_opt_set_canonicalize(k5_context,
1820                                                                           krb_options,
1821                                                                           test_data->canonicalize),
1822                                  0, "krb5_get_init_creds_opt_set_canonicalize failed");
1823
1824         torture_assert_int_equal(tctx,
1825                                  krb5_get_init_creds_opt_set_win2k(k5_context,
1826                                                                    krb_options,
1827                                                                    test_data->win2k),
1828                                  0, "krb5_get_init_creds_opt_set_win2k failed");
1829
1830         k5ret = krb5_get_init_creds_password(k5_context, &my_creds, principal,
1831                                              password, NULL, NULL, 0,
1832                                              NULL, krb_options);
1833
1834         if (test_data->netbios_realm && test_data->upn) {
1835                 torture_assert_int_equal(tctx, k5ret,
1836                                          KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN,
1837                                          "Got wrong error_code from krb5_get_init_creds_password");
1838                 /* We can't proceed with more checks */
1839                 return true;
1840         } else if (test_context->test_data->as_req_spn
1841                    && !test_context->test_data->spn_is_upn) {
1842                 torture_assert_int_equal(tctx, k5ret,
1843                                          KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN,
1844                                          "Got wrong error_code from "
1845                                          "krb5_get_init_creds_password");
1846                 /* We can't proceed with more checks */
1847                 return true;
1848         } else {
1849                 assertion_message = talloc_asprintf(tctx,
1850                                                     "krb5_get_init_creds_password for %s failed: %s",
1851                                                     principal_string,
1852                                                     smb_get_krb5_error_message(k5_context, k5ret, tctx));
1853                 torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
1854         }
1855
1856         torture_assert(tctx,
1857                        test_context->packet_count > 1,
1858                        "Expected krb5_get_init_creds_password to send more packets");
1859
1860         /*
1861          * Assert that the reply was with the correct type of
1862          * principal, depending on the flags we set
1863          */
1864         if (test_data->canonicalize == false && test_data->enterprise) {
1865                 torture_assert_int_equal(tctx,
1866                                          krb5_principal_get_type(k5_context,
1867                                                                  my_creds.client),
1868                                          KRB5_NT_ENTERPRISE_PRINCIPAL,
1869                                          "smb_krb5_init_context gave incorrect client->name.name_type");
1870         } else if (test_data->canonicalize == false && test_data->as_req_spn) {
1871                 torture_assert_int_equal(tctx,
1872                                          krb5_principal_get_type(k5_context,
1873                                                                  my_creds.client),
1874                                          KRB5_NT_SRV_HST,
1875                                          "smb_krb5_init_context gave incorrect client->name.name_type");
1876         } else {
1877                 torture_assert_int_equal(tctx,
1878                                          krb5_principal_get_type(k5_context,
1879                                                                  my_creds.client),
1880                                          KRB5_NT_PRINCIPAL,
1881                                          "smb_krb5_init_context gave incorrect client->name.name_type");
1882         }
1883
1884         torture_assert_int_equal(tctx,
1885                                  krb5_unparse_name(k5_context,
1886                                                    my_creds.client, &got_principal_string), 0,
1887                                  "krb5_unparse_name failed");
1888
1889         assertion_message = talloc_asprintf(tctx,
1890                                             "krb5_get_init_creds_password returned a different principal %s to what was expected %s",
1891                                             got_principal_string, expected_principal_string);
1892         krb5_free_unparsed_name(k5_context, got_principal_string);
1893
1894         torture_assert(tctx, krb5_principal_compare(k5_context,
1895                                                     my_creds.client, expected_principal),
1896                        assertion_message);
1897
1898
1899         torture_assert_int_equal(tctx,
1900                                  krb5_principal_get_type(k5_context,
1901                                                          my_creds.server), KRB5_NT_SRV_INST,
1902                                  "smb_krb5_init_context gave incorrect server->name.name_type");
1903
1904         torture_assert_int_equal(tctx,
1905                                  krb5_principal_get_num_comp(k5_context,
1906                                                              my_creds.server), 2,
1907                                  "smb_krb5_init_context gave incorrect number of components in my_creds.server->name");
1908
1909         torture_assert_str_equal(tctx,
1910                                  krb5_principal_get_comp_string(k5_context,
1911                                                                 my_creds.server, 0),
1912                                  "krbtgt",
1913                                  "smb_krb5_init_context gave incorrect my_creds.server->name.name_string[0]");
1914
1915         if (test_data->canonicalize || test_data->enterprise) {
1916                 torture_assert_str_equal(tctx,
1917                                          krb5_principal_get_comp_string(k5_context,
1918                                                                         my_creds.server, 1),
1919                                          test_data->real_realm,
1920
1921                                          "smb_krb5_init_context gave incorrect my_creds.server->name.name_string[1]");
1922         } else {
1923                 torture_assert_str_equal(tctx,
1924                                          krb5_principal_get_comp_string(k5_context,
1925                                                                         my_creds.server, 1),
1926                                          test_data->realm,
1927
1928                                          "smb_krb5_init_context gave incorrect my_creds.server->name.name_string[1]");
1929         }
1930         torture_assert_str_equal(tctx,
1931                                  krb5_principal_get_realm(k5_context,
1932                                                           my_creds.server),
1933                                  test_data->real_realm,
1934                                  "smb_krb5_init_context gave incorrect my_creds.server->realm");
1935
1936         /* Store the result of the 'kinit' above into a memory ccache */
1937         cc_name = talloc_asprintf(tctx, "MEMORY:%s", test_data->test_name);
1938         torture_assert_int_equal(tctx, krb5_cc_resolve(k5_context, cc_name,
1939                                                        &ccache),
1940                                  0, "krb5_cc_resolve failed");
1941
1942         torture_assert_int_equal(tctx, krb5_cc_initialize(k5_context,
1943                                                           ccache, my_creds.client),
1944                                  0, "krb5_cc_initialize failed");
1945
1946         torture_assert_int_equal(tctx, krb5_cc_store_cred(k5_context,
1947                                                           ccache, &my_creds),
1948                                  0, "krb5_cc_store_cred failed");
1949
1950         /*
1951          * Prepare a TGS-REQ and run the TEST_TGS_REQ_KRBTGT_CANON tests
1952          *
1953          * This tests krb5_get_creds behaviour, which allows us to set
1954          * the KRB5_GC_CANONICALIZE option against the krbtgt/ principal
1955          */
1956
1957         krbtgt_other_string = talloc_asprintf(test_data, "krbtgt/%s@%s", test_data->real_domain, test_data->real_realm);
1958         torture_assert_int_equal(tctx,
1959                                  krb5_make_principal(k5_context, &krbtgt_other,
1960                                                      test_data->real_realm, "krbtgt",
1961                                                      test_data->real_domain, NULL),
1962                                  0, "krb5_make_principal failed");
1963
1964         test_context->test_stage = TEST_TGS_REQ_KRBTGT_CANON;
1965         test_context->packet_count = 0;
1966
1967         torture_assert_int_equal(tctx,
1968                                  krb5_get_creds_opt_alloc(k5_context, &opt),
1969                                  0, "krb5_get_creds_opt_alloc");
1970
1971         krb5_get_creds_opt_add_options(k5_context,
1972                                        opt,
1973                                        KRB5_GC_CANONICALIZE);
1974
1975         krb5_get_creds_opt_add_options(k5_context,
1976                                        opt,
1977                                        KRB5_GC_NO_STORE);
1978
1979         /* Confirm if we can get a ticket krbtgt/realm that we got back with the initial kinit */
1980         k5ret = krb5_get_creds(k5_context, opt, ccache, krbtgt_other, &server_creds);
1981
1982         if (test_data->canonicalize == false && test_data->enterprise == false
1983             && test_data->netbios_realm && test_data->upper_realm) {
1984                 /*
1985                  * In these situations, the code above does store a
1986                  * principal in the credentials cache matching what
1987                  * krb5_get_creds() needs, so the test succeds, with no packets.
1988                  *
1989                  */
1990                 assertion_message = talloc_asprintf(tctx,
1991                                                     "krb5_get_creds for %s failed with: %s",
1992                                                     krbtgt_other_string,
1993                                                     smb_get_krb5_error_message(k5_context, k5ret,
1994                                                                                tctx));
1995
1996                 torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
1997                 torture_assert_int_equal(tctx,
1998                                          test_context->packet_count,
1999                                          0, "Expected krb5_get_creds not to send packets");
2000         } else if (test_data->canonicalize == false && test_data->enterprise == false
2001                    && (test_data->upper_realm == false || test_data->netbios_realm == true)) {
2002                 torture_assert_int_equal(tctx, k5ret, KRB5_CC_NOTFOUND,
2003                                          "krb5_get_creds should have failed with KRB5_CC_NOTFOUND");
2004         } else {
2005
2006                 /*
2007                  * In these situations, the code above does not store a
2008                  * principal in the credentials cache matching what
2009                  * krb5_get_creds() needs without talking to the KDC, so the
2010                  * test fails with looping detected because when we set
2011                  * canonicalize we confuse the client libs.
2012                  *
2013                  */
2014                 assertion_message = talloc_asprintf(tctx,
2015                                                     "krb5_get_creds for %s should have failed with looping detected: %s",
2016                                                     krbtgt_other_string,
2017                                                     smb_get_krb5_error_message(k5_context, k5ret,
2018                                                                                tctx));
2019
2020                 torture_assert_int_equal(tctx, k5ret, KRB5_GET_IN_TKT_LOOP, assertion_message);
2021                 torture_assert_int_equal(tctx,
2022                                          test_context->packet_count,
2023                                          2, "Expected krb5_get_creds to send packets");
2024         }
2025
2026         /*
2027          * Prepare a TGS-REQ and run the TEST_TGS_REQ_CANON tests
2028          *
2029          * This tests krb5_get_creds behaviour, which allows us to set
2030          * the KRB5_GC_CANONICALIZE option
2031          */
2032
2033         test_context->test_stage = TEST_TGS_REQ_CANON;
2034         test_context->packet_count = 0;
2035
2036         torture_assert_int_equal(tctx,
2037                                  krb5_get_creds_opt_alloc(k5_context, &opt),
2038                                  0, "krb5_get_creds_opt_alloc");
2039
2040         krb5_get_creds_opt_add_options(k5_context,
2041                                        opt,
2042                                        KRB5_GC_CANONICALIZE);
2043
2044         krb5_get_creds_opt_add_options(k5_context,
2045                                        opt,
2046                                        KRB5_GC_NO_STORE);
2047
2048         if (test_data->s4u2self) {
2049                 torture_assert_int_equal(tctx,
2050                                          krb5_get_creds_opt_set_impersonate(k5_context,
2051                                                                             opt,
2052                                                                             principal),
2053                                          0, "krb5_get_creds_opt_set_impersonate failed");
2054         }
2055
2056         /* Confirm if we can get a ticket to our own name */
2057         k5ret = krb5_get_creds(k5_context, opt, ccache, principal, &server_creds);
2058
2059         /*
2060          * In these situations, the code above does not store a
2061          * principal in the credentials cache matching what
2062          * krb5_get_creds() needs, so the test fails.
2063          *
2064          */
2065         if (test_data->canonicalize == false && test_data->enterprise == false
2066             && (test_data->upper_realm == false || test_data->netbios_realm == true)) {
2067                 torture_assert_int_equal(tctx, k5ret, KRB5_CC_NOTFOUND,
2068                                          "krb5_get_creds should have failed with KRB5_CC_NOTFOUND");
2069         } else {
2070                 assertion_message = talloc_asprintf(tctx,
2071                                                     "krb5_get_creds for %s failed: %s",
2072                                                     principal_string,
2073                                                     smb_get_krb5_error_message(k5_context, k5ret,
2074                                                                                tctx));
2075
2076                 /*
2077                  * Only machine accounts (strictly, accounts with a
2078                  * servicePrincipalName) can expect this test to succeed
2079                  */
2080                 if (torture_setting_bool(tctx, "expect_machine_account", false)
2081                     && (test_data->enterprise
2082                         || test_data->spn_is_upn
2083                         || test_data->upn == false)) {
2084
2085                         if (test_data->mitm_s4u2self) {
2086                                 torture_assert_int_equal(tctx, k5ret, KRB5KRB_AP_ERR_INAPP_CKSUM,
2087                                                          assertion_message);
2088                                 /* Done testing mitm-s4u2self */
2089                                 return true;
2090                         }
2091
2092                         torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
2093
2094                         /* Check that the impersonate principal is not being canonicalized by the KDC. */
2095                         if (test_data->s4u2self) {
2096                                 torture_assert(tctx, krb5_principal_compare(k5_context, server_creds->client,
2097                                                                             principal),
2098                                                "TGS-REP cname does not match requested client principal");
2099                         }
2100
2101                         torture_assert_int_equal(tctx, krb5_cc_store_cred(k5_context,
2102                                                                           ccache, server_creds),
2103                                                  0, "krb5_cc_store_cred failed");
2104
2105                         torture_assert_int_equal(tctx,
2106                                                  krb5_free_creds(k5_context,
2107                                                                  server_creds),
2108                                                  0, "krb5_free_cred_contents failed");
2109
2110                 } else {
2111                         torture_assert_int_equal(tctx, k5ret, KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN,
2112                                                  assertion_message);
2113                 }
2114
2115                 torture_assert_int_equal(tctx,
2116                                          test_context->packet_count,
2117                                          1, "Expected krb5_get_creds to send packets");
2118         }
2119
2120         /*
2121          * Confirm gettting a ticket to pass to the server, running
2122          * either the TEST_TGS_REQ or TEST_SELF_TRUST_TGS_REQ stage.
2123          *
2124          * This triggers the client to attempt to get a
2125          * cross-realm ticket between the alternate names of
2126          * the server, and we need to confirm that behaviour.
2127          *
2128          */
2129
2130         /*
2131          * This tries to guess when the krb5 libs will ask for a
2132          * cross-realm ticket, and when they will just ask the KDC
2133          * directly.
2134          */
2135         if (test_context->test_data->canonicalize == false
2136             || test_context->test_data->enterprise
2137             || (test_context->test_data->spn_is_upn && test_context->test_data->upn)
2138             || (test_context->test_data->upper_realm
2139                 && test_context->test_data->netbios_realm == false)) {
2140                 test_context->test_stage = TEST_TGS_REQ;
2141         } else {
2142                 test_context->test_stage = TEST_SELF_TRUST_TGS_REQ;
2143         }
2144
2145         test_context->packet_count = 0;
2146         torture_assert_int_equal(tctx, krb5_auth_con_init(k5_context, &auth_context),
2147                                  0, "krb5_auth_con_init failed");
2148
2149         in_data.length = 0;
2150         k5ret = krb5_mk_req_exact(k5_context,
2151                                   &auth_context,
2152                                   AP_OPTS_USE_SUBKEY,
2153                                   principal,
2154                                   &in_data, ccache,
2155                                   &enc_ticket);
2156         assertion_message = talloc_asprintf(tctx,
2157                                             "krb5_mk_req_exact for %s failed: %s",
2158                                             principal_string,
2159                                             smb_get_krb5_error_message(k5_context, k5ret, tctx));
2160
2161         /*
2162          * Only machine accounts (strictly, accounts with a
2163          * servicePrincipalName) can expect this test to succeed
2164          */
2165         if (torture_setting_bool(tctx, "expect_machine_account", false)
2166             && (test_data->enterprise ||
2167                 (test_context->test_data->as_req_spn 
2168                  || test_context->test_data->spn_is_upn)
2169                 || test_data->upn == false)) {
2170                 DATA_BLOB client_to_server;
2171                 torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
2172                 client_to_server = data_blob_const(enc_ticket.data, enc_ticket.length);
2173
2174                 /* This is very weird */
2175                 if (test_data->canonicalize == false
2176                     && test_context->test_data->as_req_spn
2177                     && test_context->test_data->spn_is_upn
2178                     && test_context->test_data->s4u2self) {
2179                         
2180                         torture_assert(tctx,
2181                                        test_accept_ticket(tctx,
2182                                                           popt_get_cmdline_credentials(),
2183                                                           spn_real_realm,
2184                                                           client_to_server),
2185                                        "test_accept_ticket failed - failed to accept the ticket we just created");
2186                 } else if (test_data->canonicalize == true
2187                     && test_context->test_data->as_req_spn
2188                     && test_context->test_data->spn_is_upn
2189                     && test_context->test_data->s4u2self) {
2190                         
2191                         torture_assert(tctx,
2192                                        test_accept_ticket(tctx,
2193                                                           popt_get_cmdline_credentials(),
2194                                                           spn,
2195                                                           client_to_server),
2196                                        "test_accept_ticket failed - failed to accept the ticket we just created");
2197                 } else if (test_data->canonicalize == true
2198                            && test_data->enterprise == false
2199                            && test_context->test_data->upn
2200                            && test_context->test_data->spn_is_upn
2201                            && test_context->test_data->s4u2self) {
2202                         
2203                         torture_assert(tctx,
2204                                        test_accept_ticket(tctx,
2205                                                           popt_get_cmdline_credentials(),
2206                                                           principal_string,
2207                                                           client_to_server),
2208                                        "test_accept_ticket failed - failed to accept the ticket we just created");
2209                 } else if (test_data->canonicalize == false
2210                            && test_data->enterprise == false
2211                            && test_context->test_data->upn
2212                            && test_context->test_data->spn_is_upn
2213                            && test_context->test_data->s4u2self) {
2214                         
2215                         const char *accept_expected_principal_string
2216                                 = talloc_asprintf(test_data,
2217                                                   "%s@%s",
2218                                                   test_data->username,
2219                                                   test_data->real_realm);
2220                         
2221                         torture_assert(tctx,
2222                                        test_accept_ticket(tctx,
2223                                                           popt_get_cmdline_credentials(),
2224                                                           accept_expected_principal_string,
2225                                                           client_to_server),
2226                                        "test_accept_ticket failed - failed to accept the ticket we just created");
2227                 } else {
2228                 
2229                         torture_assert(tctx,
2230                                        test_accept_ticket(tctx,
2231                                                           popt_get_cmdline_credentials(),
2232                                                           expected_unparse_principal_string,
2233                                                           client_to_server),
2234                                        "test_accept_ticket failed - failed to accept the ticket we just created");
2235                 }
2236                 krb5_data_free(&enc_ticket);
2237         } else {
2238                 torture_assert_int_equal(tctx, k5ret, KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN,
2239                                          assertion_message);
2240         }
2241
2242         /*
2243          * Only in these cases would the above code have needed to
2244          * send packets to the network
2245          */
2246         if (test_data->canonicalize == false && test_data->enterprise == false
2247             && (test_data->upper_realm == false || test_data->netbios_realm == true)) {
2248                 torture_assert(tctx,
2249                                test_context->packet_count > 0,
2250                                "Expected krb5_mk_req_exact to send packets");
2251         }
2252
2253         /*
2254          * Confirm gettting a ticket to pass to the server, running
2255          * the TEST_TGS_REQ_HOST, TEST_TGS_REQ_HOST_SRV_INST, TEST_TGS_REQ_HOST_SRV_HST stage
2256          *
2257          * This triggers the client to attempt to get a
2258          * cross-realm ticket between the alternate names of
2259          * the server, and we need to confirm that behaviour.
2260          *
2261          */
2262
2263         if (*test_data->krb5_service && *test_data->krb5_hostname) {
2264                 bool implied_canonicalize;
2265                 krb5_principal host_principal_srv_inst;
2266                 /*
2267                  * This tries to guess when the krb5 libs will ask for a
2268                  * cross-realm ticket, and when they will just ask the KDC
2269                  * directly.
2270                  */
2271                 test_context->test_stage = TEST_TGS_REQ_HOST;
2272                 test_context->packet_count = 0;
2273                 torture_assert_int_equal(tctx, krb5_auth_con_init(k5_context, &auth_context),
2274                                          0, "krb5_auth_con_init failed");
2275
2276                 in_data.length = 0;
2277                 k5ret = krb5_mk_req(k5_context,
2278                                     &auth_context,
2279                                     0,
2280                                     test_data->krb5_service,
2281                                     test_data->krb5_hostname,
2282                                     &in_data, ccache,
2283                                     &enc_ticket);
2284
2285                 implied_canonicalize = test_data->canonicalize;
2286                 if (test_data->spn_is_upn && (test_data->upn || test_data->as_req_spn)) {
2287                         implied_canonicalize = true;
2288                 }
2289                 if (test_data->enterprise) {
2290                         implied_canonicalize = true;
2291                 }
2292
2293                 if (implied_canonicalize == false
2294                     && (test_data->upper_realm == false || test_data->netbios_realm == true)) {
2295                         torture_assert_int_equal(tctx, k5ret, KRB5_CC_NOTFOUND,
2296                                                  "krb5_get_creds should have failed with KRB5_CC_NOTFOUND");
2297                 } else if (test_data->spn_is_upn
2298                            && test_data->canonicalize == false
2299                            && test_data->enterprise == false
2300                            && test_data->upper_realm == false
2301                            && test_data->upper_username == true
2302                            && test_data->upn) {
2303                         torture_assert_int_equal(tctx, k5ret, KRB5_CC_NOTFOUND,
2304                                                  "krb5_get_creds should have failed with KRB5_CC_NOTFOUND");
2305                 } else {
2306                         assertion_message = talloc_asprintf(tctx,
2307                                                             "krb5_mk_req for %s/%s failed: %s",
2308                                                             test_data->krb5_service,
2309                                                             test_data->krb5_hostname,
2310                                                             smb_get_krb5_error_message(k5_context, k5ret, tctx));
2311
2312                         torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
2313
2314                         if (test_data->spn_is_upn == false) {
2315                                 /*
2316                                  * Only in these cases would the above
2317                                  * code have needed to send packets to
2318                                  * the network
2319                                  */
2320                                 torture_assert(tctx,
2321                                                test_context->packet_count > 0,
2322                                                "Expected krb5_get_creds to send packets");
2323                         }
2324                 }
2325
2326
2327                 test_context->test_stage = TEST_TGS_REQ_HOST_SRV_INST;
2328                 test_context->packet_count = 0;
2329
2330                 torture_assert_int_equal(tctx,
2331                                          krb5_make_principal(k5_context, &host_principal_srv_inst,
2332                                                              test_data->real_realm,
2333                                                              strupper_talloc(tctx, test_data->krb5_service),
2334                                                              test_data->krb5_hostname,
2335                                                              NULL),
2336                                          0, "krb5_make_principal failed");
2337
2338                 krb5_principal_set_type(k5_context, host_principal_srv_inst, KRB5_NT_SRV_INST);
2339
2340                 torture_assert_int_equal(tctx, krb5_auth_con_init(k5_context, &auth_context),
2341                                          0, "krb5_auth_con_init failed");
2342
2343                 in_data.length = 0;
2344                 k5ret = krb5_mk_req_exact(k5_context,
2345                                           &auth_context,
2346                                           0,
2347                                           host_principal_srv_inst,
2348                                           &in_data, ccache,
2349                                           &enc_ticket);
2350                 krb5_free_principal(k5_context, host_principal_srv_inst);
2351                 if (test_data->canonicalize == false && test_data->enterprise == false
2352                     && (test_data->upper_realm == false || test_data->netbios_realm == true)) {
2353                         torture_assert_int_equal(tctx, k5ret, KRB5_CC_NOTFOUND,
2354                                                  "krb5_get_creds should have failed with KRB5_CC_NOTFOUND");
2355                 } else {
2356                         assertion_message = talloc_asprintf(tctx,
2357                                                             "krb5_mk_req for %s/%s KRB5_NT_SRV_INST failed: %s",
2358                                                             test_data->krb5_service,
2359                                                             test_data->krb5_hostname,
2360                                                             smb_get_krb5_error_message(k5_context, k5ret, tctx));
2361
2362                         torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
2363                         /*
2364                          * Only in these cases would the above code have needed to
2365                          * send packets to the network
2366                          */
2367                         torture_assert(tctx,
2368                                        test_context->packet_count > 0,
2369                                        "Expected krb5_get_creds to send packets");
2370                 }
2371
2372
2373                 test_context->test_stage = TEST_TGS_REQ_HOST_SRV_HST;
2374                 test_context->packet_count = 0;
2375
2376                 torture_assert_int_equal(tctx,
2377                                          krb5_make_principal(k5_context, &host_principal_srv_inst,
2378                                                              test_data->real_realm,
2379                                                              test_data->krb5_service,
2380                                                              strupper_talloc(tctx, test_data->krb5_hostname),
2381                                                              NULL),
2382                                          0, "krb5_make_principal failed");
2383
2384                 krb5_principal_set_type(k5_context, host_principal_srv_inst, KRB5_NT_SRV_HST);
2385
2386                 torture_assert_int_equal(tctx, krb5_auth_con_init(k5_context, &auth_context),
2387                                          0, "krb5_auth_con_init failed");
2388
2389                 in_data.length = 0;
2390                 k5ret = krb5_mk_req_exact(k5_context,
2391                                           &auth_context,
2392                                           0,
2393                                           host_principal_srv_inst,
2394                                           &in_data, ccache,
2395                                           &enc_ticket);
2396                 krb5_free_principal(k5_context, host_principal_srv_inst);
2397                 if (test_data->canonicalize == false && test_data->enterprise == false
2398                     && (test_data->upper_realm == false || test_data->netbios_realm == true)) {
2399                         torture_assert_int_equal(tctx, k5ret, KRB5_CC_NOTFOUND,
2400                                                  "krb5_get_creds should have failed with KRB5_CC_NOTFOUND");
2401                 } else {
2402                         assertion_message = talloc_asprintf(tctx,
2403                                                             "krb5_mk_req for %s/%s KRB5_NT_SRV_INST failed: %s",
2404                                                             test_data->krb5_service,
2405                                                             test_data->krb5_hostname,
2406                                                             smb_get_krb5_error_message(k5_context, k5ret, tctx));
2407
2408                         torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
2409                         /*
2410                          * Only in these cases would the above code have needed to
2411                          * send packets to the network
2412                          */
2413                         torture_assert(tctx,
2414                                        test_context->packet_count > 0,
2415                                        "Expected krb5_get_creds to send packets");
2416                 }
2417         }
2418
2419         /*
2420          * Confirm gettting a ticket for the same krbtgt/realm that we
2421          * got back with the initial ticket, running the
2422          * TEST_TGS_REQ_KRBTGT stage.
2423          *
2424          */
2425
2426         test_context->test_stage = TEST_TGS_REQ_KRBTGT;
2427         test_context->packet_count = 0;
2428
2429         in_data.length = 0;
2430         k5ret = krb5_mk_req_exact(k5_context,
2431                                   &auth_context,
2432                                   0,
2433                                   my_creds.server,
2434                                   &in_data, ccache,
2435                                   &enc_ticket);
2436
2437         assertion_message = talloc_asprintf(tctx,
2438                                             "krb5_mk_req_exact for %s failed: %s",
2439                                             principal_string,
2440                                             smb_get_krb5_error_message(k5_context, k5ret, tctx));
2441         torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
2442
2443         /*
2444          * Confirm gettting a ticket for our own principal that we
2445          * got back with the initial ticket, running the
2446          * TEST_AS_REQ_SELF stage.
2447          *
2448          */
2449         test_context->test_stage = TEST_AS_REQ_SELF;
2450         test_context->packet_count = 0;
2451
2452         k5ret = krb5_get_init_creds_password(k5_context, &my_creds, principal,
2453                                              password, NULL, NULL, 0,
2454                                              principal_string, krb_options);
2455
2456         if (torture_setting_bool(test_context->tctx, "expect_machine_account", false)
2457             && (test_data->upn == false || (test_data->enterprise == false && test_data->upn == true && test_data->spn_is_upn))) {
2458                 assertion_message = talloc_asprintf(tctx,
2459                                                     "krb5_get_init_creds_password for %s failed: %s",
2460                                                     principal_string,
2461                                                     smb_get_krb5_error_message(k5_context, k5ret, tctx));
2462                 torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
2463                 torture_assert(tctx,
2464                                test_context->packet_count >= 2,
2465                                "Expected krb5_get_init_creds_password to send more packets");
2466
2467         } else {
2468                 assertion_message = talloc_asprintf(tctx,
2469                                                     "Got wrong error_code from krb5_get_init_creds_password, expected KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN trying to get a ticket to %s for %s", principal_string, principal_string);
2470                 torture_assert_int_equal(tctx, k5ret,
2471                                          KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN,
2472                                          assertion_message);
2473                 torture_assert(tctx,
2474                                test_context->packet_count >= 1,
2475                                "Expected krb5_get_init_creds_password to send more packets");
2476
2477                 /* We can't proceed with more checks */
2478                 return true;
2479         }
2480
2481         /*
2482          * Assert that the reply was with the correct type of
2483          * principal, depending on the flags we set
2484          */
2485         if (test_data->as_req_spn && test_data->spn_is_upn && test_data->canonicalize == false) {
2486                 torture_assert_int_equal(tctx,
2487                                          krb5_principal_get_type(k5_context,
2488                                                                  my_creds.client),
2489                                          KRB5_NT_SRV_HST,
2490                                          "smb_krb5_init_context gave incorrect client->name.name_type");
2491                 torture_assert_int_equal(tctx,
2492                                          krb5_principal_get_type(k5_context,
2493                                                                  my_creds.server),
2494                                          KRB5_NT_SRV_HST,
2495                                          "smb_krb5_init_context gave incorrect server->name.name_type");
2496         } else if (test_data->canonicalize == false && test_data->enterprise) {
2497                 torture_assert_int_equal(tctx,
2498                                          krb5_principal_get_type(k5_context,
2499                                                                  my_creds.client),
2500                                          KRB5_NT_ENTERPRISE_PRINCIPAL,
2501                                          "smb_krb5_init_context gave incorrect client->name.name_type");
2502                 torture_assert_int_equal(tctx,
2503                                          krb5_principal_get_type(k5_context,
2504                                                                  my_creds.server),
2505                                          KRB5_NT_ENTERPRISE_PRINCIPAL,
2506                                          "smb_krb5_init_context gave incorrect server->name.name_type");
2507         } else {
2508                 torture_assert_int_equal(tctx,
2509                                          krb5_principal_get_type(k5_context,
2510                                                                  my_creds.client),
2511                                          KRB5_NT_PRINCIPAL,
2512                                          "smb_krb5_init_context gave incorrect client->name.name_type");
2513                 torture_assert_int_equal(tctx,
2514                                          krb5_principal_get_type(k5_context,
2515                                                                  my_creds.server),
2516                                          KRB5_NT_PRINCIPAL,
2517                                          "smb_krb5_init_context gave incorrect server->name.name_type");
2518         }
2519
2520         torture_assert_int_equal(tctx,
2521                                  krb5_unparse_name(k5_context,
2522                                                    my_creds.client, &got_principal_string), 0,
2523                                  "krb5_unparse_name failed");
2524
2525         assertion_message = talloc_asprintf(tctx,
2526                                             "krb5_get_init_creds_password returned a different principal %s to what was expected %s",
2527                                             got_principal_string, expected_principal_string);
2528         krb5_free_unparsed_name(k5_context, got_principal_string);
2529
2530         torture_assert(tctx, krb5_principal_compare(k5_context,
2531                                                     my_creds.client, expected_principal),
2532                        assertion_message);
2533
2534         torture_assert_int_equal(tctx,
2535                                  krb5_unparse_name(k5_context,
2536                                                    my_creds.client, &got_principal_string), 0,
2537                                  "krb5_unparse_name failed");
2538
2539         assertion_message = talloc_asprintf(tctx,
2540                                             "krb5_get_init_creds_password returned a different server principal %s to what was expected %s",
2541                                             got_principal_string, expected_principal_string);
2542         krb5_free_unparsed_name(k5_context, got_principal_string);
2543
2544         torture_assert(tctx, krb5_principal_compare(k5_context,
2545                                                     my_creds.server, expected_principal),
2546                        assertion_message);
2547
2548         krb5_free_principal(k5_context, principal);
2549         krb5_get_init_creds_opt_free(k5_context, krb_options);
2550
2551         torture_assert_int_equal(tctx, krb5_free_cred_contents(k5_context, &my_creds),
2552                                  0, "krb5_free_cred_contents failed");
2553
2554         return true;
2555 }
2556
2557 struct torture_suite *torture_krb5_canon(TALLOC_CTX *mem_ctx)
2558 {
2559         unsigned int i;
2560         struct torture_suite *suite = torture_suite_create(mem_ctx, "canon");
2561         suite->description = talloc_strdup(suite, "Kerberos Canonicalisation tests");
2562
2563         for (i = 0; i < TEST_ALL; i++) {
2564                 char *name = talloc_asprintf(suite, "%s.%s.%s.%s.%s.%s.%s.%s",
2565                                              (i & TEST_CANONICALIZE) ? "canon" : "no-canon",
2566                                              (i & TEST_ENTERPRISE) ? "enterprise" : "no-enterprise",
2567                                              (i & TEST_UPPER_REALM) ? "uc-realm" : "lc-realm",
2568                                              (i & TEST_UPPER_USERNAME) ? "uc-user" : "lc-user",
2569                                              (i & TEST_NETBIOS_REALM) ? "netbios-realm" : "krb5-realm",
2570                                              (i & TEST_WIN2K) ? "win2k" : "no-win2k",
2571                                              (i & TEST_UPN) ? "upn" :
2572                                              ((i & TEST_AS_REQ_SPN) ? "spn" : 
2573                                               ((i & TEST_REMOVEDOLLAR) ? "removedollar" : "samaccountname")),
2574                                              (i & TEST_S4U2SELF) ? (i & TEST_MITM_S4U2SELF) ? "mitm-s4u2self" : "s4u2self" : "normal");
2575                 struct torture_suite *sub_suite = torture_suite_create(mem_ctx, name);
2576
2577                 struct test_data *test_data = talloc_zero(suite, struct test_data);
2578                 if (i & TEST_UPN) {
2579                         if (i & TEST_AS_REQ_SPN) {
2580                                 continue;
2581                         }
2582                 }
2583                 if ((i & TEST_UPN) || (i & TEST_AS_REQ_SPN)) {
2584                         if (i & TEST_REMOVEDOLLAR) {
2585                                 continue;
2586                         }
2587                 }
2588                 if (i & TEST_MITM_S4U2SELF) {
2589                         if (!(i & TEST_S4U2SELF)) {
2590                                 continue;
2591                         }
2592                 }
2593                 
2594                 test_data->test_name = name;
2595                 test_data->real_realm
2596                         = strupper_talloc(test_data,
2597                                 cli_credentials_get_realm(
2598                                         popt_get_cmdline_credentials()));
2599                 test_data->real_domain = cli_credentials_get_domain(
2600                                                 popt_get_cmdline_credentials());
2601                 test_data->username = cli_credentials_get_username(
2602                                                 popt_get_cmdline_credentials());
2603                 test_data->real_username = cli_credentials_get_username(
2604                                                 popt_get_cmdline_credentials());
2605                 test_data->canonicalize = (i & TEST_CANONICALIZE) != 0;
2606                 test_data->enterprise = (i & TEST_ENTERPRISE) != 0;
2607                 test_data->upper_realm = (i & TEST_UPPER_REALM) != 0;
2608                 test_data->upper_username = (i & TEST_UPPER_USERNAME) != 0;
2609                 test_data->netbios_realm = (i & TEST_NETBIOS_REALM) != 0;
2610                 test_data->win2k = (i & TEST_WIN2K) != 0;
2611                 test_data->upn = (i & TEST_UPN) != 0;
2612                 test_data->s4u2self = (i & TEST_S4U2SELF) != 0;
2613                 test_data->mitm_s4u2self = (i & TEST_MITM_S4U2SELF) != 0;
2614                 test_data->removedollar = (i & TEST_REMOVEDOLLAR) != 0;
2615                 test_data->as_req_spn = (i & TEST_AS_REQ_SPN) != 0;
2616                 torture_suite_add_simple_tcase_const(sub_suite, name, torture_krb5_as_req_canon,
2617                                                      test_data);
2618                 torture_suite_add_suite(suite, sub_suite);
2619
2620         }
2621         return suite;
2622 }