subr.c revision 219847
1204076Spjd/*- 2204076Spjd * Copyright (c) 2010 The FreeBSD Foundation 3204076Spjd * All rights reserved. 4204076Spjd * 5204076Spjd * This software was developed by Pawel Jakub Dawidek under sponsorship from 6204076Spjd * the FreeBSD Foundation. 7204076Spjd * 8204076Spjd * Redistribution and use in source and binary forms, with or without 9204076Spjd * modification, are permitted provided that the following conditions 10204076Spjd * are met: 11204076Spjd * 1. Redistributions of source code must retain the above copyright 12204076Spjd * notice, this list of conditions and the following disclaimer. 13204076Spjd * 2. Redistributions in binary form must reproduce the above copyright 14204076Spjd * notice, this list of conditions and the following disclaimer in the 15204076Spjd * documentation and/or other materials provided with the distribution. 16204076Spjd * 17204076Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 18204076Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19204076Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20204076Spjd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 21204076Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22204076Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23204076Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24204076Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25204076Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26204076Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27204076Spjd * SUCH DAMAGE. 28204076Spjd */ 29204076Spjd 30204076Spjd#include <sys/cdefs.h> 31204076Spjd__FBSDID("$FreeBSD: head/sbin/hastd/subr.c 219847 2011-03-21 21:31:50Z pjd $"); 32204076Spjd 33219847Spjd#include <sys/capability.h> 34204076Spjd#include <sys/types.h> 35204076Spjd#include <sys/disk.h> 36204076Spjd#include <sys/ioctl.h> 37204076Spjd#include <sys/stat.h> 38204076Spjd 39204076Spjd#include <errno.h> 40204076Spjd#include <fcntl.h> 41218048Spjd#include <pwd.h> 42219815Spjd#include <stdarg.h> 43219847Spjd#include <stdbool.h> 44219815Spjd#include <stdio.h> 45219815Spjd#include <string.h> 46218048Spjd#include <unistd.h> 47204076Spjd 48204076Spjd#include <pjdlog.h> 49204076Spjd 50204076Spjd#include "hast.h" 51204076Spjd#include "subr.h" 52204076Spjd 53204076Spjdint 54219815Spjdvsnprlcat(char *str, size_t size, const char *fmt, va_list ap) 55219815Spjd{ 56219815Spjd size_t len; 57219815Spjd 58219815Spjd len = strlen(str); 59219815Spjd return (vsnprintf(str + len, size - len, fmt, ap)); 60219815Spjd} 61219815Spjd 62219815Spjdint 63219815Spjdsnprlcat(char *str, size_t size, const char *fmt, ...) 64219815Spjd{ 65219815Spjd va_list ap; 66219815Spjd int result; 67219815Spjd 68219815Spjd va_start(ap, fmt); 69219815Spjd result = vsnprlcat(str, size, fmt, ap); 70219815Spjd va_end(ap); 71219815Spjd return (result); 72219815Spjd} 73219815Spjd 74219815Spjdint 75204076Spjdprovinfo(struct hast_resource *res, bool dowrite) 76204076Spjd{ 77204076Spjd struct stat sb; 78204076Spjd 79218138Spjd PJDLOG_ASSERT(res->hr_localpath != NULL && 80218138Spjd res->hr_localpath[0] != '\0'); 81204076Spjd 82204076Spjd if (res->hr_localfd == -1) { 83204076Spjd res->hr_localfd = open(res->hr_localpath, 84204076Spjd dowrite ? O_RDWR : O_RDONLY); 85204076Spjd if (res->hr_localfd < 0) { 86204076Spjd KEEP_ERRNO(pjdlog_errno(LOG_ERR, "Unable to open %s", 87204076Spjd res->hr_localpath)); 88204076Spjd return (-1); 89204076Spjd } 90204076Spjd } 91204076Spjd if (fstat(res->hr_localfd, &sb) < 0) { 92204076Spjd KEEP_ERRNO(pjdlog_errno(LOG_ERR, "Unable to stat %s", 93204076Spjd res->hr_localpath)); 94204076Spjd return (-1); 95204076Spjd } 96204076Spjd if (S_ISCHR(sb.st_mode)) { 97204076Spjd /* 98204076Spjd * If this is character device, it is most likely GEOM provider. 99204076Spjd */ 100204076Spjd if (ioctl(res->hr_localfd, DIOCGMEDIASIZE, 101204076Spjd &res->hr_local_mediasize) < 0) { 102204076Spjd KEEP_ERRNO(pjdlog_errno(LOG_ERR, 103204076Spjd "Unable obtain provider %s mediasize", 104204076Spjd res->hr_localpath)); 105204076Spjd return (-1); 106204076Spjd } 107204076Spjd if (ioctl(res->hr_localfd, DIOCGSECTORSIZE, 108204076Spjd &res->hr_local_sectorsize) < 0) { 109204076Spjd KEEP_ERRNO(pjdlog_errno(LOG_ERR, 110204076Spjd "Unable obtain provider %s sectorsize", 111204076Spjd res->hr_localpath)); 112204076Spjd return (-1); 113204076Spjd } 114204076Spjd } else if (S_ISREG(sb.st_mode)) { 115204076Spjd /* 116204076Spjd * We also support regular files for which we hardcode 117204076Spjd * sector size of 512 bytes. 118204076Spjd */ 119204076Spjd res->hr_local_mediasize = sb.st_size; 120204076Spjd res->hr_local_sectorsize = 512; 121204076Spjd } else { 122204076Spjd /* 123204076Spjd * We support no other file types. 124204076Spjd */ 125204076Spjd pjdlog_error("%s is neither GEOM provider nor regular file.", 126204076Spjd res->hr_localpath); 127204076Spjd errno = EFTYPE; 128204076Spjd return (-1); 129204076Spjd } 130204076Spjd return (0); 131204076Spjd} 132204076Spjd 133204076Spjdconst char * 134204076Spjdrole2str(int role) 135204076Spjd{ 136204076Spjd 137204076Spjd switch (role) { 138204076Spjd case HAST_ROLE_INIT: 139204076Spjd return ("init"); 140204076Spjd case HAST_ROLE_PRIMARY: 141204076Spjd return ("primary"); 142204076Spjd case HAST_ROLE_SECONDARY: 143204076Spjd return ("secondary"); 144204076Spjd } 145204076Spjd return ("unknown"); 146204076Spjd} 147218048Spjd 148218048Spjdint 149219847Spjddrop_privs(bool usecapsicum) 150218048Spjd{ 151218048Spjd struct passwd *pw; 152218048Spjd uid_t ruid, euid, suid; 153218048Spjd gid_t rgid, egid, sgid; 154218048Spjd gid_t gidset[1]; 155218048Spjd 156219847Spjd if (usecapsicum) { 157219847Spjd if (cap_enter() == 0) { 158219847Spjd pjdlog_debug(1, 159219847Spjd "Privileges successfully dropped using capsicum."); 160219847Spjd return (0); 161219847Spjd } 162219847Spjd pjdlog_errno(LOG_WARNING, "Unable to sandbox using capsicum"); 163219847Spjd } 164219847Spjd 165218048Spjd /* 166218048Spjd * According to getpwnam(3) we have to clear errno before calling the 167218048Spjd * function to be able to distinguish between an error and missing 168218048Spjd * entry (with is not treated as error by getpwnam(3)). 169218048Spjd */ 170218048Spjd errno = 0; 171218048Spjd pw = getpwnam(HAST_USER); 172218048Spjd if (pw == NULL) { 173218048Spjd if (errno != 0) { 174218048Spjd KEEP_ERRNO(pjdlog_errno(LOG_ERR, 175218048Spjd "Unable to find info about '%s' user", HAST_USER)); 176218048Spjd return (-1); 177218048Spjd } else { 178218048Spjd pjdlog_error("'%s' user doesn't exist.", HAST_USER); 179218048Spjd errno = ENOENT; 180218048Spjd return (-1); 181218048Spjd } 182218048Spjd } 183218048Spjd if (chroot(pw->pw_dir) == -1) { 184218048Spjd KEEP_ERRNO(pjdlog_errno(LOG_ERR, 185218048Spjd "Unable to change root directory to %s", pw->pw_dir)); 186218048Spjd return (-1); 187218048Spjd } 188218048Spjd PJDLOG_VERIFY(chdir("/") == 0); 189218048Spjd gidset[0] = pw->pw_gid; 190218048Spjd if (setgroups(1, gidset) == -1) { 191218048Spjd KEEP_ERRNO(pjdlog_errno(LOG_ERR, 192218048Spjd "Unable to set groups to gid %u", 193218048Spjd (unsigned int)pw->pw_gid)); 194218048Spjd return (-1); 195218048Spjd } 196218048Spjd if (setgid(pw->pw_gid) == -1) { 197218048Spjd KEEP_ERRNO(pjdlog_errno(LOG_ERR, "Unable to set gid to %u", 198218048Spjd (unsigned int)pw->pw_gid)); 199218048Spjd return (-1); 200218048Spjd } 201218048Spjd if (setuid(pw->pw_uid) == -1) { 202218048Spjd KEEP_ERRNO(pjdlog_errno(LOG_ERR, "Unable to set uid to %u", 203218048Spjd (unsigned int)pw->pw_uid)); 204218048Spjd return (-1); 205218048Spjd } 206218048Spjd 207218048Spjd /* 208218048Spjd * Better be sure that everything succeeded. 209218048Spjd */ 210218048Spjd PJDLOG_VERIFY(getresuid(&ruid, &euid, &suid) == 0); 211218048Spjd PJDLOG_VERIFY(ruid == pw->pw_uid); 212218048Spjd PJDLOG_VERIFY(euid == pw->pw_uid); 213218048Spjd PJDLOG_VERIFY(suid == pw->pw_uid); 214218048Spjd PJDLOG_VERIFY(getresgid(&rgid, &egid, &sgid) == 0); 215218048Spjd PJDLOG_VERIFY(rgid == pw->pw_gid); 216218048Spjd PJDLOG_VERIFY(egid == pw->pw_gid); 217218048Spjd PJDLOG_VERIFY(sgid == pw->pw_gid); 218218048Spjd PJDLOG_VERIFY(getgroups(0, NULL) == 1); 219218048Spjd PJDLOG_VERIFY(getgroups(1, gidset) == 1); 220218048Spjd PJDLOG_VERIFY(gidset[0] == pw->pw_gid); 221218048Spjd 222219847Spjd pjdlog_debug(1, 223219847Spjd "Privileges successfully dropped using chroot+setgid+setuid."); 224219847Spjd 225218048Spjd return (0); 226218048Spjd} 227