ssh.c
1 /* 2 * Copyright (c) 2021 Baptiste Daroussin <bapt@FreeBSD.org> 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer 9 * in this position and unchanged. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include <sys/stat.h> 27 28 #include <atf-c.h> 29 #include <fcntl.h> 30 #include <pkg.h> 31 #include <private/pkg.h> 32 #include <stdlib.h> 33 #include <xmalloc.h> 34 35 ATF_TC_WITHOUT_HEAD(badcommand); 36 ATF_TC_WITHOUT_HEAD(badrestrict); 37 ATF_TC_WITHOUT_HEAD(getfile); 38 ATF_TC_WITHOUT_HEAD(notrestricted); 39 ATF_TC_WITHOUT_HEAD(restricted); 40 41 ATF_TC_BODY(badcommand, tc) 42 { 43 char strout[] = 44 "ok: pkg " PKGVERSION "\n" 45 "ko: unknown command 'plop'\n"; 46 char *cwd = getcwd(NULL, 0); 47 int rootfd = open(cwd, O_DIRECTORY); 48 free(cwd); 49 int stdin_pipe[2]; 50 ATF_REQUIRE(pipe(stdin_pipe) >= 0); 51 pid_t p = atf_utils_fork(); 52 if (p == 0) { 53 close(STDIN_FILENO); 54 dup2(stdin_pipe[0], STDIN_FILENO); 55 close(stdin_pipe[1]); 56 exit(pkg_sshserve(rootfd)); 57 } 58 close(stdin_pipe[0]); 59 dprintf(stdin_pipe[1], "plop\n"); 60 dprintf(stdin_pipe[1], "quit\n"); 61 atf_utils_wait(p, 0, strout, ""); 62 } 63 64 ATF_TC_BODY(getfile, tc) 65 { 66 extern ucl_object_t *config; 67 struct stat st; 68 char strout[] = 69 "ok: pkg " PKGVERSION "\n" 70 "ko: bad command get, expecting 'get file age'\n" 71 "ko: bad command get, expecting 'get file age'\n" 72 "ok: 12\n" 73 "testcontent\n" 74 "ok: 0\n" 75 "ko: bad number plop: invalid\n" 76 "ko: file not found\n" 77 "ko: not a file\n" 78 "ko: file not found\n"; 79 char * cwd = getcwd(NULL, 0); 80 int rootfd = open(cwd, O_DIRECTORY); 81 free(cwd); 82 int stdin_pipe[2]; 83 ATF_REQUIRE(pipe(stdin_pipe) >= 0); 84 FILE *f = fopen("testfile", "w+"); 85 ATF_CHECK(f != NULL); 86 fputs("testcontent\n", f); 87 fclose(f); 88 ATF_CHECK(stat("testfile", &st) >= 0); 89 pid_t p = atf_utils_fork(); 90 if (p == 0) { 91 close(STDIN_FILENO); 92 dup2(stdin_pipe[0], STDIN_FILENO); 93 close(stdin_pipe[1]); 94 config = ucl_object_typed_new(UCL_OBJECT); 95 char *cwd = getcwd(NULL, 0); 96 ucl_object_insert_key(config, ucl_object_fromstring_common(cwd, 0, UCL_STRING_TRIM), 97 "SSH_RESTRICT_DIR", 16, false); 98 free(cwd); 99 exit(pkg_sshserve(rootfd)); 100 } 101 close(stdin_pipe[0]); 102 dprintf(stdin_pipe[1], "get \n"); 103 /* get a file without stating the age, should fail */ 104 dprintf(stdin_pipe[1], "get /testfile\n"); 105 dprintf(stdin_pipe[1], "get /testfile 0\n"); 106 /* get a file already in cache */ 107 dprintf(stdin_pipe[1], "get /testfile %ld\n", st.st_mtime); 108 /* get a file with a bad age specified */ 109 dprintf(stdin_pipe[1], "get /testfile plop\n"); 110 dprintf(stdin_pipe[1], "get /nonexistent 0\n"); 111 mkdir("test", 0755); 112 dprintf(stdin_pipe[1], "get test 0\n"); 113 dprintf(stdin_pipe[1], "get %s/Makefile.autosetup 0\n", atf_tc_get_config_var(tc, "srcdir")); 114 dprintf(stdin_pipe[1], "quit\n"); 115 atf_utils_wait(p, 0, strout, ""); 116 } 117 118 ATF_TC_BODY(badrestrict, tc) 119 { 120 extern ucl_object_t *config; 121 char strout[] = 122 "ok: pkg " PKGVERSION "\n" 123 "ko: chdir failed (/nonexistent)\n"; 124 char *cwd = getcwd(NULL, 0); 125 int rootfd = open(cwd, O_DIRECTORY); 126 free(cwd); 127 int stdin_pipe[2]; 128 ATF_REQUIRE(pipe(stdin_pipe) >= 0); 129 pid_t p = atf_utils_fork(); 130 if (p == 0) { 131 close(STDIN_FILENO); 132 dup2(stdin_pipe[0], STDIN_FILENO); 133 close(stdin_pipe[1]); 134 config = ucl_object_typed_new(UCL_OBJECT); 135 ucl_object_insert_key(config, ucl_object_fromstring_common("/nonexistent", 0, UCL_STRING_TRIM), 136 "SSH_RESTRICT_DIR", 16, false); 137 exit(pkg_sshserve(rootfd)); 138 } 139 close(stdin_pipe[0]); 140 dprintf(stdin_pipe[1], "get /testfile 0\n"); 141 dprintf(stdin_pipe[1], "quit\n"); 142 atf_utils_wait(p, 0, strout, ""); 143 } 144 145 ATF_TC_BODY(notrestricted, tc) 146 { 147 struct stat st; 148 char strout[] = 149 "ok: pkg " PKGVERSION "\n" 150 "ok: 12\n" 151 "testcontent\n"; 152 mkdir("test", 0755); 153 int rootfd = open("test", O_DIRECTORY); 154 int stdin_pipe[2]; 155 ATF_REQUIRE(pipe(stdin_pipe) >= 0); 156 FILE *f = fopen("testfile", "w+"); 157 ATF_CHECK(f != NULL); 158 fputs("testcontent\n", f); 159 fclose(f); 160 ATF_CHECK(stat("testfile", &st) >= 0); 161 pid_t p = atf_utils_fork(); 162 if (p == 0) { 163 close(STDIN_FILENO); 164 dup2(stdin_pipe[0], STDIN_FILENO); 165 close(stdin_pipe[1]); 166 exit(pkg_sshserve(rootfd)); 167 } 168 close(stdin_pipe[0]); 169 dprintf(stdin_pipe[1], "get ../testfile 0\n"); 170 dprintf(stdin_pipe[1], "quit\n"); 171 atf_utils_wait(p, 0, strout, ""); 172 } 173 174 ATF_TC_BODY(restricted, tc) 175 { 176 extern ucl_object_t *config; 177 struct stat st; 178 char strout[] = 179 "ok: pkg " PKGVERSION "\n" 180 "ko: file not found\n"; 181 mkdir("test", 0755); 182 int rootfd = open("test", O_DIRECTORY); 183 int stdin_pipe[2]; 184 ATF_REQUIRE(pipe(stdin_pipe) >= 0); 185 FILE *f = fopen("testfile", "w+"); 186 ATF_CHECK(f != NULL); 187 fputs("testcontent\n", f); 188 fclose(f); 189 ATF_CHECK(stat("testfile", &st) >= 0); 190 pid_t p = atf_utils_fork(); 191 if (p == 0) { 192 close(STDIN_FILENO); 193 dup2(stdin_pipe[0], STDIN_FILENO); 194 close(stdin_pipe[1]); 195 config = ucl_object_typed_new(UCL_OBJECT); 196 char *restriteddir = 0; 197 char *cwd = getcwd(NULL, 0); 198 xasprintf(&restriteddir, "%s/test", cwd); 199 free(cwd); 200 ucl_object_insert_key(config, ucl_object_fromstring_common(restriteddir, 0, UCL_STRING_TRIM), 201 "SSH_RESTRICT_DIR", 16, false); 202 int ret = pkg_sshserve(rootfd); 203 free(restriteddir); 204 exit(ret); 205 } 206 close(stdin_pipe[0]); 207 dprintf(stdin_pipe[1], "get ../testfile 0\n"); 208 dprintf(stdin_pipe[1], "quit\n"); 209 atf_utils_wait(p, 0, strout, ""); 210 } 211 212 ATF_TP_ADD_TCS(tp) 213 { 214 ATF_TP_ADD_TC(tp, badcommand); 215 ATF_TP_ADD_TC(tp, getfile); 216 ATF_TP_ADD_TC(tp, badrestrict); 217 ATF_TP_ADD_TC(tp, notrestricted); 218 ATF_TP_ADD_TC(tp, restricted); 219 220 return (atf_no_error()); 221 }