1204076Spjd/*-
2204076Spjd * Copyright (c) 2010 The FreeBSD Foundation
3219887Spjd * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>
4204076Spjd * All rights reserved.
5204076Spjd *
6204076Spjd * This software was developed by Pawel Jakub Dawidek under sponsorship from
7204076Spjd * the FreeBSD Foundation.
8204076Spjd *
9204076Spjd * Redistribution and use in source and binary forms, with or without
10204076Spjd * modification, are permitted provided that the following conditions
11204076Spjd * are met:
12204076Spjd * 1. Redistributions of source code must retain the above copyright
13204076Spjd *    notice, this list of conditions and the following disclaimer.
14204076Spjd * 2. Redistributions in binary form must reproduce the above copyright
15204076Spjd *    notice, this list of conditions and the following disclaimer in the
16204076Spjd *    documentation and/or other materials provided with the distribution.
17204076Spjd *
18204076Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
19204076Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20204076Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21204076Spjd * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
22204076Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23204076Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24204076Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25204076Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26204076Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27204076Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28204076Spjd * SUCH DAMAGE.
29204076Spjd */
30204076Spjd
31204076Spjd#include <sys/cdefs.h>
32204076Spjd__FBSDID("$FreeBSD$");
33204076Spjd
34223585Spjd#ifdef HAVE_CAPSICUM
35219847Spjd#include <sys/capability.h>
36223585Spjd#endif
37221899Spjd#include <sys/param.h>
38204076Spjd#include <sys/disk.h>
39204076Spjd#include <sys/ioctl.h>
40221899Spjd#include <sys/jail.h>
41204076Spjd#include <sys/stat.h>
42204076Spjd
43204076Spjd#include <errno.h>
44204076Spjd#include <fcntl.h>
45218048Spjd#include <pwd.h>
46219815Spjd#include <stdarg.h>
47219847Spjd#include <stdbool.h>
48219815Spjd#include <stdio.h>
49219815Spjd#include <string.h>
50218048Spjd#include <unistd.h>
51204076Spjd
52204076Spjd#include <pjdlog.h>
53204076Spjd
54204076Spjd#include "hast.h"
55204076Spjd#include "subr.h"
56204076Spjd
57204076Spjdint
58219815Spjdvsnprlcat(char *str, size_t size, const char *fmt, va_list ap)
59219815Spjd{
60219815Spjd	size_t len;
61219815Spjd
62219815Spjd	len = strlen(str);
63219815Spjd	return (vsnprintf(str + len, size - len, fmt, ap));
64219815Spjd}
65219815Spjd
66219815Spjdint
67219815Spjdsnprlcat(char *str, size_t size, const char *fmt, ...)
68219815Spjd{
69219815Spjd	va_list ap;
70219815Spjd	int result;
71219815Spjd
72219815Spjd	va_start(ap, fmt);
73219815Spjd	result = vsnprlcat(str, size, fmt, ap);
74219815Spjd	va_end(ap);
75219815Spjd	return (result);
76219815Spjd}
77219815Spjd
78219815Spjdint
79204076Spjdprovinfo(struct hast_resource *res, bool dowrite)
80204076Spjd{
81204076Spjd	struct stat sb;
82204076Spjd
83218138Spjd	PJDLOG_ASSERT(res->hr_localpath != NULL &&
84218138Spjd	    res->hr_localpath[0] != '\0');
85204076Spjd
86204076Spjd	if (res->hr_localfd == -1) {
87204076Spjd		res->hr_localfd = open(res->hr_localpath,
88204076Spjd		    dowrite ? O_RDWR : O_RDONLY);
89231017Strociny		if (res->hr_localfd == -1) {
90229509Strociny			pjdlog_errno(LOG_ERR, "Unable to open %s",
91229509Strociny			    res->hr_localpath);
92204076Spjd			return (-1);
93204076Spjd		}
94204076Spjd	}
95231017Strociny	if (fstat(res->hr_localfd, &sb) == -1) {
96229509Strociny		pjdlog_errno(LOG_ERR, "Unable to stat %s", res->hr_localpath);
97204076Spjd		return (-1);
98204076Spjd	}
99204076Spjd	if (S_ISCHR(sb.st_mode)) {
100204076Spjd		/*
101204076Spjd		 * If this is character device, it is most likely GEOM provider.
102204076Spjd		 */
103204076Spjd		if (ioctl(res->hr_localfd, DIOCGMEDIASIZE,
104231017Strociny		    &res->hr_local_mediasize) == -1) {
105229509Strociny			pjdlog_errno(LOG_ERR,
106204076Spjd			    "Unable obtain provider %s mediasize",
107229509Strociny			    res->hr_localpath);
108204076Spjd			return (-1);
109204076Spjd		}
110204076Spjd		if (ioctl(res->hr_localfd, DIOCGSECTORSIZE,
111231017Strociny		    &res->hr_local_sectorsize) == -1) {
112229509Strociny			pjdlog_errno(LOG_ERR,
113204076Spjd			    "Unable obtain provider %s sectorsize",
114229509Strociny			    res->hr_localpath);
115204076Spjd			return (-1);
116204076Spjd		}
117204076Spjd	} else if (S_ISREG(sb.st_mode)) {
118204076Spjd		/*
119204076Spjd		 * We also support regular files for which we hardcode
120204076Spjd		 * sector size of 512 bytes.
121204076Spjd		 */
122204076Spjd		res->hr_local_mediasize = sb.st_size;
123204076Spjd		res->hr_local_sectorsize = 512;
124204076Spjd	} else {
125204076Spjd		/*
126204076Spjd		 * We support no other file types.
127204076Spjd		 */
128204076Spjd		pjdlog_error("%s is neither GEOM provider nor regular file.",
129204076Spjd		    res->hr_localpath);
130204076Spjd		errno = EFTYPE;
131204076Spjd		return (-1);
132204076Spjd	}
133204076Spjd	return (0);
134204076Spjd}
135204076Spjd
136204076Spjdconst char *
137204076Spjdrole2str(int role)
138204076Spjd{
139204076Spjd
140219864Spjd	switch (role) {
141219864Spjd	case HAST_ROLE_INIT:
142204076Spjd		return ("init");
143219864Spjd	case HAST_ROLE_PRIMARY:
144204076Spjd		return ("primary");
145219864Spjd	case HAST_ROLE_SECONDARY:
146204076Spjd		return ("secondary");
147204076Spjd	}
148204076Spjd	return ("unknown");
149204076Spjd}
150218048Spjd
151218048Spjdint
152231017Strocinydrop_privs(const struct hast_resource *res)
153218048Spjd{
154221899Spjd	char jailhost[sizeof(res->hr_name) * 2];
155221899Spjd	struct jail jailst;
156218048Spjd	struct passwd *pw;
157218048Spjd	uid_t ruid, euid, suid;
158218048Spjd	gid_t rgid, egid, sgid;
159218048Spjd	gid_t gidset[1];
160221899Spjd	bool capsicum, jailed;
161218048Spjd
162218048Spjd	/*
163218048Spjd	 * According to getpwnam(3) we have to clear errno before calling the
164218048Spjd	 * function to be able to distinguish between an error and missing
165218048Spjd	 * entry (with is not treated as error by getpwnam(3)).
166218048Spjd	 */
167218048Spjd	errno = 0;
168218048Spjd	pw = getpwnam(HAST_USER);
169218048Spjd	if (pw == NULL) {
170218048Spjd		if (errno != 0) {
171229509Strociny			pjdlog_errno(LOG_ERR,
172229509Strociny			    "Unable to find info about '%s' user", HAST_USER);
173218048Spjd			return (-1);
174218048Spjd		} else {
175218048Spjd			pjdlog_error("'%s' user doesn't exist.", HAST_USER);
176218048Spjd			errno = ENOENT;
177218048Spjd			return (-1);
178218048Spjd		}
179218048Spjd	}
180221899Spjd
181221899Spjd	bzero(&jailst, sizeof(jailst));
182221899Spjd	jailst.version = JAIL_API_VERSION;
183221899Spjd	jailst.path = pw->pw_dir;
184221899Spjd	if (res == NULL) {
185221899Spjd		(void)snprintf(jailhost, sizeof(jailhost), "hastctl");
186221899Spjd	} else {
187221899Spjd		(void)snprintf(jailhost, sizeof(jailhost), "hastd: %s (%s)",
188221899Spjd		    res->hr_name, role2str(res->hr_role));
189218048Spjd	}
190221899Spjd	jailst.hostname = jailhost;
191221899Spjd	jailst.jailname = NULL;
192221899Spjd	jailst.ip4s = 0;
193221899Spjd	jailst.ip4 = NULL;
194221899Spjd	jailst.ip6s = 0;
195221899Spjd	jailst.ip6 = NULL;
196221899Spjd	if (jail(&jailst) >= 0) {
197221899Spjd		jailed = true;
198221899Spjd	} else {
199221899Spjd		jailed = false;
200221899Spjd		pjdlog_errno(LOG_WARNING,
201221899Spjd		    "Unable to jail to directory to %s", pw->pw_dir);
202221899Spjd		if (chroot(pw->pw_dir) == -1) {
203229509Strociny			pjdlog_errno(LOG_ERR,
204221899Spjd			    "Unable to change root directory to %s",
205229509Strociny			    pw->pw_dir);
206221899Spjd			return (-1);
207221899Spjd		}
208221899Spjd	}
209218048Spjd	PJDLOG_VERIFY(chdir("/") == 0);
210218048Spjd	gidset[0] = pw->pw_gid;
211218048Spjd	if (setgroups(1, gidset) == -1) {
212229509Strociny		pjdlog_errno(LOG_ERR, "Unable to set groups to gid %u",
213229509Strociny		    (unsigned int)pw->pw_gid);
214218048Spjd		return (-1);
215218048Spjd	}
216218048Spjd	if (setgid(pw->pw_gid) == -1) {
217229509Strociny		pjdlog_errno(LOG_ERR, "Unable to set gid to %u",
218229509Strociny		    (unsigned int)pw->pw_gid);
219218048Spjd		return (-1);
220218048Spjd	}
221218048Spjd	if (setuid(pw->pw_uid) == -1) {
222229509Strociny		pjdlog_errno(LOG_ERR, "Unable to set uid to %u",
223229509Strociny		    (unsigned int)pw->pw_uid);
224218048Spjd		return (-1);
225218048Spjd	}
226218048Spjd
227222224Spjd	/*
228222224Spjd	 * Until capsicum doesn't allow ioctl(2) we cannot use it to sandbox
229222224Spjd	 * primary and secondary worker processes, as primary uses GGATE
230222224Spjd	 * ioctls and secondary uses ioctls to handle BIO_DELETE and BIO_FLUSH.
231222224Spjd	 * For now capsicum is only used to sandbox hastctl.
232222224Spjd	 */
233223585Spjd#ifdef HAVE_CAPSICUM
234223584Spjd	if (res == NULL) {
235221899Spjd		capsicum = (cap_enter() == 0);
236223584Spjd		if (!capsicum) {
237223584Spjd			pjdlog_common(LOG_DEBUG, 1, errno,
238223584Spjd			    "Unable to sandbox using capsicum");
239223584Spjd		}
240223584Spjd	} else
241223585Spjd#endif
242221899Spjd		capsicum = false;
243221898Spjd
244218048Spjd	/*
245218048Spjd	 * Better be sure that everything succeeded.
246218048Spjd	 */
247218048Spjd	PJDLOG_VERIFY(getresuid(&ruid, &euid, &suid) == 0);
248218048Spjd	PJDLOG_VERIFY(ruid == pw->pw_uid);
249218048Spjd	PJDLOG_VERIFY(euid == pw->pw_uid);
250218048Spjd	PJDLOG_VERIFY(suid == pw->pw_uid);
251218048Spjd	PJDLOG_VERIFY(getresgid(&rgid, &egid, &sgid) == 0);
252218048Spjd	PJDLOG_VERIFY(rgid == pw->pw_gid);
253218048Spjd	PJDLOG_VERIFY(egid == pw->pw_gid);
254218048Spjd	PJDLOG_VERIFY(sgid == pw->pw_gid);
255218048Spjd	PJDLOG_VERIFY(getgroups(0, NULL) == 1);
256218048Spjd	PJDLOG_VERIFY(getgroups(1, gidset) == 1);
257218048Spjd	PJDLOG_VERIFY(gidset[0] == pw->pw_gid);
258218048Spjd
259219847Spjd	pjdlog_debug(1,
260221899Spjd	    "Privileges successfully dropped using %s%s+setgid+setuid.",
261221899Spjd	    capsicum ? "capsicum+" : "", jailed ? "jail" : "chroot");
262219847Spjd
263218048Spjd	return (0);
264218048Spjd}
265