s4:torture: Remove netbios realm and lowercase realm tests
[samba.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/cmdline.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 #undef strcasecmp
38
39 #define TEST_CANONICALIZE     0x0000001
40 #define TEST_ENTERPRISE       0x0000002
41 #define TEST_UPPER_USERNAME   0x0000008
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_ALL              0x00003FF
48
49 struct test_data {
50         const char *test_name;
51         const char *realm;
52         const char *real_realm;
53         const char *real_domain;
54         const char *username;
55         const char *real_username;
56         bool canonicalize;
57         bool enterprise;
58         bool upper_username;
59         bool win2k;
60         bool upn;
61         bool other_upn_suffix;
62         bool s4u2self;
63         bool removedollar;
64         bool as_req_spn;
65         bool spn_is_upn;
66         const char *krb5_service;
67         const char *krb5_hostname;
68 };
69
70 struct torture_krb5_context {
71         struct smb_krb5_context *smb_krb5_context;
72         struct torture_context *tctx;
73         struct addrinfo *server;
74         struct test_data *test_data;
75         int packet_count;
76 };
77
78 struct pac_data {
79         const char *principal_name;
80 };
81
82 /*
83  * A helper function which avoids touching the local databases to
84  * generate the session info, as we just want to verify the principal
85  * name that we found in the ticket not the full local token
86  */
87 static NTSTATUS test_generate_session_info_pac(struct auth4_context *auth_ctx,
88                                                TALLOC_CTX *mem_ctx,
89                                                struct smb_krb5_context *smb_krb5_context,
90                                                DATA_BLOB *pac_blob,
91                                                const char *principal_name,
92                                                const struct tsocket_address *remote_address,
93                                                uint32_t session_info_flags,
94                                                struct auth_session_info **session_info)
95 {
96         NTSTATUS nt_status;
97         struct auth_user_info_dc *user_info_dc;
98         TALLOC_CTX *tmp_ctx;
99         struct pac_data *pac_data;
100
101         tmp_ctx = talloc_named(mem_ctx, 0, "gensec_gssapi_session_info context");
102         NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
103
104         auth_ctx->private_data = pac_data = talloc_zero(auth_ctx, struct pac_data);
105
106         pac_data->principal_name = talloc_strdup(pac_data, principal_name);
107         if (!pac_data->principal_name) {
108                 talloc_free(tmp_ctx);
109                 return NT_STATUS_NO_MEMORY;
110         }
111
112         nt_status = kerberos_pac_blob_to_user_info_dc(tmp_ctx,
113                                                       *pac_blob,
114                                                       smb_krb5_context->krb5_context,
115                                                       &user_info_dc, NULL, NULL);
116         if (!NT_STATUS_IS_OK(nt_status)) {
117                 talloc_free(tmp_ctx);
118                 return nt_status;
119         }
120
121         if (user_info_dc->info->authenticated) {
122                 session_info_flags |= AUTH_SESSION_INFO_AUTHENTICATED;
123         }
124
125         session_info_flags |= AUTH_SESSION_INFO_SIMPLE_PRIVILEGES;
126         nt_status = auth_generate_session_info(mem_ctx,
127                                                NULL,
128                                                NULL,
129                                                user_info_dc, session_info_flags,
130                                                session_info);
131         if (!NT_STATUS_IS_OK(nt_status)) {
132                 talloc_free(tmp_ctx);
133                 return nt_status;
134         }
135
136         talloc_free(tmp_ctx);
137         return NT_STATUS_OK;
138 }
139
140 /* Check to see if we can pass the PAC across to the NETLOGON server for validation */
141
142 /* Also happens to be a really good one-step verfication of our Kerberos stack */
143
144 static bool test_accept_ticket(struct torture_context *tctx,
145                                struct cli_credentials *credentials,
146                                const char *principal,
147                                DATA_BLOB client_to_server)
148 {
149         NTSTATUS status;
150         struct gensec_security *gensec_server_context;
151         DATA_BLOB server_to_client;
152         struct auth4_context *auth_context;
153         struct auth_session_info *session_info;
154         struct pac_data *pac_data;
155         TALLOC_CTX *tmp_ctx = talloc_new(tctx);
156
157         torture_assert(tctx, tmp_ctx != NULL, "talloc_new() failed");
158
159         auth_context = talloc_zero(tmp_ctx, struct auth4_context);
160         torture_assert(tctx, auth_context != NULL, "talloc_new() failed");
161
162         auth_context->generate_session_info_pac = test_generate_session_info_pac;
163
164         status = gensec_server_start(tctx,
165                                      lpcfg_gensec_settings(tctx, tctx->lp_ctx),
166                                      auth_context, &gensec_server_context);
167         torture_assert_ntstatus_ok(tctx, status, "gensec_server_start (server) failed");
168
169         status = gensec_set_credentials(gensec_server_context, credentials);
170         torture_assert_ntstatus_ok(tctx, status, "gensec_set_credentials (server) failed");
171
172         status = gensec_start_mech_by_name(gensec_server_context, "krb5");
173         torture_assert_ntstatus_ok(tctx, status, "gensec_start_mech_by_name (server) failed");
174
175         server_to_client = data_blob(NULL, 0);
176
177         /* Do a client-server update dance */
178         status = gensec_update(gensec_server_context, tmp_ctx, client_to_server, &server_to_client);
179         torture_assert_ntstatus_ok(tctx, status, "gensec_update (server) failed");
180
181         /* Extract the PAC using Samba's code */
182
183         status = gensec_session_info(gensec_server_context, gensec_server_context, &session_info);
184         torture_assert_ntstatus_ok(tctx, status, "gensec_session_info failed");
185
186         pac_data = talloc_get_type(auth_context->private_data, struct pac_data);
187
188         torture_assert(tctx, pac_data != NULL, "gensec_update failed to fill in pac_data in auth_context");
189         torture_assert(tctx, pac_data->principal_name != NULL, "principal_name not present");
190         torture_assert_str_equal(tctx, pac_data->principal_name, principal, "wrong principal name");
191         return true;
192 }
193
194 /*
195  * This function is set in torture_krb5_init_context_canon as krb5
196  * send_and_recv function.  This allows us to override what server the
197  * test is aimed at, and to inspect the packets just before they are
198  * sent to the network, and before they are processed on the recv
199  * side.
200  *
201  */
202 static krb5_error_code smb_krb5_send_and_recv_func_canon_override(krb5_context context,
203                                                                    void *data, /* struct torture_krb5_context */
204                                                                    krb5_krbhst_info *hi,
205                                                                    time_t timeout,
206                                                                    const krb5_data *send_buf,
207                                                                    krb5_data *recv_buf)
208 {
209         krb5_error_code k5ret;
210
211         struct torture_krb5_context *test_context
212                 = talloc_get_type_abort(data, struct torture_krb5_context);
213
214         k5ret = smb_krb5_send_and_recv_func_forced(context, test_context->server,
215                                                    hi, timeout, send_buf,
216                                                    recv_buf);
217         if (k5ret != 0) {
218                 return k5ret;
219         }
220
221         test_context->packet_count++;
222
223         return k5ret;
224 }
225
226 static int test_context_destructor(struct torture_krb5_context *test_context)
227 {
228         freeaddrinfo(test_context->server);
229         return 0;
230 }
231
232
233 static bool torture_krb5_init_context_canon(struct torture_context *tctx,
234                                              struct test_data *test_data,
235                                              struct torture_krb5_context **torture_krb5_context)
236 {
237         const char *host = torture_setting_string(tctx, "host", NULL);
238         krb5_error_code k5ret;
239         bool ok;
240
241         struct torture_krb5_context *test_context = talloc_zero(tctx, struct torture_krb5_context);
242         torture_assert(tctx, test_context != NULL, "Failed to allocate");
243
244         test_context->test_data = test_data;
245         test_context->tctx = tctx;
246
247         k5ret = smb_krb5_init_context(test_context, tctx->lp_ctx, &test_context->smb_krb5_context);
248         torture_assert_int_equal(tctx, k5ret, 0, "smb_krb5_init_context failed");
249
250         ok = interpret_string_addr_internal(&test_context->server, host, AI_NUMERICHOST);
251         torture_assert(tctx, ok, "Failed to parse target server");
252
253         talloc_set_destructor(test_context, test_context_destructor);
254
255         set_sockaddr_port(test_context->server->ai_addr, 88);
256
257         k5ret = krb5_set_send_to_kdc_func(test_context->smb_krb5_context->krb5_context,
258                                           smb_krb5_send_and_recv_func_canon_override,
259                                           test_context);
260         torture_assert_int_equal(tctx, k5ret, 0, "krb5_set_send_to_kdc_func failed");
261         *torture_krb5_context = test_context;
262         return true;
263 }
264
265
266 static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void *tcase_data)
267 {
268         krb5_error_code k5ret;
269         krb5_get_init_creds_opt *krb_options = NULL;
270         struct test_data *test_data = talloc_get_type_abort(tcase_data, struct test_data);
271         krb5_principal principal;
272         krb5_principal krbtgt_other;
273         krb5_principal expected_principal;
274         const char *principal_string = NULL;
275         char *krbtgt_other_string;
276         int principal_flags;
277         const char *expected_principal_string = NULL;
278         char *expected_unparse_principal_string;
279         int expected_principal_flags;
280         char *got_principal_string;
281         char *assertion_message;
282         const char *password = cli_credentials_get_password(
283                         samba_cmdline_get_creds());
284         krb5_context k5_context;
285         struct torture_krb5_context *test_context;
286         bool ok;
287         krb5_creds my_creds;
288         krb5_creds *server_creds;
289         krb5_ccache ccache;
290         krb5_auth_context auth_context;
291         char *cc_name;
292         krb5_data in_data, enc_ticket;
293         krb5_get_creds_opt opt;
294
295         const char *spn = NULL;
296         const char *spn_real_realm = NULL;
297         const char *upn = torture_setting_string(tctx, "krb5-upn", "");
298         test_data->krb5_service = torture_setting_string(tctx, "krb5-service", "host");
299         test_data->krb5_hostname = torture_setting_string(tctx, "krb5-hostname", "");
300
301         /*
302          * If we have not passed a UPN on the command line,
303          * then skip the UPN tests.
304          */
305         if (test_data->upn && upn[0] == '\0') {
306                 torture_skip(tctx, "This test needs a UPN specified as --option=torture:krb5-upn=user@example.com to run");
307         }
308
309         /*
310          * If we have not passed a SPN on the command line,
311          * then skip the SPN tests.
312          */
313         if (test_data->as_req_spn && test_data->krb5_hostname[0] == '\0') {
314                 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");
315         }
316
317         if (test_data->removedollar &&
318             !torture_setting_bool(tctx, "run_removedollar_test", false))
319         {
320                 torture_skip(tctx, "--option=torture:run_removedollar_test=true not specified");
321         }
322
323         test_data->realm = test_data->real_realm;
324
325         if (test_data->upn) {
326                 char *p;
327                 test_data->username = talloc_strdup(test_data, upn);
328                 p = strchr(test_data->username, '@');
329                 if (p) {
330                         *p = '\0';
331                         p++;
332                 }
333                 /*
334                  * Test the UPN behaviour carefully.  We can
335                  * test in two different modes, depending on
336                  * what UPN has been set up for us.
337                  *
338                  * If the UPN is in our realm, then we do all the tests with this name also.
339                  *
340                  * If the UPN is not in our realm, then we
341                  * expect the tests that replace the realm to
342                  * fail (as it won't match)
343                  */
344                 if (strcasecmp(p, test_data->real_realm) != 0) {
345                         test_data->other_upn_suffix = true;
346                 } else {
347                         test_data->other_upn_suffix = false;
348                 }
349
350                 /*
351                  * This lets us test the combination of the UPN prefix
352                  * with a valid domain, without adding even more
353                  * combinations
354                  */
355                 test_data->realm = p;
356         }
357
358         ok = torture_krb5_init_context_canon(tctx, test_data, &test_context);
359         torture_assert(tctx, ok, "torture_krb5_init_context failed");
360         k5_context = test_context->smb_krb5_context->krb5_context;
361
362         test_data->realm = strupper_talloc(test_data, test_data->realm);
363         if (test_data->upper_username) {
364                 test_data->username = strupper_talloc(test_data, test_data->username);
365         } else {
366                 test_data->username = talloc_strdup(test_data, test_data->username);
367         }
368
369         if (test_data->removedollar) {
370                 char *p;
371
372                 p = strchr_m(test_data->username, '$');
373                 torture_assert(tctx, p != NULL, talloc_asprintf(tctx,
374                                "username[%s] contains no '$'\n",
375                                test_data->username));
376                 *p = '\0';
377         }
378
379         spn = talloc_asprintf(test_data, "%s/%s@%s",
380                               test_data->krb5_service,
381                               test_data->krb5_hostname,
382                               test_data->realm);
383
384         spn_real_realm = talloc_asprintf(test_data, "%s/%s@%s",
385                                          test_data->krb5_service,
386                                          test_data->krb5_hostname,
387                                          test_data->real_realm);
388
389         if (!test_data->canonicalize && test_data->enterprise) {
390                 torture_skip(tctx,
391                              "This test combination "
392                              "is skipped intentionally");
393         }
394
395         if (test_data->as_req_spn) {
396                 if (test_data->enterprise) {
397                         torture_skip(tctx,
398                                      "This test combination "
399                                      "is skipped intentionally");
400                 }
401                 principal_string = spn;
402         } else {
403                 principal_string = talloc_asprintf(test_data,
404                                                    "%s@%s",
405                                                    test_data->username,
406                                                    test_data->realm);
407                 
408         }
409
410         test_data->spn_is_upn
411                 = (strcasecmp(upn, spn) == 0);
412                                 
413         /*
414          * If we are set to canonicalize, we get back the fixed UPPER
415          * case realm, and the real username (ie matching LDAP
416          * samAccountName)
417          *
418          * Otherwise, if we are set to enterprise, we
419          * get back the whole principal as-sent
420          *
421          * Finally, if we are not set to canonicalize, we get back the
422          * fixed UPPER case realm, but the as-sent username
423          */
424         if (test_data->as_req_spn && !test_data->spn_is_upn) {
425                 expected_principal_string = spn;
426         } else if (test_data->canonicalize) {
427                 expected_principal_string = talloc_asprintf(test_data,
428                                                             "%s@%s",
429                                                             test_data->real_username,
430                                                             test_data->real_realm);
431         } else if (test_data->as_req_spn && test_data->spn_is_upn) {
432                 expected_principal_string = spn_real_realm;
433         } else {
434                 expected_principal_string = talloc_asprintf(test_data,
435                                                             "%s@%s",
436                                                             test_data->username,
437                                                             test_data->real_realm);
438         }
439
440         if (test_data->enterprise) {
441                 principal_flags = KRB5_PRINCIPAL_PARSE_ENTERPRISE;
442         } else {
443                 if (test_data->upn && test_data->other_upn_suffix) {
444                         torture_skip(tctx, "UPN test for UPN with other UPN suffix only runs with enterprise principals");
445                 }
446                 principal_flags = 0;
447         }
448
449         if (test_data->canonicalize) {
450                 expected_principal_flags = 0;
451         } else {
452                 expected_principal_flags = principal_flags;
453         }
454
455         torture_assert_int_equal(tctx,
456                                  krb5_parse_name_flags(k5_context,
457                                                        principal_string,
458                                                        principal_flags,
459                                                        &principal),
460                                          0, "krb5_parse_name_flags failed");
461         torture_assert_int_equal(tctx,
462                                  krb5_parse_name_flags(k5_context,
463                                                        expected_principal_string,
464                                                        expected_principal_flags,
465                                                        &expected_principal),
466                                  0, "krb5_parse_name_flags failed");
467         
468         if (test_data->as_req_spn) {
469                 if (test_data->upn) {
470                         krb5_principal_set_type(k5_context,
471                                                 principal,
472                                                 KRB5_NT_PRINCIPAL);
473                         krb5_principal_set_type(k5_context,
474                                                 expected_principal,
475                                                 KRB5_NT_PRINCIPAL);
476                 } else {
477                         krb5_principal_set_type(k5_context,
478                                                 principal,
479                                                 KRB5_NT_SRV_HST);
480                         krb5_principal_set_type(k5_context,
481                                                 expected_principal,
482                                                 KRB5_NT_SRV_HST);
483                 }
484         }
485         
486         torture_assert_int_equal(tctx,
487                                  krb5_unparse_name(k5_context,
488                                                    expected_principal,
489                                                    &expected_unparse_principal_string),
490                                  0, "krb5_unparse_name failed");
491         /*
492          * Prepare a AS-REQ and run the TEST_AS_REQ tests
493          *
494          */
495
496         test_context->packet_count = 0;
497
498         /*
499          * Set the canonicalize flag if this test requires it
500          */
501         torture_assert_int_equal(tctx,
502                                  krb5_get_init_creds_opt_alloc(k5_context, &krb_options),
503                                  0, "krb5_get_init_creds_opt_alloc failed");
504
505         torture_assert_int_equal(tctx,
506                                  krb5_get_init_creds_opt_set_canonicalize(k5_context,
507                                                                           krb_options,
508                                                                           test_data->canonicalize),
509                                  0, "krb5_get_init_creds_opt_set_canonicalize failed");
510
511         torture_assert_int_equal(tctx,
512                                  krb5_get_init_creds_opt_set_win2k(k5_context,
513                                                                    krb_options,
514                                                                    test_data->win2k),
515                                  0, "krb5_get_init_creds_opt_set_win2k failed");
516
517         k5ret = krb5_get_init_creds_password(k5_context, &my_creds, principal,
518                                              password, NULL, NULL, 0,
519                                              NULL, krb_options);
520
521         if (test_context->test_data->as_req_spn
522                    && !test_context->test_data->spn_is_upn) {
523                 torture_assert_int_equal(tctx, k5ret,
524                                          KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN,
525                                          "Got wrong error_code from "
526                                          "krb5_get_init_creds_password");
527                 /* We can't proceed with more checks */
528                 return true;
529         } else {
530                 assertion_message = talloc_asprintf(tctx,
531                                                     "krb5_get_init_creds_password for %s failed: %s",
532                                                     principal_string,
533                                                     smb_get_krb5_error_message(k5_context, k5ret, tctx));
534                 torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
535         }
536
537         torture_assert(tctx,
538                        test_context->packet_count > 1,
539                        "Expected krb5_get_init_creds_password to send more packets");
540
541         /*
542          * Assert that the reply was with the correct type of
543          * principal, depending on the flags we set
544          */
545         if (test_data->canonicalize == false && test_data->as_req_spn) {
546                 torture_assert_int_equal(tctx,
547                                          krb5_principal_get_type(k5_context,
548                                                                  my_creds.client),
549                                          KRB5_NT_SRV_HST,
550                                          "smb_krb5_init_context gave incorrect client->name.name_type");
551         } else {
552                 torture_assert_int_equal(tctx,
553                                          krb5_principal_get_type(k5_context,
554                                                                  my_creds.client),
555                                          KRB5_NT_PRINCIPAL,
556                                          "smb_krb5_init_context gave incorrect client->name.name_type");
557         }
558
559         torture_assert_int_equal(tctx,
560                                  krb5_unparse_name(k5_context,
561                                                    my_creds.client, &got_principal_string), 0,
562                                  "krb5_unparse_name failed");
563
564         assertion_message = talloc_asprintf(tctx,
565                                             "krb5_get_init_creds_password returned a different principal %s to what was expected %s",
566                                             got_principal_string, expected_principal_string);
567         krb5_free_unparsed_name(k5_context, got_principal_string);
568
569         torture_assert(tctx, krb5_principal_compare(k5_context,
570                                                     my_creds.client, expected_principal),
571                        assertion_message);
572
573
574         torture_assert_int_equal(tctx,
575                                  krb5_principal_get_type(k5_context,
576                                                          my_creds.server), KRB5_NT_SRV_INST,
577                                  "smb_krb5_init_context gave incorrect server->name.name_type");
578
579         torture_assert_int_equal(tctx,
580                                  krb5_principal_get_num_comp(k5_context,
581                                                              my_creds.server), 2,
582                                  "smb_krb5_init_context gave incorrect number of components in my_creds.server->name");
583
584         torture_assert_str_equal(tctx,
585                                  krb5_principal_get_comp_string(k5_context,
586                                                                 my_creds.server, 0),
587                                  "krbtgt",
588                                  "smb_krb5_init_context gave incorrect my_creds.server->name.name_string[0]");
589
590         if (test_data->canonicalize) {
591                 torture_assert_str_equal(tctx,
592                                          krb5_principal_get_comp_string(k5_context,
593                                                                         my_creds.server, 1),
594                                          test_data->real_realm,
595
596                                          "smb_krb5_init_context gave incorrect my_creds.server->name.name_string[1]");
597         } else {
598                 torture_assert_str_equal(tctx,
599                                          krb5_principal_get_comp_string(k5_context,
600                                                                         my_creds.server, 1),
601                                          test_data->realm,
602
603                                          "smb_krb5_init_context gave incorrect my_creds.server->name.name_string[1]");
604         }
605         torture_assert_str_equal(tctx,
606                                  krb5_principal_get_realm(k5_context,
607                                                           my_creds.server),
608                                  test_data->real_realm,
609                                  "smb_krb5_init_context gave incorrect my_creds.server->realm");
610
611         /* Store the result of the 'kinit' above into a memory ccache */
612         cc_name = talloc_asprintf(tctx, "MEMORY:%s", test_data->test_name);
613         torture_assert_int_equal(tctx, krb5_cc_resolve(k5_context, cc_name,
614                                                        &ccache),
615                                  0, "krb5_cc_resolve failed");
616
617         torture_assert_int_equal(tctx, krb5_cc_initialize(k5_context,
618                                                           ccache, my_creds.client),
619                                  0, "krb5_cc_initialize failed");
620
621         torture_assert_int_equal(tctx, krb5_cc_store_cred(k5_context,
622                                                           ccache, &my_creds),
623                                  0, "krb5_cc_store_cred failed");
624
625         /*
626          * Prepare a TGS-REQ and run the TEST_TGS_REQ_KRBTGT_CANON tests
627          *
628          * This tests krb5_get_creds behaviour, which allows us to set
629          * the KRB5_GC_CANONICALIZE option against the krbtgt/ principal
630          */
631
632         krbtgt_other_string = talloc_asprintf(test_data, "krbtgt/%s@%s", test_data->real_domain, test_data->real_realm);
633         torture_assert_int_equal(tctx,
634                                  krb5_make_principal(k5_context, &krbtgt_other,
635                                                      test_data->real_realm, "krbtgt",
636                                                      test_data->real_domain, NULL),
637                                  0, "krb5_make_principal failed");
638
639         test_context->packet_count = 0;
640
641         torture_assert_int_equal(tctx,
642                                  krb5_get_creds_opt_alloc(k5_context, &opt),
643                                  0, "krb5_get_creds_opt_alloc");
644
645         krb5_get_creds_opt_add_options(k5_context,
646                                        opt,
647                                        KRB5_GC_CANONICALIZE);
648
649         krb5_get_creds_opt_add_options(k5_context,
650                                        opt,
651                                        KRB5_GC_NO_STORE);
652
653         /* Confirm if we can get a ticket krbtgt/realm that we got back with the initial kinit */
654         k5ret = krb5_get_creds(k5_context, opt, ccache, krbtgt_other, &server_creds);
655
656         {
657                 /*
658                  * In these situations, the code above does not store a
659                  * principal in the credentials cache matching what
660                  * krb5_get_creds() needs without talking to the KDC, so the
661                  * test fails with looping detected because when we set
662                  * canonicalize we confuse the client libs.
663                  *
664                  */
665                 assertion_message = talloc_asprintf(tctx,
666                                                     "krb5_get_creds for %s should have failed with looping detected: %s",
667                                                     krbtgt_other_string,
668                                                     smb_get_krb5_error_message(k5_context, k5ret,
669                                                                                tctx));
670
671                 torture_assert_int_equal(tctx, k5ret, KRB5_GET_IN_TKT_LOOP, assertion_message);
672                 torture_assert_int_equal(tctx,
673                                          test_context->packet_count,
674                                          2, "Expected krb5_get_creds to send packets");
675         }
676
677         /*
678          * Prepare a TGS-REQ and run the TEST_TGS_REQ_CANON tests
679          *
680          * This tests krb5_get_creds behaviour, which allows us to set
681          * the KRB5_GC_CANONICALIZE option
682          */
683
684         test_context->packet_count = 0;
685
686         torture_assert_int_equal(tctx,
687                                  krb5_get_creds_opt_alloc(k5_context, &opt),
688                                  0, "krb5_get_creds_opt_alloc");
689
690         krb5_get_creds_opt_add_options(k5_context,
691                                        opt,
692                                        KRB5_GC_CANONICALIZE);
693
694         krb5_get_creds_opt_add_options(k5_context,
695                                        opt,
696                                        KRB5_GC_NO_STORE);
697
698         if (test_data->s4u2self) {
699                 torture_assert_int_equal(tctx,
700                                          krb5_get_creds_opt_set_impersonate(k5_context,
701                                                                             opt,
702                                                                             principal),
703                                          0, "krb5_get_creds_opt_set_impersonate failed");
704         }
705
706         /* Confirm if we can get a ticket to our own name */
707         k5ret = krb5_get_creds(k5_context, opt, ccache, principal, &server_creds);
708
709         /*
710          * In these situations, the code above does not store a
711          * principal in the credentials cache matching what
712          * krb5_get_creds() needs, so the test fails.
713          *
714          */
715         {
716                 assertion_message = talloc_asprintf(tctx,
717                                                     "krb5_get_creds for %s failed: %s",
718                                                     principal_string,
719                                                     smb_get_krb5_error_message(k5_context, k5ret,
720                                                                                tctx));
721
722                 /*
723                  * Only machine accounts (strictly, accounts with a
724                  * servicePrincipalName) can expect this test to succeed
725                  */
726                 if (torture_setting_bool(tctx, "expect_machine_account", false)
727                     && (test_data->enterprise
728                         || test_data->spn_is_upn
729                         || test_data->upn == false)) {
730                         torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
731                         torture_assert_int_equal(tctx, krb5_cc_store_cred(k5_context,
732                                                                           ccache, server_creds),
733                                                  0, "krb5_cc_store_cred failed");
734
735                         torture_assert_int_equal(tctx,
736                                                  krb5_free_creds(k5_context,
737                                                                  server_creds),
738                                                  0, "krb5_free_cred_contents failed");
739
740                 } else {
741                         torture_assert_int_equal(tctx, k5ret, KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN,
742                                                  assertion_message);
743                 }
744
745                 torture_assert_int_equal(tctx,
746                                          test_context->packet_count,
747                                          1, "Expected krb5_get_creds to send packets");
748         }
749
750         /*
751          * Confirm gettting a ticket to pass to the server, running
752          * either the TEST_TGS_REQ or TEST_SELF_TRUST_TGS_REQ stage.
753          *
754          * This triggers the client to attempt to get a
755          * cross-realm ticket between the alternate names of
756          * the server, and we need to confirm that behaviour.
757          *
758          */
759
760         test_context->packet_count = 0;
761         torture_assert_int_equal(tctx, krb5_auth_con_init(k5_context, &auth_context),
762                                  0, "krb5_auth_con_init failed");
763
764         in_data.length = 0;
765         k5ret = krb5_mk_req_exact(k5_context,
766                                   &auth_context,
767                                   AP_OPTS_USE_SUBKEY,
768                                   principal,
769                                   &in_data, ccache,
770                                   &enc_ticket);
771         assertion_message = talloc_asprintf(tctx,
772                                             "krb5_mk_req_exact for %s failed: %s",
773                                             principal_string,
774                                             smb_get_krb5_error_message(k5_context, k5ret, tctx));
775
776         /*
777          * Only machine accounts (strictly, accounts with a
778          * servicePrincipalName) can expect this test to succeed
779          */
780         if (torture_setting_bool(tctx, "expect_machine_account", false)
781             && (test_data->enterprise ||
782                 (test_context->test_data->as_req_spn 
783                  || test_context->test_data->spn_is_upn)
784                 || test_data->upn == false)) {
785                 DATA_BLOB client_to_server;
786                 torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
787                 client_to_server = data_blob_const(enc_ticket.data, enc_ticket.length);
788
789                 /* This is very weird */
790                 if (test_data->canonicalize == false
791                     && test_context->test_data->as_req_spn
792                     && test_context->test_data->spn_is_upn
793                     && test_context->test_data->s4u2self) {
794                         
795                         torture_assert(tctx,
796                                        test_accept_ticket(tctx,
797                                                           samba_cmdline_get_creds(),
798                                                           spn_real_realm,
799                                                           client_to_server),
800                                        "test_accept_ticket failed - failed to accept the ticket we just created");
801                 } else if (test_data->canonicalize == true
802                     && test_context->test_data->as_req_spn
803                     && test_context->test_data->spn_is_upn
804                     && test_context->test_data->s4u2self) {
805                         
806                         torture_assert(tctx,
807                                        test_accept_ticket(tctx,
808                                                           samba_cmdline_get_creds(),
809                                                           spn,
810                                                           client_to_server),
811                                        "test_accept_ticket failed - failed to accept the ticket we just created");
812                 } else if (test_data->canonicalize == true
813                            && test_data->enterprise == false
814                            && test_context->test_data->upn
815                            && test_context->test_data->spn_is_upn
816                            && test_context->test_data->s4u2self) {
817                         
818                         torture_assert(tctx,
819                                        test_accept_ticket(tctx,
820                                                           samba_cmdline_get_creds(),
821                                                           principal_string,
822                                                           client_to_server),
823                                        "test_accept_ticket failed - failed to accept the ticket we just created");
824                 } else if (test_data->canonicalize == false
825                            && test_context->test_data->upn
826                            && test_context->test_data->spn_is_upn
827                            && test_context->test_data->s4u2self) {
828                         
829                         const char *accept_expected_principal_string
830                                 = talloc_asprintf(test_data,
831                                                   "%s@%s",
832                                                   test_data->username,
833                                                   test_data->real_realm);
834                         
835                         torture_assert(tctx,
836                                        test_accept_ticket(tctx,
837                                                           samba_cmdline_get_creds(),
838                                                           accept_expected_principal_string,
839                                                           client_to_server),
840                                        "test_accept_ticket failed - failed to accept the ticket we just created");
841                 } else {
842                 
843                         torture_assert(tctx,
844                                        test_accept_ticket(tctx,
845                                                           samba_cmdline_get_creds(),
846                                                           expected_unparse_principal_string,
847                                                           client_to_server),
848                                        "test_accept_ticket failed - failed to accept the ticket we just created");
849                 }
850                 krb5_data_free(&enc_ticket);
851         } else {
852                 torture_assert_int_equal(tctx, k5ret, KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN,
853                                          assertion_message);
854         }
855
856         /*
857          * Confirm gettting a ticket to pass to the server, running
858          * the TEST_TGS_REQ_HOST, TEST_TGS_REQ_HOST_SRV_INST, TEST_TGS_REQ_HOST_SRV_HST stage
859          *
860          * This triggers the client to attempt to get a
861          * cross-realm ticket between the alternate names of
862          * the server, and we need to confirm that behaviour.
863          *
864          */
865
866         if (*test_data->krb5_service && *test_data->krb5_hostname) {
867                 krb5_principal host_principal_srv_inst;
868                 /*
869                  * This tries to guess when the krb5 libs will ask for a
870                  * cross-realm ticket, and when they will just ask the KDC
871                  * directly.
872                  */
873                 test_context->packet_count = 0;
874                 torture_assert_int_equal(tctx, krb5_auth_con_init(k5_context, &auth_context),
875                                          0, "krb5_auth_con_init failed");
876
877                 in_data.length = 0;
878                 k5ret = krb5_mk_req(k5_context,
879                                     &auth_context,
880                                     0,
881                                     test_data->krb5_service,
882                                     test_data->krb5_hostname,
883                                     &in_data, ccache,
884                                     &enc_ticket);
885
886                 {
887                         assertion_message = talloc_asprintf(tctx,
888                                                             "krb5_mk_req for %s/%s failed: %s",
889                                                             test_data->krb5_service,
890                                                             test_data->krb5_hostname,
891                                                             smb_get_krb5_error_message(k5_context, k5ret, tctx));
892
893                         torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
894
895                         if (test_data->spn_is_upn == false) {
896                                 /*
897                                  * Only in these cases would the above
898                                  * code have needed to send packets to
899                                  * the network
900                                  */
901                                 torture_assert(tctx,
902                                                test_context->packet_count > 0,
903                                                "Expected krb5_get_creds to send packets");
904                         }
905                 }
906
907
908                 test_context->packet_count = 0;
909
910                 torture_assert_int_equal(tctx,
911                                          krb5_make_principal(k5_context, &host_principal_srv_inst,
912                                                              test_data->real_realm,
913                                                              strupper_talloc(tctx, test_data->krb5_service),
914                                                              test_data->krb5_hostname,
915                                                              NULL),
916                                          0, "krb5_make_principal failed");
917
918                 krb5_principal_set_type(k5_context, host_principal_srv_inst, KRB5_NT_SRV_INST);
919
920                 torture_assert_int_equal(tctx, krb5_auth_con_init(k5_context, &auth_context),
921                                          0, "krb5_auth_con_init failed");
922
923                 in_data.length = 0;
924                 k5ret = krb5_mk_req_exact(k5_context,
925                                           &auth_context,
926                                           0,
927                                           host_principal_srv_inst,
928                                           &in_data, ccache,
929                                           &enc_ticket);
930                 krb5_free_principal(k5_context, host_principal_srv_inst);
931                 {
932                         assertion_message = talloc_asprintf(tctx,
933                                                             "krb5_mk_req for %s/%s KRB5_NT_SRV_INST failed: %s",
934                                                             test_data->krb5_service,
935                                                             test_data->krb5_hostname,
936                                                             smb_get_krb5_error_message(k5_context, k5ret, tctx));
937
938                         torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
939                         /*
940                          * Only in these cases would the above code have needed to
941                          * send packets to the network
942                          */
943                         torture_assert(tctx,
944                                        test_context->packet_count > 0,
945                                        "Expected krb5_get_creds to send packets");
946                 }
947
948
949                 test_context->packet_count = 0;
950
951                 torture_assert_int_equal(tctx,
952                                          krb5_make_principal(k5_context, &host_principal_srv_inst,
953                                                              test_data->real_realm,
954                                                              test_data->krb5_service,
955                                                              strupper_talloc(tctx, test_data->krb5_hostname),
956                                                              NULL),
957                                          0, "krb5_make_principal failed");
958
959                 krb5_principal_set_type(k5_context, host_principal_srv_inst, KRB5_NT_SRV_HST);
960
961                 torture_assert_int_equal(tctx, krb5_auth_con_init(k5_context, &auth_context),
962                                          0, "krb5_auth_con_init failed");
963
964                 in_data.length = 0;
965                 k5ret = krb5_mk_req_exact(k5_context,
966                                           &auth_context,
967                                           0,
968                                           host_principal_srv_inst,
969                                           &in_data, ccache,
970                                           &enc_ticket);
971                 krb5_free_principal(k5_context, host_principal_srv_inst);
972                 {
973                         assertion_message = talloc_asprintf(tctx,
974                                                             "krb5_mk_req for %s/%s KRB5_NT_SRV_INST failed: %s",
975                                                             test_data->krb5_service,
976                                                             test_data->krb5_hostname,
977                                                             smb_get_krb5_error_message(k5_context, k5ret, tctx));
978
979                         torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
980                         /*
981                          * Only in these cases would the above code have needed to
982                          * send packets to the network
983                          */
984                         torture_assert(tctx,
985                                        test_context->packet_count > 0,
986                                        "Expected krb5_get_creds to send packets");
987                 }
988         }
989
990         /*
991          * Confirm gettting a ticket for the same krbtgt/realm that we
992          * got back with the initial ticket, running the
993          * TEST_TGS_REQ_KRBTGT stage.
994          *
995          */
996
997         test_context->packet_count = 0;
998
999         in_data.length = 0;
1000         k5ret = krb5_mk_req_exact(k5_context,
1001                                   &auth_context,
1002                                   0,
1003                                   my_creds.server,
1004                                   &in_data, ccache,
1005                                   &enc_ticket);
1006
1007         assertion_message = talloc_asprintf(tctx,
1008                                             "krb5_mk_req_exact for %s failed: %s",
1009                                             principal_string,
1010                                             smb_get_krb5_error_message(k5_context, k5ret, tctx));
1011         torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
1012
1013         krb5_free_principal(k5_context, principal);
1014         krb5_get_init_creds_opt_free(k5_context, krb_options);
1015
1016         torture_assert_int_equal(tctx, krb5_free_cred_contents(k5_context, &my_creds),
1017                                  0, "krb5_free_cred_contents failed");
1018
1019         return true;
1020 }
1021
1022 struct torture_suite *torture_krb5_canon(TALLOC_CTX *mem_ctx)
1023 {
1024         unsigned int i;
1025         struct torture_suite *suite = torture_suite_create(mem_ctx, "canon");
1026         suite->description = talloc_strdup(suite, "Kerberos Canonicalisation tests");
1027
1028         for (i = 0; i < TEST_ALL; i++) {
1029                 char *name = talloc_asprintf(suite, "%s.%s.%s.%s.%s.%s",
1030                                              (i & TEST_CANONICALIZE) ? "canon" : "no-canon",
1031                                              (i & TEST_ENTERPRISE) ? "enterprise" : "no-enterprise",
1032                                              (i & TEST_UPPER_USERNAME) ? "uc-user" : "lc-user",
1033                                              (i & TEST_WIN2K) ? "win2k" : "no-win2k",
1034                                              (i & TEST_UPN) ? "upn" :
1035                                              ((i & TEST_AS_REQ_SPN) ? "spn" : 
1036                                               ((i & TEST_REMOVEDOLLAR) ? "removedollar" : "samaccountname")),
1037                                              (i & TEST_S4U2SELF) ? "s4u2self" : "normal");
1038                 struct torture_suite *sub_suite = torture_suite_create(mem_ctx, name);
1039
1040                 struct test_data *test_data = talloc_zero(suite, struct test_data);
1041                 if (i & TEST_UPN) {
1042                         if (i & TEST_AS_REQ_SPN) {
1043                                 continue;
1044                         }
1045                 }
1046                 if ((i & TEST_UPN) || (i & TEST_AS_REQ_SPN)) {
1047                         if (i & TEST_REMOVEDOLLAR) {
1048                                 continue;
1049                         }
1050                 }
1051                 
1052                 test_data->test_name = name;
1053                 test_data->real_realm
1054                         = strupper_talloc(test_data,
1055                                 cli_credentials_get_realm(
1056                                         samba_cmdline_get_creds()));
1057                 test_data->real_domain = cli_credentials_get_domain(
1058                                                 samba_cmdline_get_creds());
1059                 test_data->username = cli_credentials_get_username(
1060                                                 samba_cmdline_get_creds());
1061                 test_data->real_username = cli_credentials_get_username(
1062                                                 samba_cmdline_get_creds());
1063                 test_data->canonicalize = (i & TEST_CANONICALIZE) != 0;
1064                 test_data->enterprise = (i & TEST_ENTERPRISE) != 0;
1065                 test_data->upper_username = (i & TEST_UPPER_USERNAME) != 0;
1066                 test_data->win2k = (i & TEST_WIN2K) != 0;
1067                 test_data->upn = (i & TEST_UPN) != 0;
1068                 test_data->s4u2self = (i & TEST_S4U2SELF) != 0;
1069                 test_data->removedollar = (i & TEST_REMOVEDOLLAR) != 0;
1070                 test_data->as_req_spn = (i & TEST_AS_REQ_SPN) != 0;
1071                 torture_suite_add_simple_tcase_const(sub_suite, name, torture_krb5_as_req_canon,
1072                                                      test_data);
1073                 torture_suite_add_suite(suite, sub_suite);
1074
1075         }
1076         return suite;
1077 }