1/* 	$OpenBSD: tests.c,v 1.1 2018/03/03 03:16:17 djm Exp $ */
2
3/*
4 * Regress test for keys options functions.
5 *
6 * Placed in the public domain
7 */
8
9#include <sys/types.h>
10#include <sys/param.h>
11#include <stdio.h>
12#include <stdint.h>
13#include <stdlib.h>
14#include <string.h>
15
16#include "test_helper.h"
17
18#include "sshkey.h"
19#include "authfile.h"
20#include "auth-options.h"
21#include "misc.h"
22#include "log.h"
23
24static struct sshkey *
25load_key(const char *name)
26{
27	struct sshkey *ret;
28	int r;
29
30	r = sshkey_load_public(test_data_file(name), &ret, NULL);
31	ASSERT_INT_EQ(r, 0);
32	ASSERT_PTR_NE(ret, NULL);
33	return ret;
34}
35
36static struct sshauthopt *
37default_authkey_opts(void)
38{
39	struct sshauthopt *ret = sshauthopt_new();
40
41	ASSERT_PTR_NE(ret, NULL);
42	ret->permit_port_forwarding_flag = 1;
43	ret->permit_agent_forwarding_flag = 1;
44	ret->permit_x11_forwarding_flag = 1;
45	ret->permit_pty_flag = 1;
46	ret->permit_user_rc = 1;
47	return ret;
48}
49
50static struct sshauthopt *
51default_authkey_restrict_opts(void)
52{
53	struct sshauthopt *ret = sshauthopt_new();
54
55	ASSERT_PTR_NE(ret, NULL);
56	ret->permit_port_forwarding_flag = 0;
57	ret->permit_agent_forwarding_flag = 0;
58	ret->permit_x11_forwarding_flag = 0;
59	ret->permit_pty_flag = 0;
60	ret->permit_user_rc = 0;
61	ret->restricted = 1;
62	return ret;
63}
64
65static char **
66commasplit(const char *s, size_t *np)
67{
68	char *ocp, *cp, *cp2, **ret = NULL;
69	size_t n;
70
71	ocp = cp = strdup(s);
72	ASSERT_PTR_NE(cp, NULL);
73	for (n = 0; (cp2 = strsep(&cp, ",")) != NULL;) {
74		ret = recallocarray(ret, n, n + 1, sizeof(*ret));
75		ASSERT_PTR_NE(ret, NULL);
76		cp2 = strdup(cp2);
77		ASSERT_PTR_NE(cp2, NULL);
78		ret[n++] = cp2;
79	}
80	free(ocp);
81	*np = n;
82	return ret;
83}
84
85static void
86compare_opts(const struct sshauthopt *opts,
87    const struct sshauthopt *expected)
88{
89	size_t i;
90
91	ASSERT_PTR_NE(opts, NULL);
92	ASSERT_PTR_NE(expected, NULL);
93	ASSERT_PTR_NE(expected, opts); /* bozo :) */
94
95#define FLAG_EQ(x) ASSERT_INT_EQ(opts->x, expected->x)
96	FLAG_EQ(permit_port_forwarding_flag);
97	FLAG_EQ(permit_agent_forwarding_flag);
98	FLAG_EQ(permit_x11_forwarding_flag);
99	FLAG_EQ(permit_pty_flag);
100	FLAG_EQ(permit_user_rc);
101	FLAG_EQ(restricted);
102	FLAG_EQ(cert_authority);
103#undef FLAG_EQ
104
105#define STR_EQ(x) \
106	do { \
107		if (expected->x == NULL) \
108			ASSERT_PTR_EQ(opts->x, expected->x); \
109		else \
110			ASSERT_STRING_EQ(opts->x, expected->x); \
111	} while (0)
112	STR_EQ(cert_principals);
113	STR_EQ(force_command);
114	STR_EQ(required_from_host_cert);
115	STR_EQ(required_from_host_keys);
116#undef STR_EQ
117
118#define ARRAY_EQ(nx, x) \
119	do { \
120		ASSERT_SIZE_T_EQ(opts->nx, expected->nx); \
121		if (expected->nx == 0) \
122			break; \
123		for (i = 0; i < expected->nx; i++) \
124			ASSERT_STRING_EQ(opts->x[i], expected->x[i]); \
125	} while (0)
126	ARRAY_EQ(nenv, env);
127	ARRAY_EQ(npermitopen, permitopen);
128#undef ARRAY_EQ
129}
130
131static void
132test_authkeys_parse(void)
133{
134	struct sshauthopt *opts, *expected;
135	const char *errstr;
136
137#define FAIL_TEST(label, keywords) \
138	do { \
139		TEST_START("sshauthopt_parse invalid " label); \
140		opts = sshauthopt_parse(keywords, &errstr); \
141		ASSERT_PTR_EQ(opts, NULL); \
142		ASSERT_PTR_NE(errstr, NULL); \
143		TEST_DONE(); \
144	} while (0)
145#define CHECK_SUCCESS_AND_CLEANUP() \
146	do { \
147		if (errstr != NULL) \
148			ASSERT_STRING_EQ(errstr, ""); \
149		compare_opts(opts, expected); \
150		sshauthopt_free(expected); \
151		sshauthopt_free(opts); \
152	} while (0)
153
154	/* Basic tests */
155	TEST_START("sshauthopt_parse empty");
156	expected = default_authkey_opts();
157	opts = sshauthopt_parse("", &errstr);
158	CHECK_SUCCESS_AND_CLEANUP();
159	TEST_DONE();
160
161	TEST_START("sshauthopt_parse trailing whitespace");
162	expected = default_authkey_opts();
163	opts = sshauthopt_parse(" ", &errstr);
164	CHECK_SUCCESS_AND_CLEANUP();
165	TEST_DONE();
166
167	TEST_START("sshauthopt_parse restrict");
168	expected = default_authkey_restrict_opts();
169	opts = sshauthopt_parse("restrict", &errstr);
170	CHECK_SUCCESS_AND_CLEANUP();
171	TEST_DONE();
172
173	/* Invalid syntax */
174	FAIL_TEST("trailing comma", "restrict,");
175	FAIL_TEST("bare comma", ",");
176	FAIL_TEST("unknown option", "BLAH");
177	FAIL_TEST("unknown option with trailing comma", "BLAH,");
178	FAIL_TEST("unknown option with trailing whitespace", "BLAH ");
179
180	/* force_tun_device */
181	TEST_START("sshauthopt_parse tunnel explicit");
182	expected = default_authkey_opts();
183	expected->force_tun_device = 1;
184	opts = sshauthopt_parse("tunnel=\"1\"", &errstr);
185	CHECK_SUCCESS_AND_CLEANUP();
186	TEST_DONE();
187
188	TEST_START("sshauthopt_parse tunnel any");
189	expected = default_authkey_opts();
190	expected->force_tun_device = SSH_TUNID_ANY;
191	opts = sshauthopt_parse("tunnel=\"any\"", &errstr);
192	CHECK_SUCCESS_AND_CLEANUP();
193	TEST_DONE();
194
195	FAIL_TEST("tunnel", "tunnel=\"blah\"");
196
197	/* Flag options */
198#define FLAG_TEST(keyword, var, val) \
199	do { \
200		TEST_START("sshauthopt_parse " keyword); \
201		expected = default_authkey_opts(); \
202		expected->var = val; \
203		opts = sshauthopt_parse(keyword, &errstr); \
204		CHECK_SUCCESS_AND_CLEANUP(); \
205		expected = default_authkey_restrict_opts(); \
206		expected->var = val; \
207		opts = sshauthopt_parse("restrict,"keyword, &errstr); \
208		CHECK_SUCCESS_AND_CLEANUP(); \
209		TEST_DONE(); \
210	} while (0)
211	/* Positive flags */
212	FLAG_TEST("cert-authority", cert_authority, 1);
213	FLAG_TEST("port-forwarding", permit_port_forwarding_flag, 1);
214	FLAG_TEST("agent-forwarding", permit_agent_forwarding_flag, 1);
215	FLAG_TEST("x11-forwarding", permit_x11_forwarding_flag, 1);
216	FLAG_TEST("pty", permit_pty_flag, 1);
217	FLAG_TEST("user-rc", permit_user_rc, 1);
218	/* Negative flags */
219	FLAG_TEST("no-port-forwarding", permit_port_forwarding_flag, 0);
220	FLAG_TEST("no-agent-forwarding", permit_agent_forwarding_flag, 0);
221	FLAG_TEST("no-x11-forwarding", permit_x11_forwarding_flag, 0);
222	FLAG_TEST("no-pty", permit_pty_flag, 0);
223	FLAG_TEST("no-user-rc", permit_user_rc, 0);
224#undef FLAG_TEST
225	FAIL_TEST("no-cert-authority", "no-cert-authority");
226
227	/* String options */
228#define STRING_TEST(keyword, var, val) \
229	do { \
230		TEST_START("sshauthopt_parse " keyword); \
231		expected = default_authkey_opts(); \
232		expected->var = strdup(val); \
233		ASSERT_PTR_NE(expected->var, NULL); \
234		opts = sshauthopt_parse(keyword "=" #val, &errstr); \
235		CHECK_SUCCESS_AND_CLEANUP(); \
236		expected = default_authkey_restrict_opts(); \
237		expected->var = strdup(val); \
238		ASSERT_PTR_NE(expected->var, NULL); \
239		opts = sshauthopt_parse( \
240		    "restrict," keyword "=" #val ",restrict", &errstr); \
241		CHECK_SUCCESS_AND_CLEANUP(); \
242		TEST_DONE(); \
243	} while (0)
244	STRING_TEST("command", force_command, "/bin/true");
245	STRING_TEST("principals", cert_principals, "gregor,josef,K");
246	STRING_TEST("from", required_from_host_keys, "127.0.0.0/8");
247#undef STRING_TEST
248	FAIL_TEST("unquoted command", "command=oops");
249	FAIL_TEST("unquoted principals", "principals=estragon");
250	FAIL_TEST("unquoted from", "from=127.0.0.1");
251
252	/* String array option tests */
253#define ARRAY_TEST(label, keywords, var, nvar, val) \
254	do { \
255		TEST_START("sshauthopt_parse " label); \
256		expected = default_authkey_opts(); \
257		expected->var = commasplit(val, &expected->nvar); \
258		ASSERT_PTR_NE(expected->var, NULL); \
259		opts = sshauthopt_parse(keywords, &errstr); \
260		CHECK_SUCCESS_AND_CLEANUP(); \
261		expected = default_authkey_restrict_opts(); \
262		expected->var = commasplit(val, &expected->nvar); \
263		ASSERT_PTR_NE(expected->var, NULL); \
264		opts = sshauthopt_parse( \
265		    "restrict," keywords ",restrict", &errstr); \
266		CHECK_SUCCESS_AND_CLEANUP(); \
267		TEST_DONE(); \
268	} while (0)
269	ARRAY_TEST("environment", "environment=\"foo=1\",environment=\"bar=2\"",
270	    env, nenv, "foo=1,bar=2");
271	ARRAY_TEST("permitopen", "permitopen=\"foo:123\",permitopen=\"bar:*\"",
272	    permitopen, npermitopen, "foo:123,bar:*");
273#undef ARRAY_TEST
274	FAIL_TEST("environment", "environment=\",=bah\"");
275	FAIL_TEST("permitopen port", "foo:bar");
276	FAIL_TEST("permitopen missing port", "foo:");
277	FAIL_TEST("permitopen missing port specification", "foo");
278	FAIL_TEST("permitopen invalid host", "[:");
279
280#undef CHECK_SUCCESS_AND_CLEANUP
281#undef FAIL_TEST
282}
283
284static void
285test_cert_parse(void)
286{
287	struct sshkey *cert;
288	struct sshauthopt *opts, *expected;
289
290#define CHECK_SUCCESS_AND_CLEANUP() \
291	do { \
292		compare_opts(opts, expected); \
293		sshauthopt_free(expected); \
294		sshauthopt_free(opts); \
295		sshkey_free(cert); \
296	} while (0)
297#define FLAG_TEST(keybase, var) \
298	do { \
299		TEST_START("sshauthopt_from_cert no_" keybase); \
300		cert = load_key("no_" keybase ".cert"); \
301		expected = default_authkey_opts(); \
302		expected->var = 0; \
303		opts = sshauthopt_from_cert(cert); \
304		CHECK_SUCCESS_AND_CLEANUP(); \
305		TEST_DONE(); \
306		TEST_START("sshauthopt_from_cert only_" keybase); \
307		cert = load_key("only_" keybase ".cert"); \
308		expected = sshauthopt_new(); \
309		ASSERT_PTR_NE(expected, NULL); \
310		expected->var = 1; \
311		opts = sshauthopt_from_cert(cert); \
312		CHECK_SUCCESS_AND_CLEANUP(); \
313		TEST_DONE(); \
314	} while (0)
315	FLAG_TEST("agentfwd", permit_agent_forwarding_flag);
316	FLAG_TEST("portfwd", permit_port_forwarding_flag);
317	FLAG_TEST("pty", permit_pty_flag);
318	FLAG_TEST("user_rc", permit_user_rc);
319	FLAG_TEST("x11fwd", permit_x11_forwarding_flag);
320#undef FLAG_TEST
321
322	TEST_START("sshauthopt_from_cert all permitted");
323	cert = load_key("all_permit.cert");
324	expected = default_authkey_opts();
325	opts = sshauthopt_from_cert(cert);
326	CHECK_SUCCESS_AND_CLEANUP();
327	TEST_DONE();
328
329	TEST_START("sshauthopt_from_cert nothing permitted");
330	cert = load_key("no_permit.cert");
331	expected = sshauthopt_new();
332	ASSERT_PTR_NE(expected, NULL);
333	opts = sshauthopt_from_cert(cert);
334	CHECK_SUCCESS_AND_CLEANUP();
335	TEST_DONE();
336
337	TEST_START("sshauthopt_from_cert force-command");
338	cert = load_key("force_command.cert");
339	expected = default_authkey_opts();
340	expected->force_command = strdup("foo");
341	ASSERT_PTR_NE(expected->force_command, NULL);
342	opts = sshauthopt_from_cert(cert);
343	CHECK_SUCCESS_AND_CLEANUP();
344	TEST_DONE();
345
346	TEST_START("sshauthopt_from_cert source-address");
347	cert = load_key("sourceaddr.cert");
348	expected = default_authkey_opts();
349	expected->required_from_host_cert = strdup("127.0.0.1/32,::1/128");
350	ASSERT_PTR_NE(expected->required_from_host_cert, NULL);
351	opts = sshauthopt_from_cert(cert);
352	CHECK_SUCCESS_AND_CLEANUP();
353	TEST_DONE();
354#undef CHECK_SUCCESS_AND_CLEANUP
355
356#define FAIL_TEST(keybase) \
357	do { \
358		TEST_START("sshauthopt_from_cert " keybase); \
359		cert = load_key(keybase ".cert"); \
360		opts = sshauthopt_from_cert(cert); \
361		ASSERT_PTR_EQ(opts, NULL); \
362		sshkey_free(cert); \
363		TEST_DONE(); \
364	} while (0)
365	FAIL_TEST("host");
366	FAIL_TEST("bad_sourceaddr");
367	FAIL_TEST("unknown_critical");
368#undef FAIL_TEST
369}
370
371static void
372test_merge(void)
373{
374	struct sshkey *cert;
375	struct sshauthopt *key_opts, *cert_opts, *merge_opts, *expected;
376	const char *errstr;
377
378	/*
379	 * Prepare for a test by making some key and cert options and
380	 * attempting to merge them.
381	 */
382#define PREPARE(label, keyname, keywords) \
383	do { \
384		expected = NULL; \
385		TEST_START("sshauthopt_merge " label); \
386		cert = load_key(keyname ".cert"); \
387		cert_opts = sshauthopt_from_cert(cert); \
388		ASSERT_PTR_NE(cert_opts, NULL); \
389		key_opts = sshauthopt_parse(keywords, &errstr); \
390		if (errstr != NULL) \
391			ASSERT_STRING_EQ(errstr, ""); \
392		ASSERT_PTR_NE(key_opts, NULL); \
393		merge_opts = sshauthopt_merge(key_opts, \
394		    cert_opts, &errstr); \
395	} while (0)
396
397	/* Cleanup stuff allocated by PREPARE() */
398#define CLEANUP() \
399	do { \
400		sshauthopt_free(expected); \
401		sshauthopt_free(merge_opts); \
402		sshauthopt_free(key_opts); \
403		sshauthopt_free(cert_opts); \
404		sshkey_free(cert); \
405	} while (0)
406
407	/* Check the results of PREPARE() against expectation; calls CLEANUP */
408#define CHECK_SUCCESS_AND_CLEANUP() \
409	do { \
410		if (errstr != NULL) \
411			ASSERT_STRING_EQ(errstr, ""); \
412		compare_opts(merge_opts, expected); \
413		CLEANUP(); \
414	} while (0)
415
416	/* Check a single case of merging of flag options */
417#define FLAG_CASE(keybase, label, keyname, keywords, mostly_off, var, val) \
418	do { \
419		PREPARE(keybase " " label, keyname, keywords); \
420		expected = mostly_off ? \
421		    sshauthopt_new() : default_authkey_opts(); \
422		expected->var = val; \
423		ASSERT_PTR_NE(expected, NULL); \
424		CHECK_SUCCESS_AND_CLEANUP(); \
425		TEST_DONE(); \
426	} while (0)
427
428	/*
429	 * Fairly exhaustive exercise of a flag option. Tests
430	 * option both set and clear in certificate, set and clear in
431	 * authorized_keys and set and cleared via restrict keyword.
432	 */
433#define FLAG_TEST(keybase, keyword, var) \
434	do { \
435		FLAG_CASE(keybase, "keys:default,yes cert:default,no", \
436		    "no_" keybase, keyword, 0, var, 0); \
437		FLAG_CASE(keybase,"keys:-*,yes cert:default,no", \
438		    "no_" keybase, "restrict," keyword, 1, var, 0); \
439		FLAG_CASE(keybase, "keys:default,no cert:default,no", \
440		    "no_" keybase, "no-" keyword, 0, var, 0); \
441		FLAG_CASE(keybase, "keys:-*,no cert:default,no", \
442		    "no_" keybase, "restrict,no-" keyword, 1, var, 0); \
443		\
444		FLAG_CASE(keybase, "keys:default,yes cert:-*,yes", \
445		    "only_" keybase, keyword, 1, var, 1); \
446		FLAG_CASE(keybase,"keys:-*,yes cert:-*,yes", \
447		    "only_" keybase, "restrict," keyword, 1, var, 1); \
448		FLAG_CASE(keybase, "keys:default,no cert:-*,yes", \
449		    "only_" keybase, "no-" keyword, 1, var, 0); \
450		FLAG_CASE(keybase, "keys:-*,no cert:-*,yes", \
451		    "only_" keybase, "restrict,no-" keyword, 1, var, 0); \
452		\
453		FLAG_CASE(keybase, "keys:default,yes cert:-*", \
454		    "no_permit", keyword, 1, var, 0); \
455		FLAG_CASE(keybase,"keys:-*,yes cert:-*", \
456		    "no_permit", "restrict," keyword, 1, var, 0); \
457		FLAG_CASE(keybase, "keys:default,no cert:-*", \
458		    "no_permit", "no-" keyword, 1, var, 0); \
459		FLAG_CASE(keybase, "keys:-*,no cert:-*", \
460		    "no_permit", "restrict,no-" keyword, 1, var, 0); \
461		\
462		FLAG_CASE(keybase, "keys:default,yes cert:*", \
463		    "all_permit", keyword, 0, var, 1); \
464		FLAG_CASE(keybase,"keys:-*,yes cert:*", \
465		    "all_permit", "restrict," keyword, 1, var, 1); \
466		FLAG_CASE(keybase, "keys:default,no cert:*", \
467		    "all_permit", "no-" keyword, 0, var, 0); \
468		FLAG_CASE(keybase, "keys:-*,no cert:*", \
469		    "all_permit", "restrict,no-" keyword, 1, var, 0); \
470		\
471	} while (0)
472	FLAG_TEST("portfwd", "port-forwarding", permit_port_forwarding_flag);
473	FLAG_TEST("agentfwd", "agent-forwarding", permit_agent_forwarding_flag);
474	FLAG_TEST("pty", "pty", permit_pty_flag);
475	FLAG_TEST("user_rc", "user-rc", permit_user_rc);
476	FLAG_TEST("x11fwd", "x11-forwarding", permit_x11_forwarding_flag);
477#undef FLAG_TEST
478
479	PREPARE("source-address both", "sourceaddr", "from=\"127.0.0.1\"");
480	expected = default_authkey_opts();
481	expected->required_from_host_cert = strdup("127.0.0.1/32,::1/128");
482	ASSERT_PTR_NE(expected->required_from_host_cert, NULL);
483	expected->required_from_host_keys = strdup("127.0.0.1");
484	ASSERT_PTR_NE(expected->required_from_host_keys, NULL);
485	CHECK_SUCCESS_AND_CLEANUP();
486	TEST_DONE();
487
488	PREPARE("source-address none", "all_permit", "");
489	expected = default_authkey_opts();
490	CHECK_SUCCESS_AND_CLEANUP();
491	TEST_DONE();
492
493	PREPARE("source-address keys", "all_permit", "from=\"127.0.0.1\"");
494	expected = default_authkey_opts();
495	expected->required_from_host_keys = strdup("127.0.0.1");
496	ASSERT_PTR_NE(expected->required_from_host_keys, NULL);
497	CHECK_SUCCESS_AND_CLEANUP();
498	TEST_DONE();
499
500	PREPARE("source-address cert", "sourceaddr", "");
501	expected = default_authkey_opts();
502	expected->required_from_host_cert = strdup("127.0.0.1/32,::1/128");
503	ASSERT_PTR_NE(expected->required_from_host_cert, NULL);
504	CHECK_SUCCESS_AND_CLEANUP();
505	TEST_DONE();
506
507	PREPARE("force-command both", "force_command", "command=\"foo\"");
508	expected = default_authkey_opts();
509	expected->force_command = strdup("foo");
510	ASSERT_PTR_NE(expected->force_command, NULL);
511	CHECK_SUCCESS_AND_CLEANUP();
512	TEST_DONE();
513
514	PREPARE("force-command none", "all_permit", "");
515	expected = default_authkey_opts();
516	CHECK_SUCCESS_AND_CLEANUP();
517	TEST_DONE();
518
519	PREPARE("force-command keys", "all_permit", "command=\"bar\"");
520	expected = default_authkey_opts();
521	expected->force_command = strdup("bar");
522	ASSERT_PTR_NE(expected->force_command, NULL);
523	CHECK_SUCCESS_AND_CLEANUP();
524	TEST_DONE();
525
526	PREPARE("force-command cert", "force_command", "");
527	expected = default_authkey_opts();
528	expected->force_command = strdup("foo");
529	ASSERT_PTR_NE(expected->force_command, NULL);
530	CHECK_SUCCESS_AND_CLEANUP();
531	TEST_DONE();
532
533	PREPARE("force-command mismatch", "force_command", "command=\"bar\"");
534	ASSERT_PTR_EQ(merge_opts, NULL);
535	CLEANUP();
536	TEST_DONE();
537
538	PREPARE("tunnel", "all_permit", "tunnel=\"6\"");
539	expected = default_authkey_opts();
540	expected->force_tun_device = 6;
541	CHECK_SUCCESS_AND_CLEANUP();
542	TEST_DONE();
543
544	PREPARE("permitopen", "all_permit",
545	    "permitopen=\"127.0.0.1:*\",permitopen=\"127.0.0.1:123\"");
546	expected = default_authkey_opts();
547	expected->permitopen = commasplit("127.0.0.1:*,127.0.0.1:123",
548	    &expected->npermitopen);
549	CHECK_SUCCESS_AND_CLEANUP();
550	TEST_DONE();
551
552	PREPARE("environment", "all_permit",
553	    "environment=\"foo=a\",environment=\"bar=b\"");
554	expected = default_authkey_opts();
555	expected->env = commasplit("foo=a,bar=b", &expected->nenv);
556	CHECK_SUCCESS_AND_CLEANUP();
557	TEST_DONE();
558}
559
560void
561tests(void)
562{
563	extern char *__progname;
564	LogLevel ll = test_is_verbose() ?
565	    SYSLOG_LEVEL_DEBUG3 : SYSLOG_LEVEL_QUIET;
566
567	/* test_cert_parse() are a bit spammy to error() by default... */
568	log_init(__progname, ll, SYSLOG_FACILITY_USER, 1);
569
570	test_authkeys_parse();
571	test_cert_parse();
572	test_merge();
573}
574