1*aa772005SRobert Watson /*- 2*aa772005SRobert Watson * Copyright (c) 2012 The FreeBSD Foundation 3*aa772005SRobert Watson * All rights reserved. 4*aa772005SRobert Watson * 5*aa772005SRobert Watson * This software was developed by Pawel Jakub Dawidek under sponsorship from 6*aa772005SRobert Watson * the FreeBSD Foundation. 7*aa772005SRobert Watson * 8*aa772005SRobert Watson * Redistribution and use in source and binary forms, with or without 9*aa772005SRobert Watson * modification, are permitted provided that the following conditions 10*aa772005SRobert Watson * are met: 11*aa772005SRobert Watson * 1. Redistributions of source code must retain the above copyright 12*aa772005SRobert Watson * notice, this list of conditions and the following disclaimer. 13*aa772005SRobert Watson * 2. Redistributions in binary form must reproduce the above copyright 14*aa772005SRobert Watson * notice, this list of conditions and the following disclaimer in the 15*aa772005SRobert Watson * documentation and/or other materials provided with the distribution. 16*aa772005SRobert Watson * 17*aa772005SRobert Watson * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 18*aa772005SRobert Watson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19*aa772005SRobert Watson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20*aa772005SRobert Watson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 21*aa772005SRobert Watson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22*aa772005SRobert Watson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23*aa772005SRobert Watson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24*aa772005SRobert Watson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25*aa772005SRobert Watson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26*aa772005SRobert Watson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27*aa772005SRobert Watson * SUCH DAMAGE. 28*aa772005SRobert Watson * 29*aa772005SRobert Watson * $P4: //depot/projects/trustedbsd/openbsm/bin/auditdistd/sandbox.c#3 $ 30*aa772005SRobert Watson */ 31*aa772005SRobert Watson 32*aa772005SRobert Watson #include <config/config.h> 33*aa772005SRobert Watson 34*aa772005SRobert Watson #include <sys/param.h> 35*aa772005SRobert Watson #ifdef HAVE_JAIL 36*aa772005SRobert Watson #include <sys/jail.h> 37*aa772005SRobert Watson #endif 38*aa772005SRobert Watson #ifdef HAVE_CAP_ENTER 39*aa772005SRobert Watson #include <sys/capability.h> 40*aa772005SRobert Watson #endif 41*aa772005SRobert Watson 42*aa772005SRobert Watson #include <errno.h> 43*aa772005SRobert Watson #include <pwd.h> 44*aa772005SRobert Watson #include <stdarg.h> 45*aa772005SRobert Watson #include <stdbool.h> 46*aa772005SRobert Watson #include <stdio.h> 47*aa772005SRobert Watson #include <stdlib.h> 48*aa772005SRobert Watson #include <strings.h> 49*aa772005SRobert Watson #include <unistd.h> 50*aa772005SRobert Watson 51*aa772005SRobert Watson #include "pjdlog.h" 52*aa772005SRobert Watson #include "sandbox.h" 53*aa772005SRobert Watson 54*aa772005SRobert Watson static int 55*aa772005SRobert Watson groups_compare(const void *grp0, const void *grp1) 56*aa772005SRobert Watson { 57*aa772005SRobert Watson gid_t gr0 = *(const gid_t *)grp0; 58*aa772005SRobert Watson gid_t gr1 = *(const gid_t *)grp1; 59*aa772005SRobert Watson 60*aa772005SRobert Watson return (gr0 <= gr1 ? (gr0 < gr1 ? -1 : 0) : 1); 61*aa772005SRobert Watson 62*aa772005SRobert Watson } 63*aa772005SRobert Watson 64*aa772005SRobert Watson int 65*aa772005SRobert Watson sandbox(const char *user, bool capsicum, const char *fmt, ...) 66*aa772005SRobert Watson { 67*aa772005SRobert Watson #ifdef HAVE_JAIL 68*aa772005SRobert Watson struct jail jailst; 69*aa772005SRobert Watson char *jailhost; 70*aa772005SRobert Watson va_list ap; 71*aa772005SRobert Watson #endif 72*aa772005SRobert Watson struct passwd *pw; 73*aa772005SRobert Watson uid_t ruid, euid; 74*aa772005SRobert Watson gid_t rgid, egid; 75*aa772005SRobert Watson #ifdef HAVE_GETRESUID 76*aa772005SRobert Watson uid_t suid; 77*aa772005SRobert Watson #endif 78*aa772005SRobert Watson #ifdef HAVE_GETRESGID 79*aa772005SRobert Watson gid_t sgid; 80*aa772005SRobert Watson #endif 81*aa772005SRobert Watson gid_t *groups, *ggroups; 82*aa772005SRobert Watson bool jailed; 83*aa772005SRobert Watson int ngroups, ret; 84*aa772005SRobert Watson 85*aa772005SRobert Watson PJDLOG_ASSERT(user != NULL); 86*aa772005SRobert Watson PJDLOG_ASSERT(fmt != NULL); 87*aa772005SRobert Watson 88*aa772005SRobert Watson ret = -1; 89*aa772005SRobert Watson groups = NULL; 90*aa772005SRobert Watson ggroups = NULL; 91*aa772005SRobert Watson 92*aa772005SRobert Watson /* 93*aa772005SRobert Watson * According to getpwnam(3) we have to clear errno before calling the 94*aa772005SRobert Watson * function to be able to distinguish between an error and missing 95*aa772005SRobert Watson * entry (with is not treated as error by getpwnam(3)). 96*aa772005SRobert Watson */ 97*aa772005SRobert Watson errno = 0; 98*aa772005SRobert Watson pw = getpwnam(user); 99*aa772005SRobert Watson if (pw == NULL) { 100*aa772005SRobert Watson if (errno != 0) { 101*aa772005SRobert Watson pjdlog_errno(LOG_ERR, 102*aa772005SRobert Watson "Unable to find info about '%s' user", user); 103*aa772005SRobert Watson goto out; 104*aa772005SRobert Watson } else { 105*aa772005SRobert Watson pjdlog_error("'%s' user doesn't exist.", user); 106*aa772005SRobert Watson errno = ENOENT; 107*aa772005SRobert Watson goto out; 108*aa772005SRobert Watson } 109*aa772005SRobert Watson } 110*aa772005SRobert Watson 111*aa772005SRobert Watson ngroups = sysconf(_SC_NGROUPS_MAX); 112*aa772005SRobert Watson if (ngroups == -1) { 113*aa772005SRobert Watson pjdlog_errno(LOG_WARNING, 114*aa772005SRobert Watson "Unable to obtain maximum number of groups"); 115*aa772005SRobert Watson ngroups = NGROUPS_MAX; 116*aa772005SRobert Watson } 117*aa772005SRobert Watson ngroups++; /* For base gid. */ 118*aa772005SRobert Watson groups = malloc(sizeof(groups[0]) * ngroups); 119*aa772005SRobert Watson if (groups == NULL) { 120*aa772005SRobert Watson pjdlog_error("Unable to allocate memory for %d groups.", 121*aa772005SRobert Watson ngroups); 122*aa772005SRobert Watson goto out; 123*aa772005SRobert Watson } 124*aa772005SRobert Watson if (getgrouplist(user, pw->pw_gid, groups, &ngroups) == -1) { 125*aa772005SRobert Watson pjdlog_error("Unable to obtain groups of user %s.", user); 126*aa772005SRobert Watson goto out; 127*aa772005SRobert Watson } 128*aa772005SRobert Watson 129*aa772005SRobert Watson #ifdef HAVE_JAIL 130*aa772005SRobert Watson va_start(ap, fmt); 131*aa772005SRobert Watson (void)vasprintf(&jailhost, fmt, ap); 132*aa772005SRobert Watson va_end(ap); 133*aa772005SRobert Watson if (jailhost == NULL) { 134*aa772005SRobert Watson pjdlog_error("Unable to allocate memory for jail host name."); 135*aa772005SRobert Watson goto out; 136*aa772005SRobert Watson } 137*aa772005SRobert Watson bzero(&jailst, sizeof(jailst)); 138*aa772005SRobert Watson jailst.version = JAIL_API_VERSION; 139*aa772005SRobert Watson jailst.path = pw->pw_dir; 140*aa772005SRobert Watson jailst.hostname = jailhost; 141*aa772005SRobert Watson if (jail(&jailst) >= 0) { 142*aa772005SRobert Watson jailed = true; 143*aa772005SRobert Watson } else { 144*aa772005SRobert Watson jailed = false; 145*aa772005SRobert Watson pjdlog_errno(LOG_WARNING, 146*aa772005SRobert Watson "Unable to jail to directory %s", pw->pw_dir); 147*aa772005SRobert Watson } 148*aa772005SRobert Watson free(jailhost); 149*aa772005SRobert Watson #else /* !HAVE_JAIL */ 150*aa772005SRobert Watson jailed = false; 151*aa772005SRobert Watson #endif /* !HAVE_JAIL */ 152*aa772005SRobert Watson 153*aa772005SRobert Watson if (!jailed) { 154*aa772005SRobert Watson if (chroot(pw->pw_dir) == -1) { 155*aa772005SRobert Watson pjdlog_errno(LOG_ERR, 156*aa772005SRobert Watson "Unable to change root directory to %s", 157*aa772005SRobert Watson pw->pw_dir); 158*aa772005SRobert Watson goto out; 159*aa772005SRobert Watson } 160*aa772005SRobert Watson } 161*aa772005SRobert Watson PJDLOG_VERIFY(chdir("/") == 0); 162*aa772005SRobert Watson 163*aa772005SRobert Watson if (setgroups(ngroups, groups) == -1) { 164*aa772005SRobert Watson pjdlog_errno(LOG_ERR, "Unable to set groups"); 165*aa772005SRobert Watson goto out; 166*aa772005SRobert Watson } 167*aa772005SRobert Watson if (setgid(pw->pw_gid) == -1) { 168*aa772005SRobert Watson pjdlog_errno(LOG_ERR, "Unable to set gid to %u", 169*aa772005SRobert Watson (unsigned int)pw->pw_gid); 170*aa772005SRobert Watson goto out; 171*aa772005SRobert Watson } 172*aa772005SRobert Watson if (setuid(pw->pw_uid) == -1) { 173*aa772005SRobert Watson pjdlog_errno(LOG_ERR, "Unable to set uid to %u", 174*aa772005SRobert Watson (unsigned int)pw->pw_uid); 175*aa772005SRobert Watson goto out; 176*aa772005SRobert Watson } 177*aa772005SRobert Watson 178*aa772005SRobert Watson #ifdef HAVE_CAP_ENTER 179*aa772005SRobert Watson if (capsicum) { 180*aa772005SRobert Watson capsicum = (cap_enter() == 0); 181*aa772005SRobert Watson if (!capsicum) { 182*aa772005SRobert Watson pjdlog_common(LOG_DEBUG, 1, errno, 183*aa772005SRobert Watson "Unable to sandbox using capsicum"); 184*aa772005SRobert Watson } 185*aa772005SRobert Watson } 186*aa772005SRobert Watson #else /* !HAVE_CAP_ENTER */ 187*aa772005SRobert Watson capsicum = false; 188*aa772005SRobert Watson #endif /* !HAVE_CAP_ENTER */ 189*aa772005SRobert Watson 190*aa772005SRobert Watson /* 191*aa772005SRobert Watson * Better be sure that everything succeeded. 192*aa772005SRobert Watson */ 193*aa772005SRobert Watson #ifdef HAVE_GETRESUID 194*aa772005SRobert Watson PJDLOG_VERIFY(getresuid(&ruid, &euid, &suid) == 0); 195*aa772005SRobert Watson PJDLOG_VERIFY(suid == pw->pw_uid); 196*aa772005SRobert Watson #else 197*aa772005SRobert Watson ruid = getuid(); 198*aa772005SRobert Watson euid = geteuid(); 199*aa772005SRobert Watson #endif 200*aa772005SRobert Watson PJDLOG_VERIFY(ruid == pw->pw_uid); 201*aa772005SRobert Watson PJDLOG_VERIFY(euid == pw->pw_uid); 202*aa772005SRobert Watson #ifdef HAVE_GETRESGID 203*aa772005SRobert Watson PJDLOG_VERIFY(getresgid(&rgid, &egid, &sgid) == 0); 204*aa772005SRobert Watson PJDLOG_VERIFY(sgid == pw->pw_gid); 205*aa772005SRobert Watson #else 206*aa772005SRobert Watson rgid = getgid(); 207*aa772005SRobert Watson egid = getegid(); 208*aa772005SRobert Watson #endif 209*aa772005SRobert Watson PJDLOG_VERIFY(rgid == pw->pw_gid); 210*aa772005SRobert Watson PJDLOG_VERIFY(egid == pw->pw_gid); 211*aa772005SRobert Watson PJDLOG_VERIFY(getgroups(0, NULL) == ngroups); 212*aa772005SRobert Watson ggroups = malloc(sizeof(ggroups[0]) * ngroups); 213*aa772005SRobert Watson if (ggroups == NULL) { 214*aa772005SRobert Watson pjdlog_error("Unable to allocate memory for %d groups.", 215*aa772005SRobert Watson ngroups); 216*aa772005SRobert Watson goto out; 217*aa772005SRobert Watson } 218*aa772005SRobert Watson PJDLOG_VERIFY(getgroups(ngroups, ggroups) == ngroups); 219*aa772005SRobert Watson qsort(groups, (size_t)ngroups, sizeof(groups[0]), groups_compare); 220*aa772005SRobert Watson qsort(ggroups, (size_t)ngroups, sizeof(ggroups[0]), groups_compare); 221*aa772005SRobert Watson PJDLOG_VERIFY(bcmp(groups, ggroups, sizeof(groups[0]) * ngroups) == 0); 222*aa772005SRobert Watson 223*aa772005SRobert Watson pjdlog_debug(1, 224*aa772005SRobert Watson "Privileges successfully dropped using %s%s+setgid+setuid.", 225*aa772005SRobert Watson capsicum ? "capsicum+" : "", jailed ? "jail" : "chroot"); 226*aa772005SRobert Watson 227*aa772005SRobert Watson ret = 0; 228*aa772005SRobert Watson out: 229*aa772005SRobert Watson if (groups != NULL) 230*aa772005SRobert Watson free(groups); 231*aa772005SRobert Watson if (ggroups != NULL) 232*aa772005SRobert Watson free(ggroups); 233*aa772005SRobert Watson return (ret); 234*aa772005SRobert Watson } 235