1204076Spjd/*- 2204076Spjd * Copyright (c) 2010 The FreeBSD Foundation 3219887Spjd * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net> 4204076Spjd * All rights reserved. 5204076Spjd * 6204076Spjd * This software was developed by Pawel Jakub Dawidek under sponsorship from 7204076Spjd * the FreeBSD Foundation. 8204076Spjd * 9204076Spjd * Redistribution and use in source and binary forms, with or without 10204076Spjd * modification, are permitted provided that the following conditions 11204076Spjd * are met: 12204076Spjd * 1. Redistributions of source code must retain the above copyright 13204076Spjd * notice, this list of conditions and the following disclaimer. 14204076Spjd * 2. Redistributions in binary form must reproduce the above copyright 15204076Spjd * notice, this list of conditions and the following disclaimer in the 16204076Spjd * documentation and/or other materials provided with the distribution. 17204076Spjd * 18204076Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 19204076Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20204076Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21204076Spjd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 22204076Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23204076Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24204076Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25204076Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26204076Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27204076Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28204076Spjd * SUCH DAMAGE. 29204076Spjd */ 30204076Spjd 31204076Spjd#include <sys/cdefs.h> 32204076Spjd__FBSDID("$FreeBSD$"); 33204076Spjd 34223585Spjd#ifdef HAVE_CAPSICUM 35219847Spjd#include <sys/capability.h> 36223585Spjd#endif 37221899Spjd#include <sys/param.h> 38204076Spjd#include <sys/disk.h> 39204076Spjd#include <sys/ioctl.h> 40221899Spjd#include <sys/jail.h> 41204076Spjd#include <sys/stat.h> 42204076Spjd 43204076Spjd#include <errno.h> 44204076Spjd#include <fcntl.h> 45218048Spjd#include <pwd.h> 46219815Spjd#include <stdarg.h> 47219847Spjd#include <stdbool.h> 48219815Spjd#include <stdio.h> 49219815Spjd#include <string.h> 50218048Spjd#include <unistd.h> 51204076Spjd 52204076Spjd#include <pjdlog.h> 53204076Spjd 54204076Spjd#include "hast.h" 55204076Spjd#include "subr.h" 56204076Spjd 57204076Spjdint 58219815Spjdvsnprlcat(char *str, size_t size, const char *fmt, va_list ap) 59219815Spjd{ 60219815Spjd size_t len; 61219815Spjd 62219815Spjd len = strlen(str); 63219815Spjd return (vsnprintf(str + len, size - len, fmt, ap)); 64219815Spjd} 65219815Spjd 66219815Spjdint 67219815Spjdsnprlcat(char *str, size_t size, const char *fmt, ...) 68219815Spjd{ 69219815Spjd va_list ap; 70219815Spjd int result; 71219815Spjd 72219815Spjd va_start(ap, fmt); 73219815Spjd result = vsnprlcat(str, size, fmt, ap); 74219815Spjd va_end(ap); 75219815Spjd return (result); 76219815Spjd} 77219815Spjd 78219815Spjdint 79204076Spjdprovinfo(struct hast_resource *res, bool dowrite) 80204076Spjd{ 81204076Spjd struct stat sb; 82204076Spjd 83218138Spjd PJDLOG_ASSERT(res->hr_localpath != NULL && 84218138Spjd res->hr_localpath[0] != '\0'); 85204076Spjd 86204076Spjd if (res->hr_localfd == -1) { 87204076Spjd res->hr_localfd = open(res->hr_localpath, 88204076Spjd dowrite ? O_RDWR : O_RDONLY); 89231017Strociny if (res->hr_localfd == -1) { 90229509Strociny pjdlog_errno(LOG_ERR, "Unable to open %s", 91229509Strociny res->hr_localpath); 92204076Spjd return (-1); 93204076Spjd } 94204076Spjd } 95231017Strociny if (fstat(res->hr_localfd, &sb) == -1) { 96229509Strociny pjdlog_errno(LOG_ERR, "Unable to stat %s", res->hr_localpath); 97204076Spjd return (-1); 98204076Spjd } 99204076Spjd if (S_ISCHR(sb.st_mode)) { 100204076Spjd /* 101204076Spjd * If this is character device, it is most likely GEOM provider. 102204076Spjd */ 103204076Spjd if (ioctl(res->hr_localfd, DIOCGMEDIASIZE, 104231017Strociny &res->hr_local_mediasize) == -1) { 105229509Strociny pjdlog_errno(LOG_ERR, 106204076Spjd "Unable obtain provider %s mediasize", 107229509Strociny res->hr_localpath); 108204076Spjd return (-1); 109204076Spjd } 110204076Spjd if (ioctl(res->hr_localfd, DIOCGSECTORSIZE, 111231017Strociny &res->hr_local_sectorsize) == -1) { 112229509Strociny pjdlog_errno(LOG_ERR, 113204076Spjd "Unable obtain provider %s sectorsize", 114229509Strociny res->hr_localpath); 115204076Spjd return (-1); 116204076Spjd } 117204076Spjd } else if (S_ISREG(sb.st_mode)) { 118204076Spjd /* 119204076Spjd * We also support regular files for which we hardcode 120204076Spjd * sector size of 512 bytes. 121204076Spjd */ 122204076Spjd res->hr_local_mediasize = sb.st_size; 123204076Spjd res->hr_local_sectorsize = 512; 124204076Spjd } else { 125204076Spjd /* 126204076Spjd * We support no other file types. 127204076Spjd */ 128204076Spjd pjdlog_error("%s is neither GEOM provider nor regular file.", 129204076Spjd res->hr_localpath); 130204076Spjd errno = EFTYPE; 131204076Spjd return (-1); 132204076Spjd } 133204076Spjd return (0); 134204076Spjd} 135204076Spjd 136204076Spjdconst char * 137204076Spjdrole2str(int role) 138204076Spjd{ 139204076Spjd 140219864Spjd switch (role) { 141219864Spjd case HAST_ROLE_INIT: 142204076Spjd return ("init"); 143219864Spjd case HAST_ROLE_PRIMARY: 144204076Spjd return ("primary"); 145219864Spjd case HAST_ROLE_SECONDARY: 146204076Spjd return ("secondary"); 147204076Spjd } 148204076Spjd return ("unknown"); 149204076Spjd} 150218048Spjd 151218048Spjdint 152231017Strocinydrop_privs(const struct hast_resource *res) 153218048Spjd{ 154221899Spjd char jailhost[sizeof(res->hr_name) * 2]; 155221899Spjd struct jail jailst; 156218048Spjd struct passwd *pw; 157218048Spjd uid_t ruid, euid, suid; 158218048Spjd gid_t rgid, egid, sgid; 159218048Spjd gid_t gidset[1]; 160221899Spjd bool capsicum, jailed; 161218048Spjd 162218048Spjd /* 163218048Spjd * According to getpwnam(3) we have to clear errno before calling the 164218048Spjd * function to be able to distinguish between an error and missing 165218048Spjd * entry (with is not treated as error by getpwnam(3)). 166218048Spjd */ 167218048Spjd errno = 0; 168218048Spjd pw = getpwnam(HAST_USER); 169218048Spjd if (pw == NULL) { 170218048Spjd if (errno != 0) { 171229509Strociny pjdlog_errno(LOG_ERR, 172229509Strociny "Unable to find info about '%s' user", HAST_USER); 173218048Spjd return (-1); 174218048Spjd } else { 175218048Spjd pjdlog_error("'%s' user doesn't exist.", HAST_USER); 176218048Spjd errno = ENOENT; 177218048Spjd return (-1); 178218048Spjd } 179218048Spjd } 180221899Spjd 181221899Spjd bzero(&jailst, sizeof(jailst)); 182221899Spjd jailst.version = JAIL_API_VERSION; 183221899Spjd jailst.path = pw->pw_dir; 184221899Spjd if (res == NULL) { 185221899Spjd (void)snprintf(jailhost, sizeof(jailhost), "hastctl"); 186221899Spjd } else { 187221899Spjd (void)snprintf(jailhost, sizeof(jailhost), "hastd: %s (%s)", 188221899Spjd res->hr_name, role2str(res->hr_role)); 189218048Spjd } 190221899Spjd jailst.hostname = jailhost; 191221899Spjd jailst.jailname = NULL; 192221899Spjd jailst.ip4s = 0; 193221899Spjd jailst.ip4 = NULL; 194221899Spjd jailst.ip6s = 0; 195221899Spjd jailst.ip6 = NULL; 196221899Spjd if (jail(&jailst) >= 0) { 197221899Spjd jailed = true; 198221899Spjd } else { 199221899Spjd jailed = false; 200221899Spjd pjdlog_errno(LOG_WARNING, 201221899Spjd "Unable to jail to directory to %s", pw->pw_dir); 202221899Spjd if (chroot(pw->pw_dir) == -1) { 203229509Strociny pjdlog_errno(LOG_ERR, 204221899Spjd "Unable to change root directory to %s", 205229509Strociny pw->pw_dir); 206221899Spjd return (-1); 207221899Spjd } 208221899Spjd } 209218048Spjd PJDLOG_VERIFY(chdir("/") == 0); 210218048Spjd gidset[0] = pw->pw_gid; 211218048Spjd if (setgroups(1, gidset) == -1) { 212229509Strociny pjdlog_errno(LOG_ERR, "Unable to set groups to gid %u", 213229509Strociny (unsigned int)pw->pw_gid); 214218048Spjd return (-1); 215218048Spjd } 216218048Spjd if (setgid(pw->pw_gid) == -1) { 217229509Strociny pjdlog_errno(LOG_ERR, "Unable to set gid to %u", 218229509Strociny (unsigned int)pw->pw_gid); 219218048Spjd return (-1); 220218048Spjd } 221218048Spjd if (setuid(pw->pw_uid) == -1) { 222229509Strociny pjdlog_errno(LOG_ERR, "Unable to set uid to %u", 223229509Strociny (unsigned int)pw->pw_uid); 224218048Spjd return (-1); 225218048Spjd } 226218048Spjd 227222224Spjd /* 228222224Spjd * Until capsicum doesn't allow ioctl(2) we cannot use it to sandbox 229222224Spjd * primary and secondary worker processes, as primary uses GGATE 230222224Spjd * ioctls and secondary uses ioctls to handle BIO_DELETE and BIO_FLUSH. 231222224Spjd * For now capsicum is only used to sandbox hastctl. 232222224Spjd */ 233223585Spjd#ifdef HAVE_CAPSICUM 234223584Spjd if (res == NULL) { 235221899Spjd capsicum = (cap_enter() == 0); 236223584Spjd if (!capsicum) { 237223584Spjd pjdlog_common(LOG_DEBUG, 1, errno, 238223584Spjd "Unable to sandbox using capsicum"); 239223584Spjd } 240223584Spjd } else 241223585Spjd#endif 242221899Spjd capsicum = false; 243221898Spjd 244218048Spjd /* 245218048Spjd * Better be sure that everything succeeded. 246218048Spjd */ 247218048Spjd PJDLOG_VERIFY(getresuid(&ruid, &euid, &suid) == 0); 248218048Spjd PJDLOG_VERIFY(ruid == pw->pw_uid); 249218048Spjd PJDLOG_VERIFY(euid == pw->pw_uid); 250218048Spjd PJDLOG_VERIFY(suid == pw->pw_uid); 251218048Spjd PJDLOG_VERIFY(getresgid(&rgid, &egid, &sgid) == 0); 252218048Spjd PJDLOG_VERIFY(rgid == pw->pw_gid); 253218048Spjd PJDLOG_VERIFY(egid == pw->pw_gid); 254218048Spjd PJDLOG_VERIFY(sgid == pw->pw_gid); 255218048Spjd PJDLOG_VERIFY(getgroups(0, NULL) == 1); 256218048Spjd PJDLOG_VERIFY(getgroups(1, gidset) == 1); 257218048Spjd PJDLOG_VERIFY(gidset[0] == pw->pw_gid); 258218048Spjd 259219847Spjd pjdlog_debug(1, 260221899Spjd "Privileges successfully dropped using %s%s+setgid+setuid.", 261221899Spjd capsicum ? "capsicum+" : "", jailed ? "jail" : "chroot"); 262219847Spjd 263218048Spjd return (0); 264218048Spjd} 265