subr.c revision 218048
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 218048 2011-01-28 22:33:47Z pjd $");
32
33#include <sys/types.h>
34#include <sys/disk.h>
35#include <sys/ioctl.h>
36#include <sys/stat.h>
37
38#include <assert.h>
39#include <errno.h>
40#include <fcntl.h>
41#include <pwd.h>
42#include <unistd.h>
43
44#include <pjdlog.h>
45
46#include "hast.h"
47#include "subr.h"
48
49int
50provinfo(struct hast_resource *res, bool dowrite)
51{
52	struct stat sb;
53
54	assert(res->hr_localpath != NULL && res->hr_localpath[0] != '\0');
55
56	if (res->hr_localfd == -1) {
57		res->hr_localfd = open(res->hr_localpath,
58		    dowrite ? O_RDWR : O_RDONLY);
59		if (res->hr_localfd < 0) {
60			KEEP_ERRNO(pjdlog_errno(LOG_ERR, "Unable to open %s",
61			    res->hr_localpath));
62			return (-1);
63		}
64	}
65	if (fstat(res->hr_localfd, &sb) < 0) {
66		KEEP_ERRNO(pjdlog_errno(LOG_ERR, "Unable to stat %s",
67		    res->hr_localpath));
68		return (-1);
69	}
70	if (S_ISCHR(sb.st_mode)) {
71		/*
72		 * If this is character device, it is most likely GEOM provider.
73		 */
74		if (ioctl(res->hr_localfd, DIOCGMEDIASIZE,
75		    &res->hr_local_mediasize) < 0) {
76			KEEP_ERRNO(pjdlog_errno(LOG_ERR,
77			    "Unable obtain provider %s mediasize",
78			    res->hr_localpath));
79			return (-1);
80		}
81		if (ioctl(res->hr_localfd, DIOCGSECTORSIZE,
82		    &res->hr_local_sectorsize) < 0) {
83			KEEP_ERRNO(pjdlog_errno(LOG_ERR,
84			    "Unable obtain provider %s sectorsize",
85			    res->hr_localpath));
86			return (-1);
87		}
88	} else if (S_ISREG(sb.st_mode)) {
89		/*
90		 * We also support regular files for which we hardcode
91		 * sector size of 512 bytes.
92		 */
93		res->hr_local_mediasize = sb.st_size;
94		res->hr_local_sectorsize = 512;
95	} else {
96		/*
97		 * We support no other file types.
98		 */
99		pjdlog_error("%s is neither GEOM provider nor regular file.",
100		    res->hr_localpath);
101		errno = EFTYPE;
102		return (-1);
103	}
104	return (0);
105}
106
107const char *
108role2str(int role)
109{
110
111	switch (role) {
112	case HAST_ROLE_INIT:
113		return ("init");
114	case HAST_ROLE_PRIMARY:
115		return ("primary");
116	case HAST_ROLE_SECONDARY:
117		return ("secondary");
118	}
119	return ("unknown");
120}
121
122int
123drop_privs(void)
124{
125	struct passwd *pw;
126	uid_t ruid, euid, suid;
127	gid_t rgid, egid, sgid;
128	gid_t gidset[1];
129
130	/*
131	 * According to getpwnam(3) we have to clear errno before calling the
132	 * function to be able to distinguish between an error and missing
133	 * entry (with is not treated as error by getpwnam(3)).
134	 */
135	errno = 0;
136	pw = getpwnam(HAST_USER);
137	if (pw == NULL) {
138		if (errno != 0) {
139			KEEP_ERRNO(pjdlog_errno(LOG_ERR,
140			    "Unable to find info about '%s' user", HAST_USER));
141			return (-1);
142		} else {
143			pjdlog_error("'%s' user doesn't exist.", HAST_USER);
144			errno = ENOENT;
145			return (-1);
146		}
147	}
148	if (chroot(pw->pw_dir) == -1) {
149		KEEP_ERRNO(pjdlog_errno(LOG_ERR,
150		    "Unable to change root directory to %s", pw->pw_dir));
151		return (-1);
152	}
153	PJDLOG_VERIFY(chdir("/") == 0);
154	gidset[0] = pw->pw_gid;
155	if (setgroups(1, gidset) == -1) {
156		KEEP_ERRNO(pjdlog_errno(LOG_ERR,
157		    "Unable to set groups to gid %u",
158		    (unsigned int)pw->pw_gid));
159		return (-1);
160	}
161	if (setgid(pw->pw_gid) == -1) {
162		KEEP_ERRNO(pjdlog_errno(LOG_ERR, "Unable to set gid to %u",
163		    (unsigned int)pw->pw_gid));
164		return (-1);
165	}
166	if (setuid(pw->pw_uid) == -1) {
167		KEEP_ERRNO(pjdlog_errno(LOG_ERR, "Unable to set uid to %u",
168		    (unsigned int)pw->pw_uid));
169		return (-1);
170	}
171
172	/*
173	 * Better be sure that everything succeeded.
174	 */
175	PJDLOG_VERIFY(getresuid(&ruid, &euid, &suid) == 0);
176	PJDLOG_VERIFY(ruid == pw->pw_uid);
177	PJDLOG_VERIFY(euid == pw->pw_uid);
178	PJDLOG_VERIFY(suid == pw->pw_uid);
179	PJDLOG_VERIFY(getresgid(&rgid, &egid, &sgid) == 0);
180	PJDLOG_VERIFY(rgid == pw->pw_gid);
181	PJDLOG_VERIFY(egid == pw->pw_gid);
182	PJDLOG_VERIFY(sgid == pw->pw_gid);
183	PJDLOG_VERIFY(getgroups(0, NULL) == 1);
184	PJDLOG_VERIFY(getgroups(1, gidset) == 1);
185	PJDLOG_VERIFY(gidset[0] == pw->pw_gid);
186
187	pjdlog_info("Privileges successfully dropped.");
188
189	return (0);
190}
191