xref: /freebsd/sbin/hastd/subr.c (revision 587392a5a04955b85c3beac4cff26ca568359f54)
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$");
32 
33 #include <sys/types.h>
34 #include <sys/disk.h>
35 #include <sys/ioctl.h>
36 #include <sys/stat.h>
37 
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <pwd.h>
41 #include <unistd.h>
42 
43 #include <pjdlog.h>
44 
45 #include "hast.h"
46 #include "subr.h"
47 
48 int
49 provinfo(struct hast_resource *res, bool dowrite)
50 {
51 	struct stat sb;
52 
53 	PJDLOG_ASSERT(res->hr_localpath != NULL &&
54 	    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 
107 const char *
108 role2str(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 
122 int
123 drop_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 	return (0);
188 }
189