subr.c revision 219847
1204076Spjd/*-
2204076Spjd * Copyright (c) 2010 The FreeBSD Foundation
3204076Spjd * All rights reserved.
4204076Spjd *
5204076Spjd * This software was developed by Pawel Jakub Dawidek under sponsorship from
6204076Spjd * the FreeBSD Foundation.
7204076Spjd *
8204076Spjd * Redistribution and use in source and binary forms, with or without
9204076Spjd * modification, are permitted provided that the following conditions
10204076Spjd * are met:
11204076Spjd * 1. Redistributions of source code must retain the above copyright
12204076Spjd *    notice, this list of conditions and the following disclaimer.
13204076Spjd * 2. Redistributions in binary form must reproduce the above copyright
14204076Spjd *    notice, this list of conditions and the following disclaimer in the
15204076Spjd *    documentation and/or other materials provided with the distribution.
16204076Spjd *
17204076Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18204076Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19204076Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20204076Spjd * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21204076Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22204076Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23204076Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24204076Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25204076Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26204076Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27204076Spjd * SUCH DAMAGE.
28204076Spjd */
29204076Spjd
30204076Spjd#include <sys/cdefs.h>
31204076Spjd__FBSDID("$FreeBSD: head/sbin/hastd/subr.c 219847 2011-03-21 21:31:50Z pjd $");
32204076Spjd
33219847Spjd#include <sys/capability.h>
34204076Spjd#include <sys/types.h>
35204076Spjd#include <sys/disk.h>
36204076Spjd#include <sys/ioctl.h>
37204076Spjd#include <sys/stat.h>
38204076Spjd
39204076Spjd#include <errno.h>
40204076Spjd#include <fcntl.h>
41218048Spjd#include <pwd.h>
42219815Spjd#include <stdarg.h>
43219847Spjd#include <stdbool.h>
44219815Spjd#include <stdio.h>
45219815Spjd#include <string.h>
46218048Spjd#include <unistd.h>
47204076Spjd
48204076Spjd#include <pjdlog.h>
49204076Spjd
50204076Spjd#include "hast.h"
51204076Spjd#include "subr.h"
52204076Spjd
53204076Spjdint
54219815Spjdvsnprlcat(char *str, size_t size, const char *fmt, va_list ap)
55219815Spjd{
56219815Spjd	size_t len;
57219815Spjd
58219815Spjd	len = strlen(str);
59219815Spjd	return (vsnprintf(str + len, size - len, fmt, ap));
60219815Spjd}
61219815Spjd
62219815Spjdint
63219815Spjdsnprlcat(char *str, size_t size, const char *fmt, ...)
64219815Spjd{
65219815Spjd	va_list ap;
66219815Spjd	int result;
67219815Spjd
68219815Spjd	va_start(ap, fmt);
69219815Spjd	result = vsnprlcat(str, size, fmt, ap);
70219815Spjd	va_end(ap);
71219815Spjd	return (result);
72219815Spjd}
73219815Spjd
74219815Spjdint
75204076Spjdprovinfo(struct hast_resource *res, bool dowrite)
76204076Spjd{
77204076Spjd	struct stat sb;
78204076Spjd
79218138Spjd	PJDLOG_ASSERT(res->hr_localpath != NULL &&
80218138Spjd	    res->hr_localpath[0] != '\0');
81204076Spjd
82204076Spjd	if (res->hr_localfd == -1) {
83204076Spjd		res->hr_localfd = open(res->hr_localpath,
84204076Spjd		    dowrite ? O_RDWR : O_RDONLY);
85204076Spjd		if (res->hr_localfd < 0) {
86204076Spjd			KEEP_ERRNO(pjdlog_errno(LOG_ERR, "Unable to open %s",
87204076Spjd			    res->hr_localpath));
88204076Spjd			return (-1);
89204076Spjd		}
90204076Spjd	}
91204076Spjd	if (fstat(res->hr_localfd, &sb) < 0) {
92204076Spjd		KEEP_ERRNO(pjdlog_errno(LOG_ERR, "Unable to stat %s",
93204076Spjd		    res->hr_localpath));
94204076Spjd		return (-1);
95204076Spjd	}
96204076Spjd	if (S_ISCHR(sb.st_mode)) {
97204076Spjd		/*
98204076Spjd		 * If this is character device, it is most likely GEOM provider.
99204076Spjd		 */
100204076Spjd		if (ioctl(res->hr_localfd, DIOCGMEDIASIZE,
101204076Spjd		    &res->hr_local_mediasize) < 0) {
102204076Spjd			KEEP_ERRNO(pjdlog_errno(LOG_ERR,
103204076Spjd			    "Unable obtain provider %s mediasize",
104204076Spjd			    res->hr_localpath));
105204076Spjd			return (-1);
106204076Spjd		}
107204076Spjd		if (ioctl(res->hr_localfd, DIOCGSECTORSIZE,
108204076Spjd		    &res->hr_local_sectorsize) < 0) {
109204076Spjd			KEEP_ERRNO(pjdlog_errno(LOG_ERR,
110204076Spjd			    "Unable obtain provider %s sectorsize",
111204076Spjd			    res->hr_localpath));
112204076Spjd			return (-1);
113204076Spjd		}
114204076Spjd	} else if (S_ISREG(sb.st_mode)) {
115204076Spjd		/*
116204076Spjd		 * We also support regular files for which we hardcode
117204076Spjd		 * sector size of 512 bytes.
118204076Spjd		 */
119204076Spjd		res->hr_local_mediasize = sb.st_size;
120204076Spjd		res->hr_local_sectorsize = 512;
121204076Spjd	} else {
122204076Spjd		/*
123204076Spjd		 * We support no other file types.
124204076Spjd		 */
125204076Spjd		pjdlog_error("%s is neither GEOM provider nor regular file.",
126204076Spjd		    res->hr_localpath);
127204076Spjd		errno = EFTYPE;
128204076Spjd		return (-1);
129204076Spjd	}
130204076Spjd	return (0);
131204076Spjd}
132204076Spjd
133204076Spjdconst char *
134204076Spjdrole2str(int role)
135204076Spjd{
136204076Spjd
137204076Spjd	switch (role) {
138204076Spjd	case HAST_ROLE_INIT:
139204076Spjd		return ("init");
140204076Spjd	case HAST_ROLE_PRIMARY:
141204076Spjd		return ("primary");
142204076Spjd	case HAST_ROLE_SECONDARY:
143204076Spjd		return ("secondary");
144204076Spjd	}
145204076Spjd	return ("unknown");
146204076Spjd}
147218048Spjd
148218048Spjdint
149219847Spjddrop_privs(bool usecapsicum)
150218048Spjd{
151218048Spjd	struct passwd *pw;
152218048Spjd	uid_t ruid, euid, suid;
153218048Spjd	gid_t rgid, egid, sgid;
154218048Spjd	gid_t gidset[1];
155218048Spjd
156219847Spjd	if (usecapsicum) {
157219847Spjd		if (cap_enter() == 0) {
158219847Spjd			pjdlog_debug(1,
159219847Spjd			    "Privileges successfully dropped using capsicum.");
160219847Spjd			return (0);
161219847Spjd		}
162219847Spjd		pjdlog_errno(LOG_WARNING, "Unable to sandbox using capsicum");
163219847Spjd	}
164219847Spjd
165218048Spjd	/*
166218048Spjd	 * According to getpwnam(3) we have to clear errno before calling the
167218048Spjd	 * function to be able to distinguish between an error and missing
168218048Spjd	 * entry (with is not treated as error by getpwnam(3)).
169218048Spjd	 */
170218048Spjd	errno = 0;
171218048Spjd	pw = getpwnam(HAST_USER);
172218048Spjd	if (pw == NULL) {
173218048Spjd		if (errno != 0) {
174218048Spjd			KEEP_ERRNO(pjdlog_errno(LOG_ERR,
175218048Spjd			    "Unable to find info about '%s' user", HAST_USER));
176218048Spjd			return (-1);
177218048Spjd		} else {
178218048Spjd			pjdlog_error("'%s' user doesn't exist.", HAST_USER);
179218048Spjd			errno = ENOENT;
180218048Spjd			return (-1);
181218048Spjd		}
182218048Spjd	}
183218048Spjd	if (chroot(pw->pw_dir) == -1) {
184218048Spjd		KEEP_ERRNO(pjdlog_errno(LOG_ERR,
185218048Spjd		    "Unable to change root directory to %s", pw->pw_dir));
186218048Spjd		return (-1);
187218048Spjd	}
188218048Spjd	PJDLOG_VERIFY(chdir("/") == 0);
189218048Spjd	gidset[0] = pw->pw_gid;
190218048Spjd	if (setgroups(1, gidset) == -1) {
191218048Spjd		KEEP_ERRNO(pjdlog_errno(LOG_ERR,
192218048Spjd		    "Unable to set groups to gid %u",
193218048Spjd		    (unsigned int)pw->pw_gid));
194218048Spjd		return (-1);
195218048Spjd	}
196218048Spjd	if (setgid(pw->pw_gid) == -1) {
197218048Spjd		KEEP_ERRNO(pjdlog_errno(LOG_ERR, "Unable to set gid to %u",
198218048Spjd		    (unsigned int)pw->pw_gid));
199218048Spjd		return (-1);
200218048Spjd	}
201218048Spjd	if (setuid(pw->pw_uid) == -1) {
202218048Spjd		KEEP_ERRNO(pjdlog_errno(LOG_ERR, "Unable to set uid to %u",
203218048Spjd		    (unsigned int)pw->pw_uid));
204218048Spjd		return (-1);
205218048Spjd	}
206218048Spjd
207218048Spjd	/*
208218048Spjd	 * Better be sure that everything succeeded.
209218048Spjd	 */
210218048Spjd	PJDLOG_VERIFY(getresuid(&ruid, &euid, &suid) == 0);
211218048Spjd	PJDLOG_VERIFY(ruid == pw->pw_uid);
212218048Spjd	PJDLOG_VERIFY(euid == pw->pw_uid);
213218048Spjd	PJDLOG_VERIFY(suid == pw->pw_uid);
214218048Spjd	PJDLOG_VERIFY(getresgid(&rgid, &egid, &sgid) == 0);
215218048Spjd	PJDLOG_VERIFY(rgid == pw->pw_gid);
216218048Spjd	PJDLOG_VERIFY(egid == pw->pw_gid);
217218048Spjd	PJDLOG_VERIFY(sgid == pw->pw_gid);
218218048Spjd	PJDLOG_VERIFY(getgroups(0, NULL) == 1);
219218048Spjd	PJDLOG_VERIFY(getgroups(1, gidset) == 1);
220218048Spjd	PJDLOG_VERIFY(gidset[0] == pw->pw_gid);
221218048Spjd
222219847Spjd	pjdlog_debug(1,
223219847Spjd	    "Privileges successfully dropped using chroot+setgid+setuid.");
224219847Spjd
225218048Spjd	return (0);
226218048Spjd}
227