xref: /freebsd-src/sbin/hastd/subr.c (revision 32e86a82f54826f14ea381affa6674db3aa3b5ae)
132115b10SPawel Jakub Dawidek /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
31de7b4b8SPedro F. Giffuni  *
432115b10SPawel Jakub Dawidek  * Copyright (c) 2010 The FreeBSD Foundation
56d51b7d5SPawel Jakub Dawidek  * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>
632115b10SPawel Jakub Dawidek  * All rights reserved.
732115b10SPawel Jakub Dawidek  *
832115b10SPawel Jakub Dawidek  * This software was developed by Pawel Jakub Dawidek under sponsorship from
932115b10SPawel Jakub Dawidek  * the FreeBSD Foundation.
1032115b10SPawel Jakub Dawidek  *
1132115b10SPawel Jakub Dawidek  * Redistribution and use in source and binary forms, with or without
1232115b10SPawel Jakub Dawidek  * modification, are permitted provided that the following conditions
1332115b10SPawel Jakub Dawidek  * are met:
1432115b10SPawel Jakub Dawidek  * 1. Redistributions of source code must retain the above copyright
1532115b10SPawel Jakub Dawidek  *    notice, this list of conditions and the following disclaimer.
1632115b10SPawel Jakub Dawidek  * 2. Redistributions in binary form must reproduce the above copyright
1732115b10SPawel Jakub Dawidek  *    notice, this list of conditions and the following disclaimer in the
1832115b10SPawel Jakub Dawidek  *    documentation and/or other materials provided with the distribution.
1932115b10SPawel Jakub Dawidek  *
2032115b10SPawel Jakub Dawidek  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
2132115b10SPawel Jakub Dawidek  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2232115b10SPawel Jakub Dawidek  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2332115b10SPawel Jakub Dawidek  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
2432115b10SPawel Jakub Dawidek  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2532115b10SPawel Jakub Dawidek  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2632115b10SPawel Jakub Dawidek  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2732115b10SPawel Jakub Dawidek  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2832115b10SPawel Jakub Dawidek  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2932115b10SPawel Jakub Dawidek  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3032115b10SPawel Jakub Dawidek  * SUCH DAMAGE.
3132115b10SPawel Jakub Dawidek  */
3232115b10SPawel Jakub Dawidek 
330cddb12fSPawel Jakub Dawidek #include <sys/param.h>
3432115b10SPawel Jakub Dawidek #include <sys/disk.h>
3532115b10SPawel Jakub Dawidek #include <sys/ioctl.h>
360cddb12fSPawel Jakub Dawidek #include <sys/jail.h>
3732115b10SPawel Jakub Dawidek #include <sys/stat.h>
3851ea07d7SPawel Jakub Dawidek #ifdef HAVE_CAPSICUM
39b881b8beSRobert Watson #include <sys/capsicum.h>
4051ea07d7SPawel Jakub Dawidek #include <geom/gate/g_gate.h>
4151ea07d7SPawel Jakub Dawidek #endif
4232115b10SPawel Jakub Dawidek 
4332115b10SPawel Jakub Dawidek #include <errno.h>
4432115b10SPawel Jakub Dawidek #include <fcntl.h>
4549499e98SPawel Jakub Dawidek #include <pwd.h>
469925a680SPawel Jakub Dawidek #include <stdarg.h>
474d8dc3b8SPawel Jakub Dawidek #include <stdbool.h>
489925a680SPawel Jakub Dawidek #include <stdio.h>
499925a680SPawel Jakub Dawidek #include <string.h>
5049499e98SPawel Jakub Dawidek #include <unistd.h>
5132115b10SPawel Jakub Dawidek 
5232115b10SPawel Jakub Dawidek #include <pjdlog.h>
5332115b10SPawel Jakub Dawidek 
5432115b10SPawel Jakub Dawidek #include "hast.h"
5532115b10SPawel Jakub Dawidek #include "subr.h"
5632115b10SPawel Jakub Dawidek 
5732115b10SPawel Jakub Dawidek int
vsnprlcat(char * str,size_t size,const char * fmt,va_list ap)589925a680SPawel Jakub Dawidek vsnprlcat(char *str, size_t size, const char *fmt, va_list ap)
599925a680SPawel Jakub Dawidek {
609925a680SPawel Jakub Dawidek 	size_t len;
619925a680SPawel Jakub Dawidek 
629925a680SPawel Jakub Dawidek 	len = strlen(str);
639925a680SPawel Jakub Dawidek 	return (vsnprintf(str + len, size - len, fmt, ap));
649925a680SPawel Jakub Dawidek }
659925a680SPawel Jakub Dawidek 
669925a680SPawel Jakub Dawidek int
snprlcat(char * str,size_t size,const char * fmt,...)679925a680SPawel Jakub Dawidek snprlcat(char *str, size_t size, const char *fmt, ...)
689925a680SPawel Jakub Dawidek {
699925a680SPawel Jakub Dawidek 	va_list ap;
709925a680SPawel Jakub Dawidek 	int result;
719925a680SPawel Jakub Dawidek 
729925a680SPawel Jakub Dawidek 	va_start(ap, fmt);
739925a680SPawel Jakub Dawidek 	result = vsnprlcat(str, size, fmt, ap);
749925a680SPawel Jakub Dawidek 	va_end(ap);
759925a680SPawel Jakub Dawidek 	return (result);
769925a680SPawel Jakub Dawidek }
779925a680SPawel Jakub Dawidek 
789925a680SPawel Jakub Dawidek int
provinfo(struct hast_resource * res,bool dowrite)7932115b10SPawel Jakub Dawidek provinfo(struct hast_resource *res, bool dowrite)
8032115b10SPawel Jakub Dawidek {
8132115b10SPawel Jakub Dawidek 	struct stat sb;
8232115b10SPawel Jakub Dawidek 
832ec483c5SPawel Jakub Dawidek 	PJDLOG_ASSERT(res->hr_localpath != NULL &&
842ec483c5SPawel Jakub Dawidek 	    res->hr_localpath[0] != '\0');
8532115b10SPawel Jakub Dawidek 
8632115b10SPawel Jakub Dawidek 	if (res->hr_localfd == -1) {
8732115b10SPawel Jakub Dawidek 		res->hr_localfd = open(res->hr_localpath,
8832115b10SPawel Jakub Dawidek 		    dowrite ? O_RDWR : O_RDONLY);
892b1b224dSPawel Jakub Dawidek 		if (res->hr_localfd == -1) {
901ebc0407SPawel Jakub Dawidek 			pjdlog_errno(LOG_ERR, "Unable to open %s",
911ebc0407SPawel Jakub Dawidek 			    res->hr_localpath);
9232115b10SPawel Jakub Dawidek 			return (-1);
9332115b10SPawel Jakub Dawidek 		}
9432115b10SPawel Jakub Dawidek 	}
952b1b224dSPawel Jakub Dawidek 	if (fstat(res->hr_localfd, &sb) == -1) {
961ebc0407SPawel Jakub Dawidek 		pjdlog_errno(LOG_ERR, "Unable to stat %s", res->hr_localpath);
9732115b10SPawel Jakub Dawidek 		return (-1);
9832115b10SPawel Jakub Dawidek 	}
9932115b10SPawel Jakub Dawidek 	if (S_ISCHR(sb.st_mode)) {
10032115b10SPawel Jakub Dawidek 		/*
10132115b10SPawel Jakub Dawidek 		 * If this is character device, it is most likely GEOM provider.
10232115b10SPawel Jakub Dawidek 		 */
10332115b10SPawel Jakub Dawidek 		if (ioctl(res->hr_localfd, DIOCGMEDIASIZE,
1042b1b224dSPawel Jakub Dawidek 		    &res->hr_local_mediasize) == -1) {
1051ebc0407SPawel Jakub Dawidek 			pjdlog_errno(LOG_ERR,
10632115b10SPawel Jakub Dawidek 			    "Unable obtain provider %s mediasize",
1071ebc0407SPawel Jakub Dawidek 			    res->hr_localpath);
10832115b10SPawel Jakub Dawidek 			return (-1);
10932115b10SPawel Jakub Dawidek 		}
11032115b10SPawel Jakub Dawidek 		if (ioctl(res->hr_localfd, DIOCGSECTORSIZE,
1112b1b224dSPawel Jakub Dawidek 		    &res->hr_local_sectorsize) == -1) {
1121ebc0407SPawel Jakub Dawidek 			pjdlog_errno(LOG_ERR,
11332115b10SPawel Jakub Dawidek 			    "Unable obtain provider %s sectorsize",
1141ebc0407SPawel Jakub Dawidek 			    res->hr_localpath);
11532115b10SPawel Jakub Dawidek 			return (-1);
11632115b10SPawel Jakub Dawidek 		}
11732115b10SPawel Jakub Dawidek 	} else if (S_ISREG(sb.st_mode)) {
11832115b10SPawel Jakub Dawidek 		/*
11932115b10SPawel Jakub Dawidek 		 * We also support regular files for which we hardcode
12032115b10SPawel Jakub Dawidek 		 * sector size of 512 bytes.
12132115b10SPawel Jakub Dawidek 		 */
12232115b10SPawel Jakub Dawidek 		res->hr_local_mediasize = sb.st_size;
12332115b10SPawel Jakub Dawidek 		res->hr_local_sectorsize = 512;
12432115b10SPawel Jakub Dawidek 	} else {
12532115b10SPawel Jakub Dawidek 		/*
12632115b10SPawel Jakub Dawidek 		 * We support no other file types.
12732115b10SPawel Jakub Dawidek 		 */
12832115b10SPawel Jakub Dawidek 		pjdlog_error("%s is neither GEOM provider nor regular file.",
12932115b10SPawel Jakub Dawidek 		    res->hr_localpath);
13032115b10SPawel Jakub Dawidek 		errno = EFTYPE;
13132115b10SPawel Jakub Dawidek 		return (-1);
13232115b10SPawel Jakub Dawidek 	}
13332115b10SPawel Jakub Dawidek 	return (0);
13432115b10SPawel Jakub Dawidek }
13532115b10SPawel Jakub Dawidek 
13632115b10SPawel Jakub Dawidek const char *
role2str(int role)13732115b10SPawel Jakub Dawidek role2str(int role)
13832115b10SPawel Jakub Dawidek {
13932115b10SPawel Jakub Dawidek 
14032115b10SPawel Jakub Dawidek 	switch (role) {
14132115b10SPawel Jakub Dawidek 	case HAST_ROLE_INIT:
14232115b10SPawel Jakub Dawidek 		return ("init");
14332115b10SPawel Jakub Dawidek 	case HAST_ROLE_PRIMARY:
14432115b10SPawel Jakub Dawidek 		return ("primary");
14532115b10SPawel Jakub Dawidek 	case HAST_ROLE_SECONDARY:
14632115b10SPawel Jakub Dawidek 		return ("secondary");
14732115b10SPawel Jakub Dawidek 	}
14832115b10SPawel Jakub Dawidek 	return ("unknown");
14932115b10SPawel Jakub Dawidek }
15049499e98SPawel Jakub Dawidek 
15149499e98SPawel Jakub Dawidek int
drop_privs(const struct hast_resource * res)152f78fe260SPawel Jakub Dawidek drop_privs(const struct hast_resource *res)
15349499e98SPawel Jakub Dawidek {
1540cddb12fSPawel Jakub Dawidek 	char jailhost[sizeof(res->hr_name) * 2];
1550cddb12fSPawel Jakub Dawidek 	struct jail jailst;
15649499e98SPawel Jakub Dawidek 	struct passwd *pw;
15749499e98SPawel Jakub Dawidek 	uid_t ruid, euid, suid;
15849499e98SPawel Jakub Dawidek 	gid_t rgid, egid, sgid;
15949499e98SPawel Jakub Dawidek 	gid_t gidset[1];
1600cddb12fSPawel Jakub Dawidek 	bool capsicum, jailed;
1614d8dc3b8SPawel Jakub Dawidek 
16249499e98SPawel Jakub Dawidek 	/*
16349499e98SPawel Jakub Dawidek 	 * According to getpwnam(3) we have to clear errno before calling the
16449499e98SPawel Jakub Dawidek 	 * function to be able to distinguish between an error and missing
16549499e98SPawel Jakub Dawidek 	 * entry (with is not treated as error by getpwnam(3)).
16649499e98SPawel Jakub Dawidek 	 */
16749499e98SPawel Jakub Dawidek 	errno = 0;
16849499e98SPawel Jakub Dawidek 	pw = getpwnam(HAST_USER);
16949499e98SPawel Jakub Dawidek 	if (pw == NULL) {
17049499e98SPawel Jakub Dawidek 		if (errno != 0) {
1711ebc0407SPawel Jakub Dawidek 			pjdlog_errno(LOG_ERR,
1721ebc0407SPawel Jakub Dawidek 			    "Unable to find info about '%s' user", HAST_USER);
17349499e98SPawel Jakub Dawidek 			return (-1);
17449499e98SPawel Jakub Dawidek 		} else {
17549499e98SPawel Jakub Dawidek 			pjdlog_error("'%s' user doesn't exist.", HAST_USER);
17649499e98SPawel Jakub Dawidek 			errno = ENOENT;
17749499e98SPawel Jakub Dawidek 			return (-1);
17849499e98SPawel Jakub Dawidek 		}
17949499e98SPawel Jakub Dawidek 	}
1800cddb12fSPawel Jakub Dawidek 
1810cddb12fSPawel Jakub Dawidek 	bzero(&jailst, sizeof(jailst));
1820cddb12fSPawel Jakub Dawidek 	jailst.version = JAIL_API_VERSION;
1830cddb12fSPawel Jakub Dawidek 	jailst.path = pw->pw_dir;
1840cddb12fSPawel Jakub Dawidek 	if (res == NULL) {
1850cddb12fSPawel Jakub Dawidek 		(void)snprintf(jailhost, sizeof(jailhost), "hastctl");
1860cddb12fSPawel Jakub Dawidek 	} else {
1870cddb12fSPawel Jakub Dawidek 		(void)snprintf(jailhost, sizeof(jailhost), "hastd: %s (%s)",
1880cddb12fSPawel Jakub Dawidek 		    res->hr_name, role2str(res->hr_role));
1890cddb12fSPawel Jakub Dawidek 	}
1900cddb12fSPawel Jakub Dawidek 	jailst.hostname = jailhost;
1910cddb12fSPawel Jakub Dawidek 	jailst.jailname = NULL;
1920cddb12fSPawel Jakub Dawidek 	jailst.ip4s = 0;
1930cddb12fSPawel Jakub Dawidek 	jailst.ip4 = NULL;
1940cddb12fSPawel Jakub Dawidek 	jailst.ip6s = 0;
1950cddb12fSPawel Jakub Dawidek 	jailst.ip6 = NULL;
1960cddb12fSPawel Jakub Dawidek 	if (jail(&jailst) >= 0) {
1970cddb12fSPawel Jakub Dawidek 		jailed = true;
1980cddb12fSPawel Jakub Dawidek 	} else {
1990cddb12fSPawel Jakub Dawidek 		jailed = false;
2000cddb12fSPawel Jakub Dawidek 		pjdlog_errno(LOG_WARNING,
2010cddb12fSPawel Jakub Dawidek 		    "Unable to jail to directory to %s", pw->pw_dir);
20249499e98SPawel Jakub Dawidek 		if (chroot(pw->pw_dir) == -1) {
2031ebc0407SPawel Jakub Dawidek 			pjdlog_errno(LOG_ERR,
2040cddb12fSPawel Jakub Dawidek 			    "Unable to change root directory to %s",
2051ebc0407SPawel Jakub Dawidek 			    pw->pw_dir);
20649499e98SPawel Jakub Dawidek 			return (-1);
20749499e98SPawel Jakub Dawidek 		}
2080cddb12fSPawel Jakub Dawidek 	}
20949499e98SPawel Jakub Dawidek 	PJDLOG_VERIFY(chdir("/") == 0);
21049499e98SPawel Jakub Dawidek 	gidset[0] = pw->pw_gid;
21149499e98SPawel Jakub Dawidek 	if (setgroups(1, gidset) == -1) {
2121ebc0407SPawel Jakub Dawidek 		pjdlog_errno(LOG_ERR, "Unable to set groups to gid %u",
2131ebc0407SPawel Jakub Dawidek 		    (unsigned int)pw->pw_gid);
21449499e98SPawel Jakub Dawidek 		return (-1);
21549499e98SPawel Jakub Dawidek 	}
21649499e98SPawel Jakub Dawidek 	if (setgid(pw->pw_gid) == -1) {
2171ebc0407SPawel Jakub Dawidek 		pjdlog_errno(LOG_ERR, "Unable to set gid to %u",
2181ebc0407SPawel Jakub Dawidek 		    (unsigned int)pw->pw_gid);
21949499e98SPawel Jakub Dawidek 		return (-1);
22049499e98SPawel Jakub Dawidek 	}
22149499e98SPawel Jakub Dawidek 	if (setuid(pw->pw_uid) == -1) {
2221ebc0407SPawel Jakub Dawidek 		pjdlog_errno(LOG_ERR, "Unable to set uid to %u",
2231ebc0407SPawel Jakub Dawidek 		    (unsigned int)pw->pw_uid);
22449499e98SPawel Jakub Dawidek 		return (-1);
22549499e98SPawel Jakub Dawidek 	}
22649499e98SPawel Jakub Dawidek 
227699b26bdSPawel Jakub Dawidek #ifdef HAVE_CAPSICUM
2280cddb12fSPawel Jakub Dawidek 	capsicum = (cap_enter() == 0);
229133d75edSPawel Jakub Dawidek 	if (!capsicum) {
230133d75edSPawel Jakub Dawidek 		pjdlog_common(LOG_DEBUG, 1, errno,
231133d75edSPawel Jakub Dawidek 		    "Unable to sandbox using capsicum");
23251ea07d7SPawel Jakub Dawidek 	} else if (res != NULL) {
2337008be5bSPawel Jakub Dawidek 		cap_rights_t rights;
23451ea07d7SPawel Jakub Dawidek 		static const unsigned long geomcmds[] = {
23551ea07d7SPawel Jakub Dawidek 		    DIOCGDELETE,
23651ea07d7SPawel Jakub Dawidek 		    DIOCGFLUSH
23751ea07d7SPawel Jakub Dawidek 		};
23851ea07d7SPawel Jakub Dawidek 
23951ea07d7SPawel Jakub Dawidek 		PJDLOG_ASSERT(res->hr_role == HAST_ROLE_PRIMARY ||
24051ea07d7SPawel Jakub Dawidek 		    res->hr_role == HAST_ROLE_SECONDARY);
24151ea07d7SPawel Jakub Dawidek 
2427008be5bSPawel Jakub Dawidek 		cap_rights_init(&rights, CAP_FLOCK, CAP_IOCTL, CAP_PREAD,
2437008be5bSPawel Jakub Dawidek 		    CAP_PWRITE);
2447008be5bSPawel Jakub Dawidek 		if (cap_rights_limit(res->hr_localfd, &rights) == -1) {
24551ea07d7SPawel Jakub Dawidek 			pjdlog_errno(LOG_ERR,
24651ea07d7SPawel Jakub Dawidek 			    "Unable to limit capability rights on local descriptor");
247133d75edSPawel Jakub Dawidek 		}
24851ea07d7SPawel Jakub Dawidek 		if (cap_ioctls_limit(res->hr_localfd, geomcmds,
24946df5db8SMarcelo Araujo 		    nitems(geomcmds)) == -1) {
25051ea07d7SPawel Jakub Dawidek 			pjdlog_errno(LOG_ERR,
25151ea07d7SPawel Jakub Dawidek 			    "Unable to limit allowed GEOM ioctls");
25251ea07d7SPawel Jakub Dawidek 		}
25351ea07d7SPawel Jakub Dawidek 
25451ea07d7SPawel Jakub Dawidek 		if (res->hr_role == HAST_ROLE_PRIMARY) {
25551ea07d7SPawel Jakub Dawidek 			static const unsigned long ggatecmds[] = {
25651ea07d7SPawel Jakub Dawidek 			    G_GATE_CMD_MODIFY,
25751ea07d7SPawel Jakub Dawidek 			    G_GATE_CMD_START,
25851ea07d7SPawel Jakub Dawidek 			    G_GATE_CMD_DONE,
25951ea07d7SPawel Jakub Dawidek 			    G_GATE_CMD_DESTROY
26051ea07d7SPawel Jakub Dawidek 			};
26151ea07d7SPawel Jakub Dawidek 
2627008be5bSPawel Jakub Dawidek 			cap_rights_init(&rights, CAP_IOCTL);
2637008be5bSPawel Jakub Dawidek 			if (cap_rights_limit(res->hr_ggatefd, &rights) == -1) {
26451ea07d7SPawel Jakub Dawidek 				pjdlog_errno(LOG_ERR,
26551ea07d7SPawel Jakub Dawidek 				    "Unable to limit capability rights to CAP_IOCTL on ggate descriptor");
26651ea07d7SPawel Jakub Dawidek 			}
26751ea07d7SPawel Jakub Dawidek 			if (cap_ioctls_limit(res->hr_ggatefd, ggatecmds,
26846df5db8SMarcelo Araujo 			    nitems(ggatecmds)) == -1) {
26951ea07d7SPawel Jakub Dawidek 				pjdlog_errno(LOG_ERR,
27051ea07d7SPawel Jakub Dawidek 				    "Unable to limit allowed ggate ioctls");
27151ea07d7SPawel Jakub Dawidek 			}
27251ea07d7SPawel Jakub Dawidek 		}
27351ea07d7SPawel Jakub Dawidek 	}
27451ea07d7SPawel Jakub Dawidek #else
275bcc9f321SPawel Jakub Dawidek 	capsicum = false;
27651ea07d7SPawel Jakub Dawidek #endif
277bcc9f321SPawel Jakub Dawidek 
27849499e98SPawel Jakub Dawidek 	/*
27949499e98SPawel Jakub Dawidek 	 * Better be sure that everything succeeded.
28049499e98SPawel Jakub Dawidek 	 */
28149499e98SPawel Jakub Dawidek 	PJDLOG_VERIFY(getresuid(&ruid, &euid, &suid) == 0);
28249499e98SPawel Jakub Dawidek 	PJDLOG_VERIFY(ruid == pw->pw_uid);
28349499e98SPawel Jakub Dawidek 	PJDLOG_VERIFY(euid == pw->pw_uid);
28449499e98SPawel Jakub Dawidek 	PJDLOG_VERIFY(suid == pw->pw_uid);
28549499e98SPawel Jakub Dawidek 	PJDLOG_VERIFY(getresgid(&rgid, &egid, &sgid) == 0);
28649499e98SPawel Jakub Dawidek 	PJDLOG_VERIFY(rgid == pw->pw_gid);
28749499e98SPawel Jakub Dawidek 	PJDLOG_VERIFY(egid == pw->pw_gid);
28849499e98SPawel Jakub Dawidek 	PJDLOG_VERIFY(sgid == pw->pw_gid);
28949499e98SPawel Jakub Dawidek 	PJDLOG_VERIFY(getgroups(0, NULL) == 1);
29049499e98SPawel Jakub Dawidek 	PJDLOG_VERIFY(getgroups(1, gidset) == 1);
29149499e98SPawel Jakub Dawidek 	PJDLOG_VERIFY(gidset[0] == pw->pw_gid);
29249499e98SPawel Jakub Dawidek 
2934d8dc3b8SPawel Jakub Dawidek 	pjdlog_debug(1,
2940cddb12fSPawel Jakub Dawidek 	    "Privileges successfully dropped using %s%s+setgid+setuid.",
2950cddb12fSPawel Jakub Dawidek 	    capsicum ? "capsicum+" : "", jailed ? "jail" : "chroot");
2964d8dc3b8SPawel Jakub Dawidek 
29749499e98SPawel Jakub Dawidek 	return (0);
29849499e98SPawel Jakub Dawidek }
299