xref: /freebsd/usr.sbin/rtsold/cap_script.c (revision 4d65a7c6951cea0333f1a0c1b32c38489cdfa6c5)
104e9edb5SMark Johnston /*-
204e9edb5SMark Johnston  * SPDX-License-Identifier: BSD-2-Clause
304e9edb5SMark Johnston  *
404e9edb5SMark Johnston  * Copyright (c) 2018 The FreeBSD Foundation
504e9edb5SMark Johnston  *
604e9edb5SMark Johnston  * This software was developed by Mark Johnston under sponsorship from
704e9edb5SMark Johnston  * the FreeBSD Foundation.
804e9edb5SMark Johnston  *
904e9edb5SMark Johnston  * Redistribution and use in source and binary forms, with or without
1004e9edb5SMark Johnston  * modification, are permitted provided that the following conditions are
1104e9edb5SMark Johnston  * met:
1204e9edb5SMark Johnston  * 1. Redistributions of source code must retain the above copyright
1304e9edb5SMark Johnston  *    notice, this list of conditions and the following disclaimer.
1404e9edb5SMark Johnston  * 2. Redistributions in binary form must reproduce the above copyright
1504e9edb5SMark Johnston  *    notice, this list of conditions and the following disclaimer in
1604e9edb5SMark Johnston  *    the documentation and/or other materials provided with the distribution.
1704e9edb5SMark Johnston  *
1804e9edb5SMark Johnston  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1904e9edb5SMark Johnston  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2004e9edb5SMark Johnston  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2104e9edb5SMark Johnston  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2204e9edb5SMark Johnston  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2304e9edb5SMark Johnston  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2404e9edb5SMark Johnston  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2504e9edb5SMark Johnston  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2604e9edb5SMark Johnston  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2704e9edb5SMark Johnston  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2804e9edb5SMark Johnston  * SUCH DAMAGE.
2904e9edb5SMark Johnston  */
3004e9edb5SMark Johnston 
3104e9edb5SMark Johnston #include <sys/types.h>
3204e9edb5SMark Johnston #include <sys/capsicum.h>
3304e9edb5SMark Johnston #include <sys/dnv.h>
3404e9edb5SMark Johnston #include <sys/nv.h>
3504e9edb5SMark Johnston #include <sys/queue.h>
3604e9edb5SMark Johnston #include <sys/wait.h>
3704e9edb5SMark Johnston 
3804e9edb5SMark Johnston #include <net/if.h>
3904e9edb5SMark Johnston #include <netinet/in.h>
4004e9edb5SMark Johnston 
4104e9edb5SMark Johnston #include <capsicum_helpers.h>
4204e9edb5SMark Johnston #include <errno.h>
4304e9edb5SMark Johnston #include <stdlib.h>
4404e9edb5SMark Johnston #include <string.h>
4504e9edb5SMark Johnston #include <unistd.h>
4604e9edb5SMark Johnston 
4704e9edb5SMark Johnston #include <libcasper.h>
4804e9edb5SMark Johnston #include <libcasper_service.h>
4904e9edb5SMark Johnston 
5004e9edb5SMark Johnston #include "rtsold.h"
5104e9edb5SMark Johnston 
5204e9edb5SMark Johnston /*
5304e9edb5SMark Johnston  * Run the script and return the write end of a pipe to the main process.
5404e9edb5SMark Johnston  * Return -1 and set errno on error.
5504e9edb5SMark Johnston  */
5604e9edb5SMark Johnston static int
script_run(char ** argv)5704e9edb5SMark Johnston script_run(char **argv)
5804e9edb5SMark Johnston {
5904e9edb5SMark Johnston 	pid_t pid;
6004e9edb5SMark Johnston 	int fd[2], null;
6104e9edb5SMark Johnston 
6204e9edb5SMark Johnston 	if (pipe(fd) != 0)
6304e9edb5SMark Johnston 		return (-1);
6404e9edb5SMark Johnston 	if ((pid = fork()) < 0)
6504e9edb5SMark Johnston 		return (-1);
6604e9edb5SMark Johnston 	if (pid == 0) {
6704e9edb5SMark Johnston 		(void)close(fd[1]);
6804e9edb5SMark Johnston 		null = open("/dev/null", O_RDWR);
6904e9edb5SMark Johnston 		if (null < 0)
7004e9edb5SMark Johnston 			_exit(1);
7104e9edb5SMark Johnston 		if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO)
7204e9edb5SMark Johnston 			_exit(1);
7304e9edb5SMark Johnston 
7404e9edb5SMark Johnston 		closefrom(3);
7504e9edb5SMark Johnston 		(void)execve(argv[0], argv, NULL);
7604e9edb5SMark Johnston 		_exit(1);
7704e9edb5SMark Johnston 	} else
7804e9edb5SMark Johnston 		(void)close(fd[0]);
7904e9edb5SMark Johnston 
8004e9edb5SMark Johnston 	return (fd[1]);
8104e9edb5SMark Johnston }
8204e9edb5SMark Johnston 
8304e9edb5SMark Johnston int
cap_script_run(cap_channel_t * cap,const char * const * argv)8404e9edb5SMark Johnston cap_script_run(cap_channel_t *cap, const char *const *argv)
8504e9edb5SMark Johnston {
8604e9edb5SMark Johnston #ifdef WITH_CASPER
8704e9edb5SMark Johnston 	nvlist_t *nvl;
8804e9edb5SMark Johnston 	size_t argc;
8904e9edb5SMark Johnston 	int error, wfd;
9004e9edb5SMark Johnston 
9104e9edb5SMark Johnston 	for (argc = 0; argv[argc] != NULL; argc++)
9204e9edb5SMark Johnston 		;
9304e9edb5SMark Johnston 
9404e9edb5SMark Johnston 	nvl = nvlist_create(0);
9504e9edb5SMark Johnston 	nvlist_add_string(nvl, "cmd", "script_run");
9604e9edb5SMark Johnston 	nvlist_add_string_array(nvl, "argv", argv, argc);
9704e9edb5SMark Johnston 	nvl = cap_xfer_nvlist(cap, nvl);
9804e9edb5SMark Johnston 	if (nvl == NULL)
9904e9edb5SMark Johnston 		return (-1);
10004e9edb5SMark Johnston 
10104e9edb5SMark Johnston 	error = (int)dnvlist_get_number(nvl, "error", 0);
10204e9edb5SMark Johnston 	if (error == 0)
10304e9edb5SMark Johnston 		wfd = nvlist_take_descriptor(nvl, "fd");
10404e9edb5SMark Johnston 	nvlist_destroy(nvl);
10504e9edb5SMark Johnston 	if (error != 0)
10604e9edb5SMark Johnston 		errno = error;
10704e9edb5SMark Johnston 	return (error == 0 ? wfd : -1);
10804e9edb5SMark Johnston #else
10904e9edb5SMark Johnston 	(void)cap;
11004e9edb5SMark Johnston 	return (script_run(__DECONST(char **, argv)));
11104e9edb5SMark Johnston #endif
11204e9edb5SMark Johnston }
11304e9edb5SMark Johnston 
11404e9edb5SMark Johnston /*
11504e9edb5SMark Johnston  * Wait for a child process to exit, and return its status.
11604e9edb5SMark Johnston  * Return -1 and set errno upon error.
11704e9edb5SMark Johnston  */
11804e9edb5SMark Johnston static int
script_wait(int * statusp)11904e9edb5SMark Johnston script_wait(int *statusp)
12004e9edb5SMark Johnston {
12104e9edb5SMark Johnston 	int error;
12204e9edb5SMark Johnston 
12304e9edb5SMark Johnston 	error = wait(statusp);
12404e9edb5SMark Johnston 	return (error >= 0 ? 0 : -1);
12504e9edb5SMark Johnston }
12604e9edb5SMark Johnston 
12704e9edb5SMark Johnston int
cap_script_wait(cap_channel_t * cap,int * statusp)12804e9edb5SMark Johnston cap_script_wait(cap_channel_t *cap, int *statusp)
12904e9edb5SMark Johnston {
13004e9edb5SMark Johnston #ifdef WITH_CASPER
13104e9edb5SMark Johnston 	nvlist_t *nvl;
13204e9edb5SMark Johnston 	int error;
13304e9edb5SMark Johnston 
13404e9edb5SMark Johnston 	nvl = nvlist_create(0);
13504e9edb5SMark Johnston 	nvlist_add_string(nvl, "cmd", "script_wait");
13604e9edb5SMark Johnston 	nvl = cap_xfer_nvlist(cap, nvl);
13704e9edb5SMark Johnston 	if (nvl == NULL)
13804e9edb5SMark Johnston 		return (-1);
13904e9edb5SMark Johnston 
14004e9edb5SMark Johnston 	error = (int)dnvlist_get_number(nvl, "error", 0);
14104e9edb5SMark Johnston 	if (error == 0)
14204e9edb5SMark Johnston 		*statusp = (int)nvlist_get_number(nvl, "status");
14304e9edb5SMark Johnston 	nvlist_destroy(nvl);
14404e9edb5SMark Johnston 	if (error != 0)
14504e9edb5SMark Johnston 		errno = error;
14604e9edb5SMark Johnston 	return (error == 0 ? 0 : -1);
14704e9edb5SMark Johnston #else
14804e9edb5SMark Johnston 	(void)cap;
14904e9edb5SMark Johnston 	return (script_wait(statusp));
15004e9edb5SMark Johnston #endif
15104e9edb5SMark Johnston }
15204e9edb5SMark Johnston 
15304e9edb5SMark Johnston #ifdef WITH_CASPER
15404e9edb5SMark Johnston static int
script_command(const char * cmd,const nvlist_t * limits,nvlist_t * nvlin,nvlist_t * nvlout)15504e9edb5SMark Johnston script_command(const char *cmd, const nvlist_t *limits, nvlist_t *nvlin,
15604e9edb5SMark Johnston     nvlist_t *nvlout)
15704e9edb5SMark Johnston {
15804e9edb5SMark Johnston 	cap_rights_t rights;
15904e9edb5SMark Johnston 	const char *const *iargv, *const *scripts;
16004e9edb5SMark Johnston 	char **argv;
16104e9edb5SMark Johnston 	size_t argc, i, nscripts;
162*331b84b5SEric van Gyzen 	int error, fd, status;
16304e9edb5SMark Johnston 
16404e9edb5SMark Johnston 	if (strcmp(cmd, "script_wait") == 0) {
16504e9edb5SMark Johnston 		/* Wait for the result of a previous "script_run" command. */
16604e9edb5SMark Johnston 		if (script_wait(&status) == -1)
16704e9edb5SMark Johnston 			return (errno);
16804e9edb5SMark Johnston 		nvlist_add_number(nvlout, "status", status);
16904e9edb5SMark Johnston 		return (0);
17004e9edb5SMark Johnston 	}
17104e9edb5SMark Johnston 	if (strcmp(cmd, "script_run") != 0)
17204e9edb5SMark Johnston 		return (EINVAL);
17304e9edb5SMark Johnston 
17404e9edb5SMark Johnston 	/*
17504e9edb5SMark Johnston 	 * Validate the argv against the limits specified at initialization
17604e9edb5SMark Johnston 	 * time.
17704e9edb5SMark Johnston 	 */
17804e9edb5SMark Johnston 	iargv = nvlist_get_string_array(nvlin, "argv", &argc);
17904e9edb5SMark Johnston 	if (argc == 0)
18004e9edb5SMark Johnston 		return (EINVAL);
18104e9edb5SMark Johnston 	scripts = nvlist_get_string_array(limits, "scripts", &nscripts);
18204e9edb5SMark Johnston 	for (i = 0; i < nscripts; i++)
18304e9edb5SMark Johnston 		if (strcmp(iargv[0], scripts[i]) == 0)
18404e9edb5SMark Johnston 			break;
18504e9edb5SMark Johnston 	if (i == nscripts)
18604e9edb5SMark Johnston 		return (EINVAL);
18704e9edb5SMark Johnston 
18804e9edb5SMark Johnston 	/*
18904e9edb5SMark Johnston 	 * The nvlist API does not permit NULL pointers in an array, so we have
19004e9edb5SMark Johnston 	 * to add the nul terminator ourselves.  Yuck.
19104e9edb5SMark Johnston 	 */
19204e9edb5SMark Johnston 	argv = calloc(argc + 1, sizeof(*argv));
19304e9edb5SMark Johnston 	if (argv == NULL)
19404e9edb5SMark Johnston 		return (errno);
19504e9edb5SMark Johnston 	memcpy(argv, iargv, sizeof(*argv) * argc);
19604e9edb5SMark Johnston 
19704e9edb5SMark Johnston 	fd = script_run(argv);
198*331b84b5SEric van Gyzen 	error = errno;
199*331b84b5SEric van Gyzen 	free(argv);
20004e9edb5SMark Johnston 	if (fd < 0)
201*331b84b5SEric van Gyzen 		return (error);
20204e9edb5SMark Johnston 
20304e9edb5SMark Johnston 	(void)caph_rights_limit(fd, cap_rights_init(&rights, CAP_WRITE));
20404e9edb5SMark Johnston 	nvlist_move_descriptor(nvlout, "fd", fd);
20504e9edb5SMark Johnston 	return (0);
20604e9edb5SMark Johnston }
20704e9edb5SMark Johnston 
20804e9edb5SMark Johnston static int
script_limit(const nvlist_t * oldlimits,const nvlist_t * newlimits)209a792802fSMark Johnston script_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits)
21004e9edb5SMark Johnston {
21104e9edb5SMark Johnston 	const char *name;
21204e9edb5SMark Johnston 	void *cookie;
21304e9edb5SMark Johnston 	int nvtype;
21404e9edb5SMark Johnston 	bool hasscripts;
21504e9edb5SMark Johnston 
21604e9edb5SMark Johnston 	/* Limits may only be set once. */
21704e9edb5SMark Johnston 	if (oldlimits != NULL)
21804e9edb5SMark Johnston 		return (ENOTCAPABLE);
21904e9edb5SMark Johnston 
22004e9edb5SMark Johnston 	cookie = NULL;
22104e9edb5SMark Johnston 	hasscripts = false;
22204e9edb5SMark Johnston 	while ((name = nvlist_next(newlimits, &nvtype, &cookie)) != NULL) {
22304e9edb5SMark Johnston 		if (nvtype == NV_TYPE_STRING_ARRAY &&
22404e9edb5SMark Johnston 		    strcmp(name, "scripts") == 0)
22504e9edb5SMark Johnston 			hasscripts = true;
22604e9edb5SMark Johnston 		else
22704e9edb5SMark Johnston 			return (EINVAL);
22804e9edb5SMark Johnston 	}
22904e9edb5SMark Johnston 	if (!hasscripts)
23004e9edb5SMark Johnston 		return (EINVAL);
23104e9edb5SMark Johnston 	return (0);
23204e9edb5SMark Johnston }
23304e9edb5SMark Johnston 
23404e9edb5SMark Johnston CREATE_SERVICE("rtsold.script", script_limit, script_command, 0);
23504e9edb5SMark Johnston #endif /* WITH_CASPER */
236