]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/openbsm/bin/auditdistd/sandbox.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / openbsm / bin / auditdistd / sandbox.c
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#3 $
30  */
31
32 #include <config/config.h>
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
51 #include "pjdlog.h"
52 #include "sandbox.h"
53
54 static int
55 groups_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
64 int
65 sandbox(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;
228 out:
229         if (groups != NULL)
230                 free(groups);
231         if (ggroups != NULL)
232                 free(ggroups);
233         return (ret);
234 }