subr.c revision 219887
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: head/sbin/hastd/subr.c 219887 2011-03-22 21:19:51Z pjd $");
33204076Spjd
34219847Spjd#include <sys/capability.h>
35204076Spjd#include <sys/types.h>
36204076Spjd#include <sys/disk.h>
37204076Spjd#include <sys/ioctl.h>
38204076Spjd#include <sys/stat.h>
39204076Spjd
40204076Spjd#include <errno.h>
41204076Spjd#include <fcntl.h>
42218048Spjd#include <pwd.h>
43219815Spjd#include <stdarg.h>
44219847Spjd#include <stdbool.h>
45219815Spjd#include <stdio.h>
46219815Spjd#include <string.h>
47218048Spjd#include <unistd.h>
48204076Spjd
49204076Spjd#include <pjdlog.h>
50204076Spjd
51204076Spjd#include "hast.h"
52204076Spjd#include "subr.h"
53204076Spjd
54204076Spjdint
55219815Spjdvsnprlcat(char *str, size_t size, const char *fmt, va_list ap)
56219815Spjd{
57219815Spjd	size_t len;
58219815Spjd
59219815Spjd	len = strlen(str);
60219815Spjd	return (vsnprintf(str + len, size - len, fmt, ap));
61219815Spjd}
62219815Spjd
63219815Spjdint
64219815Spjdsnprlcat(char *str, size_t size, const char *fmt, ...)
65219815Spjd{
66219815Spjd	va_list ap;
67219815Spjd	int result;
68219815Spjd
69219815Spjd	va_start(ap, fmt);
70219815Spjd	result = vsnprlcat(str, size, fmt, ap);
71219815Spjd	va_end(ap);
72219815Spjd	return (result);
73219815Spjd}
74219815Spjd
75219815Spjdint
76204076Spjdprovinfo(struct hast_resource *res, bool dowrite)
77204076Spjd{
78204076Spjd	struct stat sb;
79204076Spjd
80218138Spjd	PJDLOG_ASSERT(res->hr_localpath != NULL &&
81218138Spjd	    res->hr_localpath[0] != '\0');
82204076Spjd
83204076Spjd	if (res->hr_localfd == -1) {
84204076Spjd		res->hr_localfd = open(res->hr_localpath,
85204076Spjd		    dowrite ? O_RDWR : O_RDONLY);
86204076Spjd		if (res->hr_localfd < 0) {
87204076Spjd			KEEP_ERRNO(pjdlog_errno(LOG_ERR, "Unable to open %s",
88204076Spjd			    res->hr_localpath));
89204076Spjd			return (-1);
90204076Spjd		}
91204076Spjd	}
92204076Spjd	if (fstat(res->hr_localfd, &sb) < 0) {
93204076Spjd		KEEP_ERRNO(pjdlog_errno(LOG_ERR, "Unable to stat %s",
94204076Spjd		    res->hr_localpath));
95204076Spjd		return (-1);
96204076Spjd	}
97204076Spjd	if (S_ISCHR(sb.st_mode)) {
98204076Spjd		/*
99204076Spjd		 * If this is character device, it is most likely GEOM provider.
100204076Spjd		 */
101204076Spjd		if (ioctl(res->hr_localfd, DIOCGMEDIASIZE,
102204076Spjd		    &res->hr_local_mediasize) < 0) {
103204076Spjd			KEEP_ERRNO(pjdlog_errno(LOG_ERR,
104204076Spjd			    "Unable obtain provider %s mediasize",
105204076Spjd			    res->hr_localpath));
106204076Spjd			return (-1);
107204076Spjd		}
108204076Spjd		if (ioctl(res->hr_localfd, DIOCGSECTORSIZE,
109204076Spjd		    &res->hr_local_sectorsize) < 0) {
110204076Spjd			KEEP_ERRNO(pjdlog_errno(LOG_ERR,
111204076Spjd			    "Unable obtain provider %s sectorsize",
112204076Spjd			    res->hr_localpath));
113204076Spjd			return (-1);
114204076Spjd		}
115204076Spjd	} else if (S_ISREG(sb.st_mode)) {
116204076Spjd		/*
117204076Spjd		 * We also support regular files for which we hardcode
118204076Spjd		 * sector size of 512 bytes.
119204076Spjd		 */
120204076Spjd		res->hr_local_mediasize = sb.st_size;
121204076Spjd		res->hr_local_sectorsize = 512;
122204076Spjd	} else {
123204076Spjd		/*
124204076Spjd		 * We support no other file types.
125204076Spjd		 */
126204076Spjd		pjdlog_error("%s is neither GEOM provider nor regular file.",
127204076Spjd		    res->hr_localpath);
128204076Spjd		errno = EFTYPE;
129204076Spjd		return (-1);
130204076Spjd	}
131204076Spjd	return (0);
132204076Spjd}
133204076Spjd
134204076Spjdconst char *
135204076Spjdrole2str(int role)
136204076Spjd{
137204076Spjd
138219864Spjd	switch (role) {
139219864Spjd	case HAST_ROLE_INIT:
140204076Spjd		return ("init");
141219864Spjd	case HAST_ROLE_PRIMARY:
142204076Spjd		return ("primary");
143219864Spjd	case HAST_ROLE_SECONDARY:
144204076Spjd		return ("secondary");
145204076Spjd	}
146204076Spjd	return ("unknown");
147204076Spjd}
148218048Spjd
149218048Spjdint
150219847Spjddrop_privs(bool usecapsicum)
151218048Spjd{
152218048Spjd	struct passwd *pw;
153218048Spjd	uid_t ruid, euid, suid;
154218048Spjd	gid_t rgid, egid, sgid;
155218048Spjd	gid_t gidset[1];
156218048Spjd
157219847Spjd	if (usecapsicum) {
158219847Spjd		if (cap_enter() == 0) {
159219847Spjd			pjdlog_debug(1,
160219847Spjd			    "Privileges successfully dropped using capsicum.");
161219847Spjd			return (0);
162219847Spjd		}
163219847Spjd		pjdlog_errno(LOG_WARNING, "Unable to sandbox using capsicum");
164219847Spjd	}
165219847Spjd
166218048Spjd	/*
167218048Spjd	 * According to getpwnam(3) we have to clear errno before calling the
168218048Spjd	 * function to be able to distinguish between an error and missing
169218048Spjd	 * entry (with is not treated as error by getpwnam(3)).
170218048Spjd	 */
171218048Spjd	errno = 0;
172218048Spjd	pw = getpwnam(HAST_USER);
173218048Spjd	if (pw == NULL) {
174218048Spjd		if (errno != 0) {
175218048Spjd			KEEP_ERRNO(pjdlog_errno(LOG_ERR,
176218048Spjd			    "Unable to find info about '%s' user", HAST_USER));
177218048Spjd			return (-1);
178218048Spjd		} else {
179218048Spjd			pjdlog_error("'%s' user doesn't exist.", HAST_USER);
180218048Spjd			errno = ENOENT;
181218048Spjd			return (-1);
182218048Spjd		}
183218048Spjd	}
184218048Spjd	if (chroot(pw->pw_dir) == -1) {
185218048Spjd		KEEP_ERRNO(pjdlog_errno(LOG_ERR,
186218048Spjd		    "Unable to change root directory to %s", pw->pw_dir));
187218048Spjd		return (-1);
188218048Spjd	}
189218048Spjd	PJDLOG_VERIFY(chdir("/") == 0);
190218048Spjd	gidset[0] = pw->pw_gid;
191218048Spjd	if (setgroups(1, gidset) == -1) {
192218048Spjd		KEEP_ERRNO(pjdlog_errno(LOG_ERR,
193218048Spjd		    "Unable to set groups to gid %u",
194218048Spjd		    (unsigned int)pw->pw_gid));
195218048Spjd		return (-1);
196218048Spjd	}
197218048Spjd	if (setgid(pw->pw_gid) == -1) {
198218048Spjd		KEEP_ERRNO(pjdlog_errno(LOG_ERR, "Unable to set gid to %u",
199218048Spjd		    (unsigned int)pw->pw_gid));
200218048Spjd		return (-1);
201218048Spjd	}
202218048Spjd	if (setuid(pw->pw_uid) == -1) {
203218048Spjd		KEEP_ERRNO(pjdlog_errno(LOG_ERR, "Unable to set uid to %u",
204218048Spjd		    (unsigned int)pw->pw_uid));
205218048Spjd		return (-1);
206218048Spjd	}
207218048Spjd
208218048Spjd	/*
209218048Spjd	 * Better be sure that everything succeeded.
210218048Spjd	 */
211218048Spjd	PJDLOG_VERIFY(getresuid(&ruid, &euid, &suid) == 0);
212218048Spjd	PJDLOG_VERIFY(ruid == pw->pw_uid);
213218048Spjd	PJDLOG_VERIFY(euid == pw->pw_uid);
214218048Spjd	PJDLOG_VERIFY(suid == pw->pw_uid);
215218048Spjd	PJDLOG_VERIFY(getresgid(&rgid, &egid, &sgid) == 0);
216218048Spjd	PJDLOG_VERIFY(rgid == pw->pw_gid);
217218048Spjd	PJDLOG_VERIFY(egid == pw->pw_gid);
218218048Spjd	PJDLOG_VERIFY(sgid == pw->pw_gid);
219218048Spjd	PJDLOG_VERIFY(getgroups(0, NULL) == 1);
220218048Spjd	PJDLOG_VERIFY(getgroups(1, gidset) == 1);
221218048Spjd	PJDLOG_VERIFY(gidset[0] == pw->pw_gid);
222218048Spjd
223219847Spjd	pjdlog_debug(1,
224219847Spjd	    "Privileges successfully dropped using chroot+setgid+setuid.");
225219847Spjd
226218048Spjd	return (0);
227218048Spjd}
228