/ libpkg / pkg_sandbox.c
pkg_sandbox.c
  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  #include <sys/resource.h>
 29  #include <sys/types.h>
 30  #include <sys/wait.h>
 31  #include <sys/socket.h>
 32  
 33  #if __has_include(<sys/capsicum.h>)
 34  #include <sys/capsicum.h>
 35  #define HAVE_CAPSICUM 1
 36  #endif
 37  
 38  #include <err.h>
 39  #include <string.h>
 40  #include <unistd.h>
 41  #include <errno.h>
 42  #include <time.h>
 43  #include <signal.h>
 44  #include <pwd.h>
 45  
 46  #ifdef __linux__
 47  # ifdef __GLIBC__
 48  #  include <grp.h>
 49  # endif
 50  #endif
 51  
 52  #include <xmalloc.h>
 53  #include "pkg.h"
 54  
 55  int
 56  pkg_handle_sandboxed_call(pkg_sandbox_cb func, int fd, void *ud)
 57  {
 58  	pid_t pid;
 59  	int status, ret;
 60  	struct rlimit rl_zero;
 61  
 62  	ret = -1;
 63  	pid = fork();
 64  
 65  	switch(pid) {
 66  	case -1:
 67  		warn("fork failed");
 68  		return (EPKG_FATAL);
 69  		break;
 70  	case 0:
 71  		break;
 72  	default:
 73  		/* Parent process */
 74  		while (waitpid(pid, &status, 0) == -1) {
 75  			if (errno != EINTR) {
 76  				warn("Sandboxed process pid=%d", (int)pid);
 77  				ret = -1;
 78  				break;
 79  			}
 80  		}
 81  
 82  		if (WIFEXITED(status)) {
 83  			ret = WEXITSTATUS(status);
 84  		}
 85  		if (WIFSIGNALED(status)) {
 86  			/* Process got some terminating signal, hence stop the loop */
 87  			fprintf(stderr, "Sandboxed process pid=%d terminated abnormally by signal: %d\n",
 88  					(int)pid, WTERMSIG(status));
 89  			ret = -1;
 90  		}
 91  		return (ret);
 92  	}
 93  
 94  	rl_zero.rlim_cur = rl_zero.rlim_max = 0;
 95  	if (setrlimit(RLIMIT_NPROC, &rl_zero) == -1)
 96  		err(EXIT_FAILURE, "Unable to setrlimit(RLIMIT_NPROC)");
 97  
 98  	/* Here comes child process */
 99  #ifdef HAVE_CAPSICUM
100  #ifndef COVERAGE
101  	if (cap_enter() < 0 && errno != ENOSYS) {
102  		warn("cap_enter() failed");
103  		_exit(EXIT_FAILURE);
104  	}
105  #endif
106  #endif
107  
108  	ret = func(fd, ud);
109  
110  	_exit(ret);
111  }
112  
113  int
114  pkg_handle_sandboxed_get_string(pkg_sandbox_cb func, char **result, int64_t *len,
115  		void *ud)
116  {
117  	pid_t pid;
118  	struct rlimit rl_zero;
119  	int	status, ret = EPKG_OK;
120  	int pair[2], r, allocated_len = 0, off = 0;
121  	char *buf = NULL;
122  
123  	if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) {
124  		warn("socketpair failed");
125  		return (EPKG_FATAL);
126  	}
127  
128  	pid = fork();
129  
130  	switch(pid) {
131  	case -1:
132  		warn("fork failed");
133  		return (EPKG_FATAL);
134  		break;
135  	case 0:
136  		break;
137  	default:
138  		/* Parent process */
139  		close(pair[0]);
140  		/*
141  		 * We use blocking IO here as if the child is terminated we would have
142  		 * EINTR here
143  		 */
144  		buf = xmalloc(BUFSIZ);
145  		allocated_len = BUFSIZ;
146  		do {
147  			if (off >= allocated_len) {
148  				allocated_len *= 2;
149  				buf = xrealloc(buf, allocated_len);
150  			}
151  
152  			r = read(pair[1], buf + off, allocated_len - off);
153  			if (r == -1 && errno != EINTR) {
154  				free(buf);
155  				warn("read failed");
156  				return (EPKG_FATAL);
157  			}
158  			else if (r > 0) {
159  				off += r;
160  			}
161  		} while (r > 0);
162  
163  		/* Fill the result buffer */
164  		if (off >= allocated_len) {
165  			allocated_len = off + 1;
166  			buf = xrealloc(buf, allocated_len);
167  		}
168  		buf[off] = '\0';
169  		*len = off;
170  		*result = buf;
171  		if (*result == NULL) {
172  			warn("malloc failed");
173  			kill(pid, SIGTERM);
174  			ret = EPKG_FATAL;
175  		}
176  		while (waitpid(pid, &status, 0) == -1) {
177  			if (errno != EINTR) {
178  				warn("Sandboxed process pid=%d", (int)pid);
179  				ret = -1;
180  				break;
181  			}
182  		}
183  
184  		if (WIFEXITED(status)) {
185  			ret = WEXITSTATUS(status);
186  		}
187  		if (WIFSIGNALED(status)) {
188  			/* Process got some terminating signal, hence stop the loop */
189  			fprintf(stderr, "Sandboxed process pid=%d terminated abnormally by signal: %d\n",
190  					(int)pid, WTERMSIG(status));
191  			ret = -1;
192  		}
193  		return (ret);
194  	}
195  
196  	/* Here comes child process */
197  	close(pair[1]);
198  
199  	pkg_drop_privileges();
200  
201  	rl_zero.rlim_cur = rl_zero.rlim_max = 0;
202  	if (setrlimit(RLIMIT_NPROC, &rl_zero) == -1)
203  		err(EXIT_FAILURE, "Unable to setrlimit(RLIMIT_NPROC)");
204  
205  #ifdef HAVE_CAPSICUM
206  #ifndef COVERAGE
207  	if (cap_enter() < 0 && errno != ENOSYS) {
208  		warn("cap_enter() failed");
209  		return (EPKG_FATAL);
210  	}
211  #endif
212  #endif
213  
214  	ret = func(pair[0], ud);
215  
216  	close(pair[0]);
217  
218  	_exit(ret);
219  }
220  
221  void
222  pkg_drop_privileges(void)
223  {
224  	struct passwd *nobody;
225  
226  	if (geteuid() == 0) {
227  		nobody = getpwnam("nobody");
228  		if (nobody == NULL)
229  			errx(EXIT_FAILURE, "Unable to drop privileges: no 'nobody' user");
230  		setgroups(1, &nobody->pw_gid);
231  		/* setgid also sets egid and setuid also sets euid */
232  		if (setgid(nobody->pw_gid) == -1)
233  			err(EXIT_FAILURE, "Unable to setgid");
234  		if (setuid(nobody->pw_uid) == -1)
235  			err(EXIT_FAILURE, "Unable to setuid");
236  	}
237  }