Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
ssh: cover ssh with unit tests
Baptiste Daroussin committed 4 years ago
commit b4f5453ed5776399c64d4ba59b66046b6dd7a8fd
parent c56990d
3 files changed +225 -6
modified libpkg/ssh.c
@@ -47,6 +47,7 @@

#include "pkg.h"
#include "private/event.h"
+
#include "private/utils.h"

int
pkg_sshserve(int fd)
@@ -88,12 +89,12 @@ pkg_sshserve(int fd)

		file = line + 4;

-
		if (*file == '/')
-
			file++;
-
		else if (*file == '\0') {
+
		if (*file == '\0') {
			printf("ko: bad command get, expecting 'get file age'\n");
			continue;
		}
+
		if (*file == '/')
+
			file++;

		pkg_debug(1, "SSH server> file requested: %s", file);

@@ -145,7 +146,7 @@ pkg_sshserve(int fd)

			if (realpath(file, fpath) == NULL ||
			    realpath(restricted, rpath) == NULL ||
-
			    strncmp(fpath, rpath, strlen(rpath)) != 0) {
+
			    strncmp(rpath, get_dirname(fpath), strlen(rpath)) != 0) {
				printf("ko: file not found\n");
				continue;
			}
modified tests/Makefile.autosetup
@@ -9,7 +9,8 @@ TESTS= \
	pkg_printf \
	pkg_validation \
	plist \
-
	lua
+
	lua \
+
	ssh

TESTS_SH= \
	frontend/pkg.sh \
@@ -71,6 +72,7 @@ deps_formula_OBJS= lib/deps_formula.o
pkg_add_dir_to_del_OBJS=	lib/pkg_add_dir_to_del.o
pkg_validation_OBJS=	lib/pkg_validation.o
packing_OBJS=	lib/packing.o
+
ssh_OBJS=	lib/ssh.o

SRCS=	\
	$(packing_OBJS:.o=.c) \
@@ -81,7 +83,8 @@ SRCS= \
	$(pkg_printf_OBJS:.o=.c) \
	$(deps_formula_OBJS:.o=.c) \
	$(pkg_add_dir_to_del_OBJS:.o=.c) \
-
	$(pkg_validation_OBJS:.o=.c)
+
	$(pkg_validation_OBJS:.o=.c) \
+
	$(ssh_OBJS:.o=.c)

include $(MK)/common.mk

added tests/lib/ssh.c
@@ -0,0 +1,215 @@
+
/*
+
 * Copyright (c) 2021 Baptiste Daroussin <bapt@FreeBSD.org>
+
 *
+
 * Redistribution and use in source and binary forms, with or without
+
 * modification, are permitted provided that the following conditions
+
 * are met:
+
 * 1. Redistributions of source code must retain the above copyright
+
 *    notice, this list of conditions and the following disclaimer
+
 *    in this position and unchanged.
+
 * 2. Redistributions in binary form must reproduce the above copyright
+
 *    notice, this list of conditions and the following disclaimer in the
+
 *    documentation and/or other materials provided with the distribution.
+
 *
+
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
 */
+

+
#include <sys/stat.h>
+

+
#include <atf-c.h>
+
#include <fcntl.h>
+
#include <pkg.h>
+
#include <private/pkg.h>
+
#include <stdlib.h>
+
#include <xmalloc.h>
+

+
ATF_TC(badcommand);
+
ATF_TC(badrestrict);
+
ATF_TC(getfile);
+
ATF_TC(notrestricted);
+
ATF_TC(restricted);
+

+
ATF_TC_HEAD(badcommand, tc) { }
+
ATF_TC_HEAD(getfile, tc) { }
+
ATF_TC_HEAD(badrestrict, tc) { }
+
ATF_TC_HEAD(notrestricted, tc) { }
+
ATF_TC_HEAD(restricted, tc) { }
+

+
ATF_TC_BODY(badcommand, tc)
+
{
+
	char strout[] =
+
		"ok: pkg " PKGVERSION "\n"
+
		"ko: unknown command 'plop'\n";
+
	int rootfd = open(getcwd(NULL, 0), O_DIRECTORY);
+
	int stdin_pipe[2];
+
	ATF_REQUIRE(pipe(stdin_pipe) >= 0);
+
	pid_t p = atf_utils_fork();
+
	if (p == 0) {
+
		close(STDIN_FILENO);
+
		dup2(stdin_pipe[0], STDIN_FILENO);
+
		close(stdin_pipe[1]);
+
		exit(pkg_sshserve(rootfd));
+
	}
+
	close(stdin_pipe[0]);
+
	dprintf(stdin_pipe[1], "plop\n");
+
	dprintf(stdin_pipe[1], "quit\n");
+
	atf_utils_wait(p, 0, strout, "");
+
}
+

+
ATF_TC_BODY(getfile, tc)
+
{
+
	extern ucl_object_t *config;
+
	struct stat st;
+
	char strout[] =
+
		"ok: pkg " PKGVERSION "\n"
+
		"ko: bad command get, expecting 'get file age'\n"
+
		"ko: bad command get, expecting 'get file age'\n"
+
		"ok: 12\n"
+
		"testcontent\n"
+
		"ok: 0\n"
+
		"ko: bad number plop: invalid\n"
+
		"ko: file not found\n"
+
		"ko: not a file\n"
+
		"ko: file not found\n";
+
	int rootfd = open(getcwd(NULL, 0), O_DIRECTORY);
+
	int stdin_pipe[2];
+
	ATF_REQUIRE(pipe(stdin_pipe) >= 0);
+
	FILE *f = fopen("testfile", "w+");
+
	ATF_CHECK(f != NULL);
+
	fputs("testcontent\n", f);
+
	fclose(f);
+
	ATF_CHECK(stat("testfile", &st) >= 0);
+
	pid_t p = atf_utils_fork();
+
	if (p == 0) {
+
		close(STDIN_FILENO);
+
		dup2(stdin_pipe[0], STDIN_FILENO);
+
		close(stdin_pipe[1]);
+
		config = ucl_object_typed_new(UCL_OBJECT);
+
		ucl_object_insert_key(config, ucl_object_fromstring_common(getcwd(NULL, 0), 0, UCL_STRING_TRIM),
+
			"SSH_RESTRICT_DIR", 16, false);
+
		exit(pkg_sshserve(rootfd));
+
	}
+
	close(stdin_pipe[0]);
+
	dprintf(stdin_pipe[1], "get \n");
+
	/* get a file without stating the age, should fail */
+
	dprintf(stdin_pipe[1], "get /testfile\n");
+
	dprintf(stdin_pipe[1], "get /testfile 0\n");
+
	/* get a file already in cache */
+
	dprintf(stdin_pipe[1], "get /testfile %ld\n", st.st_mtime);
+
	/* get a file with a bad age specified */
+
	dprintf(stdin_pipe[1], "get /testfile plop\n");
+
	dprintf(stdin_pipe[1], "get /nonexistent 0\n");
+
	mkdir("test", 0755);
+
	dprintf(stdin_pipe[1], "get test 0\n");
+
	dprintf(stdin_pipe[1], "get %s/Makefile.autosetup 0\n", atf_tc_get_config_var(tc, "srcdir"));
+
	dprintf(stdin_pipe[1], "quit\n");
+
	atf_utils_wait(p, 0, strout, "");
+
}
+

+
ATF_TC_BODY(badrestrict, tc)
+
{
+
	extern ucl_object_t *config;
+
	char strout[] =
+
		"ok: pkg " PKGVERSION "\n"
+
		"ko: chdir failed (/nonexistent)\n";
+
	int rootfd = open(getcwd(NULL, 0), O_DIRECTORY);
+
	int stdin_pipe[2];
+
	ATF_REQUIRE(pipe(stdin_pipe) >= 0);
+
	pid_t p = atf_utils_fork();
+
	if (p == 0) {
+
		close(STDIN_FILENO);
+
		dup2(stdin_pipe[0], STDIN_FILENO);
+
		close(stdin_pipe[1]);
+
		config = ucl_object_typed_new(UCL_OBJECT);
+
		ucl_object_insert_key(config, ucl_object_fromstring_common("/nonexistent", 0, UCL_STRING_TRIM),
+
			"SSH_RESTRICT_DIR", 16, false);
+
		exit(pkg_sshserve(rootfd));
+
	}
+
	close(stdin_pipe[0]);
+
	dprintf(stdin_pipe[1], "get /testfile 0\n");
+
	dprintf(stdin_pipe[1], "quit\n");
+
	atf_utils_wait(p, 0, strout, "");
+
}
+

+
ATF_TC_BODY(notrestricted, tc)
+
{
+
	struct stat st;
+
	char strout[] =
+
		"ok: pkg " PKGVERSION "\n"
+
		"ok: 12\n"
+
		"testcontent\n";
+
	 mkdir("test", 0755);
+
	int rootfd = open("test", O_DIRECTORY);
+
	int stdin_pipe[2];
+
	ATF_REQUIRE(pipe(stdin_pipe) >= 0);
+
	FILE *f = fopen("testfile", "w+");
+
	ATF_CHECK(f != NULL);
+
	fputs("testcontent\n", f);
+
	fclose(f);
+
	ATF_CHECK(stat("testfile", &st) >= 0);
+
	pid_t p = atf_utils_fork();
+
	if (p == 0) {
+
		close(STDIN_FILENO);
+
		dup2(stdin_pipe[0], STDIN_FILENO);
+
		close(stdin_pipe[1]);
+
		exit(pkg_sshserve(rootfd));
+
	}
+
	close(stdin_pipe[0]);
+
	dprintf(stdin_pipe[1], "get ../testfile 0\n");
+
	dprintf(stdin_pipe[1], "quit\n");
+
	atf_utils_wait(p, 0, strout, "");
+
}
+

+
ATF_TC_BODY(restricted, tc)
+
{
+
	extern ucl_object_t *config;
+
	struct stat st;
+
	char strout[] =
+
		"ok: pkg " PKGVERSION "\n"
+
		"ko: file not found\n";
+
	 mkdir("test", 0755);
+
	int rootfd = open("test", O_DIRECTORY);
+
	int stdin_pipe[2];
+
	ATF_REQUIRE(pipe(stdin_pipe) >= 0);
+
	FILE *f = fopen("testfile", "w+");
+
	ATF_CHECK(f != NULL);
+
	fputs("testcontent\n", f);
+
	fclose(f);
+
	ATF_CHECK(stat("testfile", &st) >= 0);
+
	pid_t p = atf_utils_fork();
+
	if (p == 0) {
+
		close(STDIN_FILENO);
+
		dup2(stdin_pipe[0], STDIN_FILENO);
+
		close(stdin_pipe[1]);
+
		config = ucl_object_typed_new(UCL_OBJECT);
+
		char *restriteddir;
+
		xasprintf(&restriteddir, "%s/test", getcwd(NULL, 0));
+
		ucl_object_insert_key(config, ucl_object_fromstring_common(restriteddir, 0, UCL_STRING_TRIM),
+
			"SSH_RESTRICT_DIR", 16, false);
+
		exit(pkg_sshserve(rootfd));
+
	}
+
	close(stdin_pipe[0]);
+
	dprintf(stdin_pipe[1], "get ../testfile 0\n");
+
	dprintf(stdin_pipe[1], "quit\n");
+
	atf_utils_wait(p, 0, strout, "");
+
}
+

+
ATF_TP_ADD_TCS(tp)
+
{
+
	ATF_TP_ADD_TC(tp, badcommand);
+
	ATF_TP_ADD_TC(tp, getfile);
+
	ATF_TP_ADD_TC(tp, badrestrict);
+
	ATF_TP_ADD_TC(tp, notrestricted);
+
	ATF_TP_ADD_TC(tp, restricted);
+

+
	return (atf_no_error());
+
}