torture-krb5: Add tests for combinations of enterprise, cannon, and different input...
[Samba.git] / source4 / torture / krb5 / kdc-canon.c
blob53a3b6a8d022044087bb3e98699bbef449363284
1 /*
2 Unix SMB/CIFS implementation.
4 Validate the krb5 pac generation routines
6 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2015
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.
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.
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/>.
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"
33 #define TEST_CANONICALIZE 0x0000001
34 #define TEST_ENTERPRISE 0x0000002
35 #define TEST_UPPER_REALM 0x0000004
36 #define TEST_UPPER_USERNAME 0x0000008
37 #define TEST_NETBIOS_REALM 0x0000010
38 #define TEST_ALL 0x000001F
40 struct test_data {
41 struct smb_krb5_context *smb_krb5_context;
42 const char *realm;
43 const char *real_realm;
44 const char *username;
45 bool canonicalize;
46 bool enterprise;
47 bool upper_realm;
48 bool upper_username;
49 };
51 struct torture_krb5_context {
52 struct torture_context *tctx;
53 struct addrinfo *server;
54 struct test_data *test_data;
55 int packet_count;
56 AS_REQ as_req;
57 AS_REP as_rep;
60 static bool torture_krb5_pre_send_test(struct torture_krb5_context *test_context, const krb5_data *send_buf, krb5_data *modified_send_buf)
62 krb5_error_code k5ret;
63 size_t used;
64 torture_assert_int_equal(test_context->tctx,
65 decode_AS_REQ(send_buf->data, send_buf->length, &test_context->as_req, &used), 0,
66 "decode_AS_REQ failed");
68 torture_assert_int_equal(test_context->tctx, used, send_buf->length, "length mismatch");
69 torture_assert_int_equal(test_context->tctx, test_context->as_req.pvno, 5, "Got wrong as_req->pvno");
70 if (test_context->test_data->canonicalize || test_context->test_data->enterprise) {
71 torture_assert(test_context->tctx, test_context->as_req.req_body.kdc_options.canonicalize, "krb5 libs did not set canonicalize!");
72 } else {
73 torture_assert_int_equal(test_context->tctx, test_context->as_req.req_body.kdc_options.canonicalize, false, "krb5 libs unexpectedly set canonicalize!");
76 if (test_context->test_data->enterprise) {
77 torture_assert_int_equal(test_context->tctx, test_context->as_req.req_body.cname->name_type, KRB5_NT_ENTERPRISE_PRINCIPAL, "krb5 libs did not pass principal as enterprise!");
78 } else {
79 torture_assert_int_equal(test_context->tctx, test_context->as_req.req_body.cname->name_type, KRB5_NT_PRINCIPAL, "krb5 libs unexpectedly set principal as enterprise!");
82 /* Force off canonicalize that was forced on by the krb5 libs */
83 if (test_context->test_data->canonicalize == false && test_context->test_data->enterprise) {
84 test_context->as_req.req_body.kdc_options.canonicalize = false;
87 ASN1_MALLOC_ENCODE(AS_REQ, modified_send_buf->data, modified_send_buf->length,
88 &test_context->as_req, &used, k5ret);
89 torture_assert_int_equal(test_context->tctx,
90 k5ret, 0,
91 "encode_AS_REQ failed");
92 torture_assert_int_equal(test_context->tctx, used, send_buf->length, "re-encode length mismatch");
93 return true;
96 static bool torture_krb5_post_recv_test(struct torture_krb5_context *test_context, const krb5_data *recv_buf)
98 KRB_ERROR error;
99 size_t used;
100 if (test_context->packet_count == 0) {
101 torture_assert_int_equal(test_context->tctx,
102 decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used), 0,
103 "decode_AS_REP failed");
104 torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch");
105 torture_assert_int_equal(test_context->tctx, error.pvno, 5, "Got wrong error.pvno");
106 torture_assert_int_equal(test_context->tctx, error.error_code, KRB5KDC_ERR_PREAUTH_REQUIRED - KRB5KDC_ERR_NONE,
107 "Got wrong error.error_code");
108 free_KRB_ERROR(&error);
109 } else if ((decode_KRB_ERROR(recv_buf->data, recv_buf->length, &error, &used) == 0)
110 && (test_context->packet_count == 1)) {
111 torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch");
112 torture_assert_int_equal(test_context->tctx, error.pvno, 5, "Got wrong error.pvno");
113 torture_assert_int_equal(test_context->tctx, error.error_code, KRB5KRB_ERR_RESPONSE_TOO_BIG - KRB5KDC_ERR_NONE,
114 "Got wrong error.error_code");
115 free_KRB_ERROR(&error);
116 } else {
117 torture_assert_int_equal(test_context->tctx,
118 decode_AS_REP(recv_buf->data, recv_buf->length, &test_context->as_rep, &used), 0,
119 "decode_AS_REP failed");
120 torture_assert_int_equal(test_context->tctx, used, recv_buf->length, "length mismatch");
121 torture_assert_int_equal(test_context->tctx,
122 test_context->as_rep.pvno, 5,
123 "Got wrong as_rep->pvno");
124 torture_assert_int_equal(test_context->tctx,
125 test_context->as_rep.ticket.tkt_vno, 5,
126 "Got wrong as_rep->ticket.tkt_vno");
127 torture_assert(test_context->tctx,
128 test_context->as_rep.ticket.enc_part.kvno,
129 "Did not get a KVNO in test_context->as_rep.ticket.enc_part.kvno");
130 if (torture_setting_bool(test_context->tctx, "expect_rodc", false)) {
131 torture_assert_int_not_equal(test_context->tctx,
132 *test_context->as_rep.ticket.enc_part.kvno & 0xFFFF0000,
133 0, "Did not get a RODC number in the KVNO");
134 } else {
135 torture_assert_int_equal(test_context->tctx,
136 *test_context->as_rep.ticket.enc_part.kvno & 0xFFFF0000,
137 0, "Unexpecedly got a RODC number in the KVNO");
139 free_AS_REP(&test_context->as_rep);
141 torture_assert(test_context->tctx, test_context->packet_count < 3, "too many packets");
142 free_AS_REQ(&test_context->as_req);
143 return true;
146 static krb5_error_code smb_krb5_send_and_recv_func_canon_override(krb5_context context,
147 void *data, /* struct torture_krb5_context */
148 krb5_krbhst_info *hi,
149 time_t timeout,
150 const krb5_data *send_buf,
151 krb5_data *recv_buf)
153 krb5_error_code k5ret;
154 bool ok;
155 krb5_data modified_send_buf;
157 struct torture_krb5_context *test_context
158 = talloc_get_type_abort(data, struct torture_krb5_context);
160 ok = torture_krb5_pre_send_test(test_context, send_buf, &modified_send_buf);
161 if (ok == false) {
162 return EINVAL;
165 k5ret = smb_krb5_send_and_recv_func_forced(context, test_context->server,
166 hi, timeout, &modified_send_buf, recv_buf);
168 ok = torture_krb5_post_recv_test(test_context, recv_buf);
169 if (ok == false) {
170 return EINVAL;
173 test_context->packet_count++;
175 return k5ret;
178 static int test_context_destructor(struct torture_krb5_context *test_context)
180 freeaddrinfo(test_context->server);
181 return 0;
185 static bool torture_krb5_init_context_canon(struct torture_context *tctx,
186 struct test_data *test_data,
187 struct smb_krb5_context **smb_krb5_context)
189 const char *host = torture_setting_string(tctx, "host", NULL);
190 krb5_error_code k5ret;
191 bool ok;
193 struct torture_krb5_context *test_context = talloc_zero(tctx, struct torture_krb5_context);
194 torture_assert(tctx, test_context != NULL, "Failed to allocate");
196 test_context->test_data = test_data;
197 test_context->tctx = tctx;
199 k5ret = smb_krb5_init_context(tctx, tctx->lp_ctx, smb_krb5_context);
200 torture_assert_int_equal(tctx, k5ret, 0, "smb_krb5_init_context failed");
202 ok = interpret_string_addr_internal(&test_context->server, host, AI_NUMERICHOST);
203 torture_assert(tctx, ok, "Failed to parse target server");
205 talloc_set_destructor(test_context, test_context_destructor);
207 set_sockaddr_port(test_context->server->ai_addr, 88);
209 k5ret = krb5_set_send_to_kdc_func((*smb_krb5_context)->krb5_context,
210 smb_krb5_send_and_recv_func_canon_override,
211 test_context);
212 torture_assert_int_equal(tctx, k5ret, 0, "krb5_set_send_to_kdc_func failed");
213 return true;
217 static bool torture_krb5_as_req_canon(struct torture_context *tctx, const void *tcase_data)
219 krb5_error_code k5ret;
220 krb5_get_init_creds_opt *krb_options = NULL;
221 struct test_data *test_data = talloc_get_type_abort(tcase_data, struct test_data);
222 char *realm;
223 char *upper_real_realm;
224 char *username;
225 krb5_principal principal;
226 krb5_principal expected_principal;
227 char *principal_string;
228 int principal_flags;
229 char *expected_principal_string;
230 int expected_principal_flags;
231 char *got_principal_string;
232 char *assertion_message;
233 const char *password = cli_credentials_get_password(cmdline_credentials);
234 struct smb_krb5_context *smb_krb5_context;
235 bool ok;
236 krb5_creds my_creds;
238 ok = torture_krb5_init_context_canon(tctx, test_data, &smb_krb5_context);
239 torture_assert(tctx, ok, "torture_krb5_init_context failed");
241 if (test_data->upper_realm) {
242 realm = strupper_talloc(test_data, test_data->realm);
243 } else {
244 realm = strlower_talloc(test_data, test_data->realm);
246 if (test_data->upper_username) {
247 username = strupper_talloc(test_data, test_data->username);
248 } else {
249 username = talloc_strdup(test_data, test_data->username);
252 principal_string = talloc_asprintf(test_data, "%s@%s", username, realm);
254 upper_real_realm = strupper_talloc(test_data, test_data->real_realm);
257 * If we are set to canonicalize, we get back the fixed UPPER
258 * case realm, and the real username (ie matching LDAP
259 * samAccountName)
261 * Otherwise, if we are set to enterprise, we
262 * get back the whole principal as-sent
264 * Finally, if we are not set to canonicalize, we get back the
265 * fixed UPPER case realm, but the as-sent username
267 if (test_data->canonicalize) {
268 expected_principal_string = talloc_asprintf(test_data, "%s@%s", test_data->username, upper_real_realm);
269 } else if (test_data->enterprise) {
270 expected_principal_string = principal_string;
271 } else {
272 expected_principal_string = talloc_asprintf(test_data, "%s@%s", username, upper_real_realm);
275 if (test_data->enterprise) {
276 principal_flags = KRB5_PRINCIPAL_PARSE_ENTERPRISE;
277 } else {
278 principal_flags = 0;
281 if (test_data->canonicalize) {
282 expected_principal_flags = 0;
283 } else {
284 expected_principal_flags = principal_flags;
287 torture_assert_int_equal(tctx,
288 krb5_parse_name_flags(smb_krb5_context->krb5_context,
289 principal_string,
290 principal_flags,
291 &principal),
292 0, "krb5_parse_name_flags failed");
293 torture_assert_int_equal(tctx,
294 krb5_parse_name_flags(smb_krb5_context->krb5_context,
295 expected_principal_string,
296 expected_principal_flags,
297 &expected_principal),
298 0, "krb5_parse_name_flags failed");
301 * Set the canonicalize flag if this test requires it
303 torture_assert_int_equal(tctx,
304 krb5_get_init_creds_opt_alloc(smb_krb5_context->krb5_context, &krb_options),
305 0, "krb5_get_init_creds_opt_alloc failed");
307 torture_assert_int_equal(tctx,
308 krb5_get_init_creds_opt_set_canonicalize(smb_krb5_context->krb5_context, krb_options, test_data->canonicalize),
309 0, "krb5_get_init_creds_opt_set_canonicalize failed");
311 k5ret = krb5_get_init_creds_password(smb_krb5_context->krb5_context, &my_creds, principal,
312 password, NULL, NULL, 0,
313 NULL, krb_options);
314 krb5_get_init_creds_opt_free(smb_krb5_context->krb5_context, krb_options);
316 assertion_message = talloc_asprintf(tctx,
317 "krb5_get_init_creds_password for %s failed: %s",
318 principal_string,
319 smb_get_krb5_error_message(smb_krb5_context->krb5_context, k5ret, tctx));
320 torture_assert_int_equal(tctx, k5ret, 0, assertion_message);
323 * Assert that the reply was with the correct type of
324 * principal, depending on the flags we set
326 if (test_data->canonicalize == false && test_data->enterprise) {
327 torture_assert_int_equal(tctx,
328 krb5_principal_get_type(smb_krb5_context->krb5_context,
329 my_creds.client), KRB5_NT_ENTERPRISE_PRINCIPAL,
330 "smb_krb5_init_context gave incorrect client->name.name_type");
331 } else {
332 torture_assert_int_equal(tctx,
333 krb5_principal_get_type(smb_krb5_context->krb5_context,
334 my_creds.client), KRB5_NT_PRINCIPAL,
335 "smb_krb5_init_context gave incorrect client->name.name_type");
338 torture_assert_int_equal(tctx,
339 krb5_unparse_name(smb_krb5_context->krb5_context,
340 my_creds.client, &got_principal_string), 0,
341 "krb5_unparse_name failed");
343 assertion_message = talloc_asprintf(tctx,
344 "krb5_get_init_creds_password returned a different principal %s to what was expected %s",
345 got_principal_string, expected_principal_string);
346 krb5_free_unparsed_name(smb_krb5_context->krb5_context, got_principal_string);
348 torture_assert(tctx, krb5_principal_compare(smb_krb5_context->krb5_context,
349 my_creds.client, expected_principal),
350 assertion_message);
352 torture_assert_int_equal(tctx,
353 krb5_principal_get_type(smb_krb5_context->krb5_context,
354 my_creds.server), KRB5_NT_SRV_INST,
355 "smb_krb5_init_context gave incorrect client->name.name_type");
357 torture_assert_str_equal(tctx, krb5_principal_get_comp_string(smb_krb5_context->krb5_context,
358 my_creds.server, 0),
359 "krbtgt",
360 "smb_krb5_init_context gave incorrect my_creds.server->name.name_string[0]");
362 krb5_free_principal(smb_krb5_context->krb5_context, principal);
364 k5ret = krb5_free_cred_contents(smb_krb5_context->krb5_context, &my_creds);
365 torture_assert_int_equal(tctx, k5ret, 0, "krb5_free_creds failed");
367 return true;
370 struct torture_suite *torture_krb5_canon(TALLOC_CTX *mem_ctx)
372 unsigned int i;
373 struct torture_suite *suite = torture_suite_create(mem_ctx, "canon");
374 suite->description = talloc_strdup(suite, "Kerberos Canonicalisation tests");
376 for (i = 0; i < TEST_ALL; i++) {
377 char *name = talloc_asprintf(suite, "%s.%s.%s.%s.%s",
378 (i & TEST_CANONICALIZE) ? "canon" : "no-canon",
379 (i & TEST_ENTERPRISE) ? "enterprise" : "no-enterprise",
380 (i & TEST_UPPER_REALM) ? "uc-realm" : "lc-realm",
381 (i & TEST_UPPER_USERNAME) ? "uc-user" : "lc-user",
382 (i & TEST_NETBIOS_REALM) ? "netbios-realm" : "krb5-realm");
384 struct test_data *test_data = talloc(suite, struct test_data);
385 if (i & TEST_NETBIOS_REALM) {
386 test_data->realm = cli_credentials_get_domain(cmdline_credentials);
387 } else {
388 test_data->realm = cli_credentials_get_realm(cmdline_credentials);
390 test_data->real_realm = cli_credentials_get_realm(cmdline_credentials);
391 test_data->username = cli_credentials_get_username(cmdline_credentials);
392 test_data->canonicalize = (i & TEST_CANONICALIZE) != 0;
393 test_data->enterprise = (i & TEST_ENTERPRISE) != 0;
394 test_data->upper_realm = (i & TEST_UPPER_REALM) != 0;
395 test_data->upper_username = (i & TEST_UPPER_USERNAME) != 0;
396 torture_suite_add_simple_tcase_const(suite, name, torture_krb5_as_req_canon,
397 test_data);
400 return suite;