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