/ tests / lib / ssh.c
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  }