subr.c revision 219847
1/*-
2 * Copyright (c) 2010 The FreeBSD Foundation
3 * All rights reserved.
4 *
5 * This software was developed by Pawel Jakub Dawidek under sponsorship from
6 * the FreeBSD Foundation.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD: head/sbin/hastd/subr.c 219847 2011-03-21 21:31:50Z pjd $");
32
33#include <sys/capability.h>
34#include <sys/types.h>
35#include <sys/disk.h>
36#include <sys/ioctl.h>
37#include <sys/stat.h>
38
39#include <errno.h>
40#include <fcntl.h>
41#include <pwd.h>
42#include <stdarg.h>
43#include <stdbool.h>
44#include <stdio.h>
45#include <string.h>
46#include <unistd.h>
47
48#include <pjdlog.h>
49
50#include "hast.h"
51#include "subr.h"
52
53int
54vsnprlcat(char *str, size_t size, const char *fmt, va_list ap)
55{
56	size_t len;
57
58	len = strlen(str);
59	return (vsnprintf(str + len, size - len, fmt, ap));
60}
61
62int
63snprlcat(char *str, size_t size, const char *fmt, ...)
64{
65	va_list ap;
66	int result;
67
68	va_start(ap, fmt);
69	result = vsnprlcat(str, size, fmt, ap);
70	va_end(ap);
71	return (result);
72}
73
74int
75provinfo(struct hast_resource *res, bool dowrite)
76{
77	struct stat sb;
78
79	PJDLOG_ASSERT(res->hr_localpath != NULL &&
80	    res->hr_localpath[0] != '\0');
81
82	if (res->hr_localfd == -1) {
83		res->hr_localfd = open(res->hr_localpath,
84		    dowrite ? O_RDWR : O_RDONLY);
85		if (res->hr_localfd < 0) {
86			KEEP_ERRNO(pjdlog_errno(LOG_ERR, "Unable to open %s",
87			    res->hr_localpath));
88			return (-1);
89		}
90	}
91	if (fstat(res->hr_localfd, &sb) < 0) {
92		KEEP_ERRNO(pjdlog_errno(LOG_ERR, "Unable to stat %s",
93		    res->hr_localpath));
94		return (-1);
95	}
96	if (S_ISCHR(sb.st_mode)) {
97		/*
98		 * If this is character device, it is most likely GEOM provider.
99		 */
100		if (ioctl(res->hr_localfd, DIOCGMEDIASIZE,
101		    &res->hr_local_mediasize) < 0) {
102			KEEP_ERRNO(pjdlog_errno(LOG_ERR,
103			    "Unable obtain provider %s mediasize",
104			    res->hr_localpath));
105			return (-1);
106		}
107		if (ioctl(res->hr_localfd, DIOCGSECTORSIZE,
108		    &res->hr_local_sectorsize) < 0) {
109			KEEP_ERRNO(pjdlog_errno(LOG_ERR,
110			    "Unable obtain provider %s sectorsize",
111			    res->hr_localpath));
112			return (-1);
113		}
114	} else if (S_ISREG(sb.st_mode)) {
115		/*
116		 * We also support regular files for which we hardcode
117		 * sector size of 512 bytes.
118		 */
119		res->hr_local_mediasize = sb.st_size;
120		res->hr_local_sectorsize = 512;
121	} else {
122		/*
123		 * We support no other file types.
124		 */
125		pjdlog_error("%s is neither GEOM provider nor regular file.",
126		    res->hr_localpath);
127		errno = EFTYPE;
128		return (-1);
129	}
130	return (0);
131}
132
133const char *
134role2str(int role)
135{
136
137	switch (role) {
138	case HAST_ROLE_INIT:
139		return ("init");
140	case HAST_ROLE_PRIMARY:
141		return ("primary");
142	case HAST_ROLE_SECONDARY:
143		return ("secondary");
144	}
145	return ("unknown");
146}
147
148int
149drop_privs(bool usecapsicum)
150{
151	struct passwd *pw;
152	uid_t ruid, euid, suid;
153	gid_t rgid, egid, sgid;
154	gid_t gidset[1];
155
156	if (usecapsicum) {
157		if (cap_enter() == 0) {
158			pjdlog_debug(1,
159			    "Privileges successfully dropped using capsicum.");
160			return (0);
161		}
162		pjdlog_errno(LOG_WARNING, "Unable to sandbox using capsicum");
163	}
164
165	/*
166	 * According to getpwnam(3) we have to clear errno before calling the
167	 * function to be able to distinguish between an error and missing
168	 * entry (with is not treated as error by getpwnam(3)).
169	 */
170	errno = 0;
171	pw = getpwnam(HAST_USER);
172	if (pw == NULL) {
173		if (errno != 0) {
174			KEEP_ERRNO(pjdlog_errno(LOG_ERR,
175			    "Unable to find info about '%s' user", HAST_USER));
176			return (-1);
177		} else {
178			pjdlog_error("'%s' user doesn't exist.", HAST_USER);
179			errno = ENOENT;
180			return (-1);
181		}
182	}
183	if (chroot(pw->pw_dir) == -1) {
184		KEEP_ERRNO(pjdlog_errno(LOG_ERR,
185		    "Unable to change root directory to %s", pw->pw_dir));
186		return (-1);
187	}
188	PJDLOG_VERIFY(chdir("/") == 0);
189	gidset[0] = pw->pw_gid;
190	if (setgroups(1, gidset) == -1) {
191		KEEP_ERRNO(pjdlog_errno(LOG_ERR,
192		    "Unable to set groups to gid %u",
193		    (unsigned int)pw->pw_gid));
194		return (-1);
195	}
196	if (setgid(pw->pw_gid) == -1) {
197		KEEP_ERRNO(pjdlog_errno(LOG_ERR, "Unable to set gid to %u",
198		    (unsigned int)pw->pw_gid));
199		return (-1);
200	}
201	if (setuid(pw->pw_uid) == -1) {
202		KEEP_ERRNO(pjdlog_errno(LOG_ERR, "Unable to set uid to %u",
203		    (unsigned int)pw->pw_uid));
204		return (-1);
205	}
206
207	/*
208	 * Better be sure that everything succeeded.
209	 */
210	PJDLOG_VERIFY(getresuid(&ruid, &euid, &suid) == 0);
211	PJDLOG_VERIFY(ruid == pw->pw_uid);
212	PJDLOG_VERIFY(euid == pw->pw_uid);
213	PJDLOG_VERIFY(suid == pw->pw_uid);
214	PJDLOG_VERIFY(getresgid(&rgid, &egid, &sgid) == 0);
215	PJDLOG_VERIFY(rgid == pw->pw_gid);
216	PJDLOG_VERIFY(egid == pw->pw_gid);
217	PJDLOG_VERIFY(sgid == pw->pw_gid);
218	PJDLOG_VERIFY(getgroups(0, NULL) == 1);
219	PJDLOG_VERIFY(getgroups(1, gidset) == 1);
220	PJDLOG_VERIFY(gidset[0] == pw->pw_gid);
221
222	pjdlog_debug(1,
223	    "Privileges successfully dropped using chroot+setgid+setuid.");
224
225	return (0);
226}
227