Branch data Line data Source code
1 : : /*-
2 : : * Copyright (c) 2011-2014 Baptiste Daroussin <bapt@FreeBSD.org>
3 : : * Copyright (c) 2014 Vsevolod Stakhov <vsevolod@FreeBSD.org>
4 : : * All rights reserved.
5 : : *
6 : : * Redistribution and use in source and binary forms, with or without
7 : : * modification, are permitted provided that the following conditions
8 : : * are met:
9 : : * 1. Redistributions of source code must retain the above copyright
10 : : * notice, this list of conditions and the following disclaimer
11 : : * in this position and unchanged.
12 : : * 2. Redistributions in binary form must reproduce the above copyright
13 : : * notice, this list of conditions and the following disclaimer in the
14 : : * documentation and/or other materials provided with the distribution.
15 : : *
16 : : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
17 : : * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 : : * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 : : * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
20 : : * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 : : * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 : : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 : : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 : : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 : : * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 : : */
27 : :
28 : : #ifdef HAVE_CONFIG_H
29 : : #include "pkg_config.h"
30 : : #endif
31 : :
32 : : #include <sys/resource.h>
33 : : #include <sys/types.h>
34 : : #include <sys/wait.h>
35 : : #include <sys/socket.h>
36 : :
37 : : #ifdef HAVE_CAPSICUM
38 : : #include <sys/capsicum.h>
39 : : #endif
40 : :
41 : : #include <err.h>
42 : : #include <string.h>
43 : : #include <unistd.h>
44 : : #include <errno.h>
45 : : #include <time.h>
46 : : #include <signal.h>
47 : : #include <pwd.h>
48 : :
49 : : #ifdef __linux__
50 : : # ifdef __GLIBC__
51 : : # include <grp.h>
52 : : # endif
53 : : #endif
54 : :
55 : : #include "pkg.h"
56 : :
57 : : int
58 : 8 : pkg_handle_sandboxed_call(pkg_sandbox_cb func, int fd, void *ud)
59 : : {
60 : : pid_t pid;
61 : : int status, ret;
62 : : struct rlimit rl_zero;
63 : :
64 : 8 : ret = -1;
65 : 8 : pid = fork();
66 : :
67 [ - - + ]: 8 : switch(pid) {
68 : : case -1:
69 : 0 : warn("fork failed");
70 : 0 : return (EPKG_FATAL);
71 : : break;
72 : : case 0:
73 : 0 : break;
74 : : default:
75 : : /* Parent process */
76 : 8 : while (waitpid(pid, &status, 0) == -1) {
77 [ # # ]: 0 : if (errno != EINTR) {
78 : 0 : warn("Sandboxed process pid=%d", (int)pid);
79 : 0 : ret = -1;
80 : 0 : break;
81 : : }
82 : : }
83 : :
84 : 8 : if (WIFEXITED(status)) {
85 : 8 : ret = WEXITSTATUS(status);
86 : 8 : }
87 : 8 : if (WIFSIGNALED(status)) {
88 : : /* Process got some terminating signal, hence stop the loop */
89 : 0 : fprintf(stderr, "Sandboxed process pid=%d terminated abnormally by signal: %d\n",
90 : 0 : (int)pid, WTERMSIG(status));
91 : 0 : ret = -1;
92 : 0 : }
93 : 8 : return (ret);
94 : : }
95 : :
96 : 0 : rl_zero.rlim_cur = rl_zero.rlim_max = 0;
97 [ # # ]: 0 : if (setrlimit(RLIMIT_NPROC, &rl_zero) == -1)
98 : 0 : err(EXIT_FAILURE, "Unable to setrlimit(RLIMIT_NPROC)");
99 : :
100 : : /* Here comes child process */
101 : : #ifdef HAVE_CAPSICUM
102 : : #ifndef PKG_COVERAGE
103 : : if (cap_enter() < 0 && errno != ENOSYS) {
104 : : warn("cap_enter() failed");
105 : : _exit(EXIT_FAILURE);
106 : : }
107 : : #endif
108 : : #endif
109 : :
110 : 0 : ret = func(fd, ud);
111 : :
112 : 0 : _exit(ret);
113 : 8 : }
114 : :
115 : : int
116 : 72 : pkg_handle_sandboxed_get_string(pkg_sandbox_cb func, char **result, int64_t *len,
117 : : void *ud)
118 : : {
119 : : pid_t pid;
120 : : struct rlimit rl_zero;
121 : 72 : int status, ret = EPKG_OK;
122 : 72 : int pair[2], r, allocated_len = 0, off = 0;
123 : 72 : char *buf = NULL;
124 : :
125 [ - + ]: 72 : if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) {
126 : 0 : warn("socketpair failed");
127 : 0 : return (EPKG_FATAL);
128 : : }
129 : :
130 : 72 : pid = fork();
131 : :
132 [ - - + ]: 72 : switch(pid) {
133 : : case -1:
134 : 0 : warn("fork failed");
135 : 0 : return (EPKG_FATAL);
136 : : break;
137 : : case 0:
138 : 0 : break;
139 : : default:
140 : : /* Parent process */
141 : 72 : close(pair[0]);
142 : : /*
143 : : * We use blocking IO here as if the child is terminated we would have
144 : : * EINTR here
145 : : */
146 : 72 : buf = malloc(BUFSIZ);
147 [ - + ]: 72 : if (buf == NULL) {
148 : 0 : warn("malloc failed");
149 : 0 : return (EPKG_FATAL);
150 : : }
151 : 72 : allocated_len = BUFSIZ;
152 : 80 : do {
153 : 80 : if (off >= allocated_len) {
154 : 0 : allocated_len *= 2;
155 : 0 : buf = realloc(buf, allocated_len);
156 [ # # ]: 0 : if (buf == NULL) {
157 : 0 : warn("realloc failed");
158 : 0 : return (EPKG_FATAL);
159 : : }
160 : 0 : }
161 : :
162 : 0 : r = read(pair[1], buf + off, allocated_len - off);
163 : 0 : if (r == -1 && errno != EINTR) {
164 : 0 : free(buf);
165 : 0 : warn("read failed");
166 : 0 : return (EPKG_FATAL);
167 : : }
168 : 8 : else if (r > 0) {
169 : 8 : off += r;
170 : 8 : }
171 [ + + ]: 80 : } while (r > 0);
172 : :
173 : : /* Fill the result buffer */
174 : 72 : *len = off;
175 : 72 : *result = buf;
176 : 72 : if (*result == NULL) {
177 : 0 : warn("malloc failed");
178 : 0 : kill(pid, SIGTERM);
179 : 0 : ret = EPKG_FATAL;
180 : 0 : }
181 : 72 : while (waitpid(pid, &status, 0) == -1) {
182 [ # # ]: 0 : if (errno != EINTR) {
183 : 0 : warn("Sandboxed process pid=%d", (int)pid);
184 : 0 : ret = -1;
185 : 0 : break;
186 : : }
187 : : }
188 : :
189 : 72 : if (WIFEXITED(status)) {
190 : 72 : ret = WEXITSTATUS(status);
191 : 72 : }
192 : 72 : if (WIFSIGNALED(status)) {
193 : : /* Process got some terminating signal, hence stop the loop */
194 : 0 : fprintf(stderr, "Sandboxed process pid=%d terminated abnormally by signal: %d\n",
195 : 0 : (int)pid, WTERMSIG(status));
196 : 0 : ret = -1;
197 : 0 : }
198 : 72 : return (ret);
199 : : }
200 : :
201 : : /* Here comes child process */
202 : 0 : close(pair[1]);
203 : :
204 : 0 : pkg_drop_privileges();
205 : :
206 : 0 : rl_zero.rlim_cur = rl_zero.rlim_max = 0;
207 [ # # ]: 0 : if (setrlimit(RLIMIT_NPROC, &rl_zero) == -1)
208 : 0 : err(EXIT_FAILURE, "Unable to setrlimit(RLIMIT_NPROC)");
209 : :
210 : : #ifdef HAVE_CAPSICUM
211 : : #ifndef PKG_COVERAGE
212 : : if (cap_enter() < 0 && errno != ENOSYS) {
213 : : warn("cap_enter() failed");
214 : : return (EPKG_FATAL);
215 : : }
216 : : #endif
217 : : #endif
218 : :
219 : 0 : ret = func(pair[0], ud);
220 : :
221 : 0 : close(pair[0]);
222 : :
223 : 0 : _exit(ret);
224 : 72 : }
225 : :
226 : : void
227 : 103 : pkg_drop_privileges(void)
228 : : {
229 : : struct passwd *nobody;
230 : :
231 : 103 : if (geteuid() == 0) {
232 : 0 : nobody = getpwnam("nobody");
233 [ # # ]: 0 : if (nobody == NULL)
234 : 0 : errx(EXIT_FAILURE, "Unable to drop privileges: no 'nobody' user");
235 : 0 : setgroups(1, &nobody->pw_gid);
236 : : /* setgid also sets egid and setuid also sets euid */
237 [ # # ]: 0 : if (setgid(nobody->pw_gid) == -1)
238 : 0 : err(EXIT_FAILURE, "Unable to setgid");
239 [ # # ]: 0 : if (setuid(nobody->pw_uid) == -1)
240 : 0 : err(EXIT_FAILURE, "Unable to setuid");
241 : 0 : }
242 : 103 : }
|