Deleted Added
full compact
sandbox.c (243730) sandbox.c (243734)
1/*-
2 * Copyright (c) 2012 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 *
1/*-
2 * Copyright (c) 2012 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 * $P4: //depot/projects/trustedbsd/openbsm/bin/auditdistd/sandbox.c#1 $
29 * $P4: //depot/projects/trustedbsd/openbsm/bin/auditdistd/sandbox.c#3 $
30 */
31
30 */
31
32#ifdef HAVE_CONFIG_H
33#include "config.h"
34#endif
32#include <config/config.h>
35
36#include <sys/param.h>
37#ifdef HAVE_JAIL
38#include <sys/jail.h>
39#endif
40#ifdef HAVE_CAP_ENTER
41#include <sys/capability.h>
42#endif
43
44#include <errno.h>
45#include <pwd.h>
46#include <stdarg.h>
47#include <stdbool.h>
48#include <stdio.h>
49#include <stdlib.h>
50#include <strings.h>
51#include <unistd.h>
52
33
34#include <sys/param.h>
35#ifdef HAVE_JAIL
36#include <sys/jail.h>
37#endif
38#ifdef HAVE_CAP_ENTER
39#include <sys/capability.h>
40#endif
41
42#include <errno.h>
43#include <pwd.h>
44#include <stdarg.h>
45#include <stdbool.h>
46#include <stdio.h>
47#include <stdlib.h>
48#include <strings.h>
49#include <unistd.h>
50
53#include <pjdlog.h>
54
51#include "pjdlog.h"
55#include "sandbox.h"
56
57static int
58groups_compare(const void *grp0, const void *grp1)
59{
60 gid_t gr0 = *(const gid_t *)grp0;
61 gid_t gr1 = *(const gid_t *)grp1;
62
63 return (gr0 <= gr1 ? (gr0 < gr1 ? -1 : 0) : 1);
64
65}
66
67int
68sandbox(const char *user, bool capsicum, const char *fmt, ...)
69{
70#ifdef HAVE_JAIL
71 struct jail jailst;
72 char *jailhost;
73 va_list ap;
74#endif
75 struct passwd *pw;
76 uid_t ruid, euid;
77 gid_t rgid, egid;
78#ifdef HAVE_GETRESUID
79 uid_t suid;
80#endif
81#ifdef HAVE_GETRESGID
82 gid_t sgid;
83#endif
84 gid_t *groups, *ggroups;
85 bool jailed;
86 int ngroups, ret;
87
88 PJDLOG_ASSERT(user != NULL);
89 PJDLOG_ASSERT(fmt != NULL);
90
91 ret = -1;
92 groups = NULL;
93 ggroups = NULL;
94
95 /*
96 * According to getpwnam(3) we have to clear errno before calling the
97 * function to be able to distinguish between an error and missing
98 * entry (with is not treated as error by getpwnam(3)).
99 */
100 errno = 0;
101 pw = getpwnam(user);
102 if (pw == NULL) {
103 if (errno != 0) {
104 pjdlog_errno(LOG_ERR,
105 "Unable to find info about '%s' user", user);
106 goto out;
107 } else {
108 pjdlog_error("'%s' user doesn't exist.", user);
109 errno = ENOENT;
110 goto out;
111 }
112 }
113
114 ngroups = sysconf(_SC_NGROUPS_MAX);
115 if (ngroups == -1) {
116 pjdlog_errno(LOG_WARNING,
117 "Unable to obtain maximum number of groups");
118 ngroups = NGROUPS_MAX;
119 }
120 ngroups++; /* For base gid. */
121 groups = malloc(sizeof(groups[0]) * ngroups);
122 if (groups == NULL) {
123 pjdlog_error("Unable to allocate memory for %d groups.",
124 ngroups);
125 goto out;
126 }
127 if (getgrouplist(user, pw->pw_gid, groups, &ngroups) == -1) {
128 pjdlog_error("Unable to obtain groups of user %s.", user);
129 goto out;
130 }
131
132#ifdef HAVE_JAIL
133 va_start(ap, fmt);
134 (void)vasprintf(&jailhost, fmt, ap);
135 va_end(ap);
136 if (jailhost == NULL) {
137 pjdlog_error("Unable to allocate memory for jail host name.");
138 goto out;
139 }
140 bzero(&jailst, sizeof(jailst));
141 jailst.version = JAIL_API_VERSION;
142 jailst.path = pw->pw_dir;
143 jailst.hostname = jailhost;
144 if (jail(&jailst) >= 0) {
145 jailed = true;
146 } else {
147 jailed = false;
148 pjdlog_errno(LOG_WARNING,
149 "Unable to jail to directory %s", pw->pw_dir);
150 }
151 free(jailhost);
152#else /* !HAVE_JAIL */
153 jailed = false;
154#endif /* !HAVE_JAIL */
155
156 if (!jailed) {
157 if (chroot(pw->pw_dir) == -1) {
158 pjdlog_errno(LOG_ERR,
159 "Unable to change root directory to %s",
160 pw->pw_dir);
161 goto out;
162 }
163 }
164 PJDLOG_VERIFY(chdir("/") == 0);
165
166 if (setgroups(ngroups, groups) == -1) {
167 pjdlog_errno(LOG_ERR, "Unable to set groups");
168 goto out;
169 }
170 if (setgid(pw->pw_gid) == -1) {
171 pjdlog_errno(LOG_ERR, "Unable to set gid to %u",
172 (unsigned int)pw->pw_gid);
173 goto out;
174 }
175 if (setuid(pw->pw_uid) == -1) {
176 pjdlog_errno(LOG_ERR, "Unable to set uid to %u",
177 (unsigned int)pw->pw_uid);
178 goto out;
179 }
180
181#ifdef HAVE_CAP_ENTER
182 if (capsicum) {
183 capsicum = (cap_enter() == 0);
184 if (!capsicum) {
185 pjdlog_common(LOG_DEBUG, 1, errno,
186 "Unable to sandbox using capsicum");
187 }
188 }
189#else /* !HAVE_CAP_ENTER */
190 capsicum = false;
191#endif /* !HAVE_CAP_ENTER */
192
193 /*
194 * Better be sure that everything succeeded.
195 */
196#ifdef HAVE_GETRESUID
197 PJDLOG_VERIFY(getresuid(&ruid, &euid, &suid) == 0);
198 PJDLOG_VERIFY(suid == pw->pw_uid);
199#else
200 ruid = getuid();
201 euid = geteuid();
202#endif
203 PJDLOG_VERIFY(ruid == pw->pw_uid);
204 PJDLOG_VERIFY(euid == pw->pw_uid);
205#ifdef HAVE_GETRESGID
206 PJDLOG_VERIFY(getresgid(&rgid, &egid, &sgid) == 0);
207 PJDLOG_VERIFY(sgid == pw->pw_gid);
208#else
209 rgid = getgid();
210 egid = getegid();
211#endif
212 PJDLOG_VERIFY(rgid == pw->pw_gid);
213 PJDLOG_VERIFY(egid == pw->pw_gid);
214 PJDLOG_VERIFY(getgroups(0, NULL) == ngroups);
215 ggroups = malloc(sizeof(ggroups[0]) * ngroups);
216 if (ggroups == NULL) {
217 pjdlog_error("Unable to allocate memory for %d groups.",
218 ngroups);
219 goto out;
220 }
221 PJDLOG_VERIFY(getgroups(ngroups, ggroups) == ngroups);
222 qsort(groups, (size_t)ngroups, sizeof(groups[0]), groups_compare);
223 qsort(ggroups, (size_t)ngroups, sizeof(ggroups[0]), groups_compare);
224 PJDLOG_VERIFY(bcmp(groups, ggroups, sizeof(groups[0]) * ngroups) == 0);
225
226 pjdlog_debug(1,
227 "Privileges successfully dropped using %s%s+setgid+setuid.",
228 capsicum ? "capsicum+" : "", jailed ? "jail" : "chroot");
229
230 ret = 0;
231out:
232 if (groups != NULL)
233 free(groups);
234 if (ggroups != NULL)
235 free(ggroups);
236 return (ret);
237}
52#include "sandbox.h"
53
54static int
55groups_compare(const void *grp0, const void *grp1)
56{
57 gid_t gr0 = *(const gid_t *)grp0;
58 gid_t gr1 = *(const gid_t *)grp1;
59
60 return (gr0 <= gr1 ? (gr0 < gr1 ? -1 : 0) : 1);
61
62}
63
64int
65sandbox(const char *user, bool capsicum, const char *fmt, ...)
66{
67#ifdef HAVE_JAIL
68 struct jail jailst;
69 char *jailhost;
70 va_list ap;
71#endif
72 struct passwd *pw;
73 uid_t ruid, euid;
74 gid_t rgid, egid;
75#ifdef HAVE_GETRESUID
76 uid_t suid;
77#endif
78#ifdef HAVE_GETRESGID
79 gid_t sgid;
80#endif
81 gid_t *groups, *ggroups;
82 bool jailed;
83 int ngroups, ret;
84
85 PJDLOG_ASSERT(user != NULL);
86 PJDLOG_ASSERT(fmt != NULL);
87
88 ret = -1;
89 groups = NULL;
90 ggroups = NULL;
91
92 /*
93 * According to getpwnam(3) we have to clear errno before calling the
94 * function to be able to distinguish between an error and missing
95 * entry (with is not treated as error by getpwnam(3)).
96 */
97 errno = 0;
98 pw = getpwnam(user);
99 if (pw == NULL) {
100 if (errno != 0) {
101 pjdlog_errno(LOG_ERR,
102 "Unable to find info about '%s' user", user);
103 goto out;
104 } else {
105 pjdlog_error("'%s' user doesn't exist.", user);
106 errno = ENOENT;
107 goto out;
108 }
109 }
110
111 ngroups = sysconf(_SC_NGROUPS_MAX);
112 if (ngroups == -1) {
113 pjdlog_errno(LOG_WARNING,
114 "Unable to obtain maximum number of groups");
115 ngroups = NGROUPS_MAX;
116 }
117 ngroups++; /* For base gid. */
118 groups = malloc(sizeof(groups[0]) * ngroups);
119 if (groups == NULL) {
120 pjdlog_error("Unable to allocate memory for %d groups.",
121 ngroups);
122 goto out;
123 }
124 if (getgrouplist(user, pw->pw_gid, groups, &ngroups) == -1) {
125 pjdlog_error("Unable to obtain groups of user %s.", user);
126 goto out;
127 }
128
129#ifdef HAVE_JAIL
130 va_start(ap, fmt);
131 (void)vasprintf(&jailhost, fmt, ap);
132 va_end(ap);
133 if (jailhost == NULL) {
134 pjdlog_error("Unable to allocate memory for jail host name.");
135 goto out;
136 }
137 bzero(&jailst, sizeof(jailst));
138 jailst.version = JAIL_API_VERSION;
139 jailst.path = pw->pw_dir;
140 jailst.hostname = jailhost;
141 if (jail(&jailst) >= 0) {
142 jailed = true;
143 } else {
144 jailed = false;
145 pjdlog_errno(LOG_WARNING,
146 "Unable to jail to directory %s", pw->pw_dir);
147 }
148 free(jailhost);
149#else /* !HAVE_JAIL */
150 jailed = false;
151#endif /* !HAVE_JAIL */
152
153 if (!jailed) {
154 if (chroot(pw->pw_dir) == -1) {
155 pjdlog_errno(LOG_ERR,
156 "Unable to change root directory to %s",
157 pw->pw_dir);
158 goto out;
159 }
160 }
161 PJDLOG_VERIFY(chdir("/") == 0);
162
163 if (setgroups(ngroups, groups) == -1) {
164 pjdlog_errno(LOG_ERR, "Unable to set groups");
165 goto out;
166 }
167 if (setgid(pw->pw_gid) == -1) {
168 pjdlog_errno(LOG_ERR, "Unable to set gid to %u",
169 (unsigned int)pw->pw_gid);
170 goto out;
171 }
172 if (setuid(pw->pw_uid) == -1) {
173 pjdlog_errno(LOG_ERR, "Unable to set uid to %u",
174 (unsigned int)pw->pw_uid);
175 goto out;
176 }
177
178#ifdef HAVE_CAP_ENTER
179 if (capsicum) {
180 capsicum = (cap_enter() == 0);
181 if (!capsicum) {
182 pjdlog_common(LOG_DEBUG, 1, errno,
183 "Unable to sandbox using capsicum");
184 }
185 }
186#else /* !HAVE_CAP_ENTER */
187 capsicum = false;
188#endif /* !HAVE_CAP_ENTER */
189
190 /*
191 * Better be sure that everything succeeded.
192 */
193#ifdef HAVE_GETRESUID
194 PJDLOG_VERIFY(getresuid(&ruid, &euid, &suid) == 0);
195 PJDLOG_VERIFY(suid == pw->pw_uid);
196#else
197 ruid = getuid();
198 euid = geteuid();
199#endif
200 PJDLOG_VERIFY(ruid == pw->pw_uid);
201 PJDLOG_VERIFY(euid == pw->pw_uid);
202#ifdef HAVE_GETRESGID
203 PJDLOG_VERIFY(getresgid(&rgid, &egid, &sgid) == 0);
204 PJDLOG_VERIFY(sgid == pw->pw_gid);
205#else
206 rgid = getgid();
207 egid = getegid();
208#endif
209 PJDLOG_VERIFY(rgid == pw->pw_gid);
210 PJDLOG_VERIFY(egid == pw->pw_gid);
211 PJDLOG_VERIFY(getgroups(0, NULL) == ngroups);
212 ggroups = malloc(sizeof(ggroups[0]) * ngroups);
213 if (ggroups == NULL) {
214 pjdlog_error("Unable to allocate memory for %d groups.",
215 ngroups);
216 goto out;
217 }
218 PJDLOG_VERIFY(getgroups(ngroups, ggroups) == ngroups);
219 qsort(groups, (size_t)ngroups, sizeof(groups[0]), groups_compare);
220 qsort(ggroups, (size_t)ngroups, sizeof(ggroups[0]), groups_compare);
221 PJDLOG_VERIFY(bcmp(groups, ggroups, sizeof(groups[0]) * ngroups) == 0);
222
223 pjdlog_debug(1,
224 "Privileges successfully dropped using %s%s+setgid+setuid.",
225 capsicum ? "capsicum+" : "", jailed ? "jail" : "chroot");
226
227 ret = 0;
228out:
229 if (groups != NULL)
230 free(groups);
231 if (ggroups != NULL)
232 free(ggroups);
233 return (ret);
234}