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