Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sbin/hastd/subr.c
39475 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2010 The FreeBSD Foundation
5
* Copyright (c) 2011 Pawel Jakub Dawidek <[email protected]>
6
* All rights reserved.
7
*
8
* This software was developed by Pawel Jakub Dawidek under sponsorship from
9
* the FreeBSD Foundation.
10
*
11
* Redistribution and use in source and binary forms, with or without
12
* modification, are permitted provided that the following conditions
13
* are met:
14
* 1. Redistributions of source code must retain the above copyright
15
* notice, this list of conditions and the following disclaimer.
16
* 2. Redistributions in binary form must reproduce the above copyright
17
* notice, this list of conditions and the following disclaimer in the
18
* documentation and/or other materials provided with the distribution.
19
*
20
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
21
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
24
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30
* SUCH DAMAGE.
31
*/
32
33
#include <sys/param.h>
34
#include <sys/disk.h>
35
#include <sys/ioctl.h>
36
#include <sys/jail.h>
37
#include <sys/stat.h>
38
#ifdef HAVE_CAPSICUM
39
#include <sys/capsicum.h>
40
#include <geom/gate/g_gate.h>
41
#endif
42
43
#include <errno.h>
44
#include <fcntl.h>
45
#include <pwd.h>
46
#include <stdarg.h>
47
#include <stdbool.h>
48
#include <stdio.h>
49
#include <string.h>
50
#include <unistd.h>
51
52
#include <pjdlog.h>
53
54
#include "hast.h"
55
#include "subr.h"
56
57
int
58
vsnprlcat(char *str, size_t size, const char *fmt, va_list ap)
59
{
60
size_t len;
61
62
len = strlen(str);
63
return (vsnprintf(str + len, size - len, fmt, ap));
64
}
65
66
int
67
snprlcat(char *str, size_t size, const char *fmt, ...)
68
{
69
va_list ap;
70
int result;
71
72
va_start(ap, fmt);
73
result = vsnprlcat(str, size, fmt, ap);
74
va_end(ap);
75
return (result);
76
}
77
78
int
79
provinfo(struct hast_resource *res, bool dowrite)
80
{
81
struct stat sb;
82
83
PJDLOG_ASSERT(res->hr_localpath != NULL &&
84
res->hr_localpath[0] != '\0');
85
86
if (res->hr_localfd == -1) {
87
res->hr_localfd = open(res->hr_localpath,
88
dowrite ? O_RDWR : O_RDONLY);
89
if (res->hr_localfd == -1) {
90
pjdlog_errno(LOG_ERR, "Unable to open %s",
91
res->hr_localpath);
92
return (-1);
93
}
94
}
95
if (fstat(res->hr_localfd, &sb) == -1) {
96
pjdlog_errno(LOG_ERR, "Unable to stat %s", res->hr_localpath);
97
return (-1);
98
}
99
if (S_ISCHR(sb.st_mode)) {
100
/*
101
* If this is character device, it is most likely GEOM provider.
102
*/
103
if (ioctl(res->hr_localfd, DIOCGMEDIASIZE,
104
&res->hr_local_mediasize) == -1) {
105
pjdlog_errno(LOG_ERR,
106
"Unable obtain provider %s mediasize",
107
res->hr_localpath);
108
return (-1);
109
}
110
if (ioctl(res->hr_localfd, DIOCGSECTORSIZE,
111
&res->hr_local_sectorsize) == -1) {
112
pjdlog_errno(LOG_ERR,
113
"Unable obtain provider %s sectorsize",
114
res->hr_localpath);
115
return (-1);
116
}
117
} else if (S_ISREG(sb.st_mode)) {
118
/*
119
* We also support regular files for which we hardcode
120
* sector size of 512 bytes.
121
*/
122
res->hr_local_mediasize = sb.st_size;
123
res->hr_local_sectorsize = 512;
124
} else {
125
/*
126
* We support no other file types.
127
*/
128
pjdlog_error("%s is neither GEOM provider nor regular file.",
129
res->hr_localpath);
130
errno = EFTYPE;
131
return (-1);
132
}
133
return (0);
134
}
135
136
const char *
137
role2str(int role)
138
{
139
140
switch (role) {
141
case HAST_ROLE_INIT:
142
return ("init");
143
case HAST_ROLE_PRIMARY:
144
return ("primary");
145
case HAST_ROLE_SECONDARY:
146
return ("secondary");
147
}
148
return ("unknown");
149
}
150
151
int
152
drop_privs(const struct hast_resource *res)
153
{
154
char jailhost[sizeof(res->hr_name) * 2];
155
struct jail jailst;
156
struct passwd *pw;
157
uid_t ruid, euid, suid;
158
gid_t rgid, egid, sgid;
159
bool capsicum, jailed;
160
161
/*
162
* According to getpwnam(3) we have to clear errno before calling the
163
* function to be able to distinguish between an error and missing
164
* entry (with is not treated as error by getpwnam(3)).
165
*/
166
errno = 0;
167
pw = getpwnam(HAST_USER);
168
if (pw == NULL) {
169
if (errno != 0) {
170
pjdlog_errno(LOG_ERR,
171
"Unable to find info about '%s' user", HAST_USER);
172
return (-1);
173
} else {
174
pjdlog_error("'%s' user doesn't exist.", HAST_USER);
175
errno = ENOENT;
176
return (-1);
177
}
178
}
179
180
bzero(&jailst, sizeof(jailst));
181
jailst.version = JAIL_API_VERSION;
182
jailst.path = pw->pw_dir;
183
if (res == NULL) {
184
(void)snprintf(jailhost, sizeof(jailhost), "hastctl");
185
} else {
186
(void)snprintf(jailhost, sizeof(jailhost), "hastd: %s (%s)",
187
res->hr_name, role2str(res->hr_role));
188
}
189
jailst.hostname = jailhost;
190
jailst.jailname = NULL;
191
jailst.ip4s = 0;
192
jailst.ip4 = NULL;
193
jailst.ip6s = 0;
194
jailst.ip6 = NULL;
195
if (jail(&jailst) >= 0) {
196
jailed = true;
197
} else {
198
jailed = false;
199
pjdlog_errno(LOG_WARNING,
200
"Unable to jail to directory to %s", pw->pw_dir);
201
if (chroot(pw->pw_dir) == -1) {
202
pjdlog_errno(LOG_ERR,
203
"Unable to change root directory to %s",
204
pw->pw_dir);
205
return (-1);
206
}
207
}
208
PJDLOG_VERIFY(chdir("/") == 0);
209
if (setgroups(0, NULL) == -1) {
210
pjdlog_errno(LOG_ERR, "Unable to drop supplementary groups");
211
return (-1);
212
}
213
if (setgid(pw->pw_gid) == -1) {
214
pjdlog_errno(LOG_ERR, "Unable to set gid to %u",
215
(unsigned int)pw->pw_gid);
216
return (-1);
217
}
218
if (setuid(pw->pw_uid) == -1) {
219
pjdlog_errno(LOG_ERR, "Unable to set uid to %u",
220
(unsigned int)pw->pw_uid);
221
return (-1);
222
}
223
224
#ifdef HAVE_CAPSICUM
225
capsicum = (cap_enter() == 0);
226
if (!capsicum) {
227
pjdlog_common(LOG_DEBUG, 1, errno,
228
"Unable to sandbox using capsicum");
229
} else if (res != NULL) {
230
cap_rights_t rights;
231
static const unsigned long geomcmds[] = {
232
DIOCGDELETE,
233
DIOCGFLUSH
234
};
235
236
PJDLOG_ASSERT(res->hr_role == HAST_ROLE_PRIMARY ||
237
res->hr_role == HAST_ROLE_SECONDARY);
238
239
cap_rights_init(&rights, CAP_FLOCK, CAP_IOCTL, CAP_PREAD,
240
CAP_PWRITE);
241
if (cap_rights_limit(res->hr_localfd, &rights) == -1) {
242
pjdlog_errno(LOG_ERR,
243
"Unable to limit capability rights on local descriptor");
244
}
245
if (cap_ioctls_limit(res->hr_localfd, geomcmds,
246
nitems(geomcmds)) == -1) {
247
pjdlog_errno(LOG_ERR,
248
"Unable to limit allowed GEOM ioctls");
249
}
250
251
if (res->hr_role == HAST_ROLE_PRIMARY) {
252
static const unsigned long ggatecmds[] = {
253
G_GATE_CMD_MODIFY,
254
G_GATE_CMD_START,
255
G_GATE_CMD_DONE,
256
G_GATE_CMD_DESTROY
257
};
258
259
cap_rights_init(&rights, CAP_IOCTL);
260
if (cap_rights_limit(res->hr_ggatefd, &rights) == -1) {
261
pjdlog_errno(LOG_ERR,
262
"Unable to limit capability rights to CAP_IOCTL on ggate descriptor");
263
}
264
if (cap_ioctls_limit(res->hr_ggatefd, ggatecmds,
265
nitems(ggatecmds)) == -1) {
266
pjdlog_errno(LOG_ERR,
267
"Unable to limit allowed ggate ioctls");
268
}
269
}
270
}
271
#else
272
capsicum = false;
273
#endif
274
275
/*
276
* Better be sure that everything succeeded.
277
*/
278
PJDLOG_VERIFY(getresuid(&ruid, &euid, &suid) == 0);
279
PJDLOG_VERIFY(ruid == pw->pw_uid);
280
PJDLOG_VERIFY(euid == pw->pw_uid);
281
PJDLOG_VERIFY(suid == pw->pw_uid);
282
PJDLOG_VERIFY(getresgid(&rgid, &egid, &sgid) == 0);
283
PJDLOG_VERIFY(rgid == pw->pw_gid);
284
PJDLOG_VERIFY(egid == pw->pw_gid);
285
PJDLOG_VERIFY(sgid == pw->pw_gid);
286
PJDLOG_VERIFY(getgroups(0, NULL) == 0);
287
288
pjdlog_debug(1,
289
"Privileges successfully dropped using %s%s+setgid+setuid.",
290
capsicum ? "capsicum+" : "", jailed ? "jail" : "chroot");
291
292
return (0);
293
}
294
295