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