1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright 2020 Alex Richardson <arichardson@FreeBSD.org> 5 * 6 * This software was developed by SRI International and the University of 7 * Cambridge Computer Laboratory (Department of Computer Science and 8 * Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the 9 * DARPA SSITH research programme. 10 * 11 * This work was supported by Innovate UK project 105694, "Digital Security by 12 * Design (DSbD) Technology Platform Prototype". 13 * 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions are met: 16 * 1. Redistributions of source code must retain the above copyright notice, 17 * this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright notice, 19 * this list of conditions and the following disclaimer in the documentation 20 * and/or other materials provided with the distribution. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY 23 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 24 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 25 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY 26 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 27 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 29 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 #include <sys/cdefs.h> 34 __FBSDID("$FreeBSD$"); 35 36 #include <sys/types.h> 37 #include <sys/param.h> 38 #include <err.h> 39 #include <errno.h> 40 #include <fcntl.h> 41 #include <stdbool.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <spawn.h> 45 #include <sys/module.h> 46 #include <sys/sbuf.h> 47 #include <sys/stat.h> 48 #include <sys/wait.h> 49 50 #include <atf-c.h> 51 52 /* 53 * Tests 0001-0999 are copied from OpenBSD's regress/sbin/pfctl. 54 * Tests 1001-1999 are ours (FreeBSD's own). 55 * 56 * pf: Run pfctl -nv on pfNNNN.in and check that the output matches pfNNNN.ok. 57 * Copied from OpenBSD. Main differences are some things not working 58 * in FreeBSD: 59 * * The action 'match' 60 * * The command 'set reassemble' 61 * * The 'from'/'to' options together with 'route-to' 62 * * The option 'scrub' (it is an action in FreeBSD) 63 * * Accepting undefined routing tables in actions (??: see pf0093.in) 64 * * The 'route' option 65 * * The 'set queue def' option 66 * selfpf: Feed pfctl output through pfctl again and verify it stays the same. 67 * Copied from OpenBSD. 68 */ 69 70 static bool 71 check_pf_module_available(void) 72 { 73 int modid; 74 struct module_stat stat; 75 76 if ((modid = modfind("pf")) < 0) { 77 warn("pf module not found"); 78 return false; 79 } 80 stat.version = sizeof(struct module_stat); 81 if (modstat(modid, &stat) < 0) { 82 warn("can't stat pf module id %d", modid); 83 return false; 84 } 85 return (true); 86 } 87 88 extern char **environ; 89 90 static struct sbuf * 91 read_fd(int fd, size_t sizehint) 92 { 93 struct sbuf *sb; 94 ssize_t count; 95 char buffer[MAXBSIZE]; 96 97 sb = sbuf_new(NULL, NULL, sizehint, SBUF_AUTOEXTEND); 98 errno = 0; 99 while ((count = read(fd, buffer, sizeof(buffer) - 1)) > 0) { 100 sbuf_bcat(sb, buffer, count); 101 } 102 ATF_REQUIRE_ERRNO(0, count == 0 && "Should have reached EOF"); 103 sbuf_finish(sb); /* Ensure NULL-termination */ 104 return (sb); 105 } 106 107 static struct sbuf * 108 read_file(const char *filename) 109 { 110 struct stat s; 111 struct sbuf *result; 112 int fd; 113 114 errno = 0; 115 ATF_REQUIRE_EQ_MSG(stat(filename, &s), 0, "cannot stat %s", filename); 116 fd = open(filename, O_RDONLY); 117 ATF_REQUIRE_ERRNO(0, fd > 0); 118 result = read_fd(fd, s.st_size); 119 ATF_REQUIRE_ERRNO(0, close(fd) == 0); 120 return (result); 121 } 122 123 static void 124 run_pfctl_test(const char *input_path, const char *expected_path, 125 const atf_tc_t *tc) 126 { 127 int status; 128 pid_t pid; 129 int pipefds[2]; 130 char input_files_path[PATH_MAX]; 131 struct sbuf *expected_output; 132 struct sbuf *real_output; 133 posix_spawn_file_actions_t action; 134 135 if (!check_pf_module_available()) 136 atf_tc_skip("pf(4) is not loaded"); 137 138 /* The test inputs need to be able to use relative includes. */ 139 snprintf(input_files_path, sizeof(input_files_path), "%s/files", 140 atf_tc_get_config_var(tc, "srcdir")); 141 ATF_REQUIRE_ERRNO(0, chdir(input_files_path) == 0); 142 143 ATF_REQUIRE_ERRNO(0, pipe(pipefds) == 0); 144 expected_output = read_file(expected_path); 145 146 posix_spawn_file_actions_init(&action); 147 posix_spawn_file_actions_addclose(&action, STDIN_FILENO); 148 posix_spawn_file_actions_addclose(&action, pipefds[1]); 149 posix_spawn_file_actions_adddup2(&action, pipefds[0], STDOUT_FILENO); 150 posix_spawn_file_actions_adddup2(&action, pipefds[0], STDERR_FILENO); 151 152 const char *argv[] = { "pfctl", "-o", "none", "-nvf", input_path, 153 NULL }; 154 printf("Running %s %s %s %s %s\n", argv[0], argv[1], argv[2], argv[3], 155 argv[4]); 156 status = posix_spawnp( 157 &pid, "pfctl", &action, NULL, __DECONST(char **, argv), environ); 158 ATF_REQUIRE_EQ_MSG( 159 status, 0, "posix_spawn failed: %s", strerror(errno)); 160 posix_spawn_file_actions_destroy(&action); 161 close(pipefds[0]); 162 163 real_output = read_fd(pipefds[1], 0); 164 printf("---\n%s---\n", sbuf_data(real_output)); 165 ATF_REQUIRE_EQ(waitpid(pid, &status, 0), pid); 166 ATF_REQUIRE_MSG(WIFEXITED(status), 167 "pfctl returned non-zero! Output:\n %s", sbuf_data(real_output)); 168 169 ATF_CHECK_STREQ(sbuf_data(expected_output), sbuf_data(real_output)); 170 sbuf_delete(expected_output); 171 sbuf_delete(real_output); 172 close(pipefds[1]); 173 } 174 175 static void 176 do_pf_test(const char *number, const atf_tc_t *tc) 177 { 178 char *input_path; 179 char *expected_path; 180 asprintf(&input_path, "%s/files/pf%s.in", 181 atf_tc_get_config_var(tc, "srcdir"), number); 182 asprintf(&expected_path, "%s/files/pf%s.ok", 183 atf_tc_get_config_var(tc, "srcdir"), number); 184 run_pfctl_test(input_path, expected_path, tc); 185 free(input_path); 186 free(expected_path); 187 } 188 189 static void 190 do_selfpf_test(const char *number, const atf_tc_t *tc) 191 { 192 char *expected_path; 193 asprintf(&expected_path, "%s/files/pf%s.ok", 194 atf_tc_get_config_var(tc, "srcdir"), number); 195 run_pfctl_test(expected_path, expected_path, tc); 196 free(expected_path); 197 } 198 199 #define PFCTL_TEST(number, descr) \ 200 ATF_TC(pf##number); \ 201 ATF_TC_HEAD(pf##number, tc) \ 202 { \ 203 atf_tc_set_md_var(tc, "descr", descr); \ 204 } \ 205 ATF_TC_BODY(pf##number, tc) \ 206 { \ 207 do_pf_test(#number, tc); \ 208 } \ 209 ATF_TC(selfpf##number); \ 210 ATF_TC_HEAD(selfpf##number, tc) \ 211 { \ 212 atf_tc_set_md_var(tc, "descr", "Self " descr); \ 213 } \ 214 ATF_TC_BODY(selfpf##number, tc) \ 215 { \ 216 do_selfpf_test(#number, tc); \ 217 } 218 #include "pfctl_test_list.inc" 219 #undef PFCTL_TEST 220 221 ATF_TP_ADD_TCS(tp) 222 { 223 #define PFCTL_TEST(number, descr) \ 224 ATF_TP_ADD_TC(tp, pf##number); \ 225 ATF_TP_ADD_TC(tp, selfpf##number); 226 #include "pfctl_test_list.inc" 227 #undef PFCTL_TEST 228 229 return atf_no_error(); 230 } 231