subr.c revision 219887
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: head/sbin/hastd/subr.c 219887 2011-03-22 21:19:51Z pjd $"); 33204076Spjd 34219847Spjd#include <sys/capability.h> 35204076Spjd#include <sys/types.h> 36204076Spjd#include <sys/disk.h> 37204076Spjd#include <sys/ioctl.h> 38204076Spjd#include <sys/stat.h> 39204076Spjd 40204076Spjd#include <errno.h> 41204076Spjd#include <fcntl.h> 42218048Spjd#include <pwd.h> 43219815Spjd#include <stdarg.h> 44219847Spjd#include <stdbool.h> 45219815Spjd#include <stdio.h> 46219815Spjd#include <string.h> 47218048Spjd#include <unistd.h> 48204076Spjd 49204076Spjd#include <pjdlog.h> 50204076Spjd 51204076Spjd#include "hast.h" 52204076Spjd#include "subr.h" 53204076Spjd 54204076Spjdint 55219815Spjdvsnprlcat(char *str, size_t size, const char *fmt, va_list ap) 56219815Spjd{ 57219815Spjd size_t len; 58219815Spjd 59219815Spjd len = strlen(str); 60219815Spjd return (vsnprintf(str + len, size - len, fmt, ap)); 61219815Spjd} 62219815Spjd 63219815Spjdint 64219815Spjdsnprlcat(char *str, size_t size, const char *fmt, ...) 65219815Spjd{ 66219815Spjd va_list ap; 67219815Spjd int result; 68219815Spjd 69219815Spjd va_start(ap, fmt); 70219815Spjd result = vsnprlcat(str, size, fmt, ap); 71219815Spjd va_end(ap); 72219815Spjd return (result); 73219815Spjd} 74219815Spjd 75219815Spjdint 76204076Spjdprovinfo(struct hast_resource *res, bool dowrite) 77204076Spjd{ 78204076Spjd struct stat sb; 79204076Spjd 80218138Spjd PJDLOG_ASSERT(res->hr_localpath != NULL && 81218138Spjd res->hr_localpath[0] != '\0'); 82204076Spjd 83204076Spjd if (res->hr_localfd == -1) { 84204076Spjd res->hr_localfd = open(res->hr_localpath, 85204076Spjd dowrite ? O_RDWR : O_RDONLY); 86204076Spjd if (res->hr_localfd < 0) { 87204076Spjd KEEP_ERRNO(pjdlog_errno(LOG_ERR, "Unable to open %s", 88204076Spjd res->hr_localpath)); 89204076Spjd return (-1); 90204076Spjd } 91204076Spjd } 92204076Spjd if (fstat(res->hr_localfd, &sb) < 0) { 93204076Spjd KEEP_ERRNO(pjdlog_errno(LOG_ERR, "Unable to stat %s", 94204076Spjd res->hr_localpath)); 95204076Spjd return (-1); 96204076Spjd } 97204076Spjd if (S_ISCHR(sb.st_mode)) { 98204076Spjd /* 99204076Spjd * If this is character device, it is most likely GEOM provider. 100204076Spjd */ 101204076Spjd if (ioctl(res->hr_localfd, DIOCGMEDIASIZE, 102204076Spjd &res->hr_local_mediasize) < 0) { 103204076Spjd KEEP_ERRNO(pjdlog_errno(LOG_ERR, 104204076Spjd "Unable obtain provider %s mediasize", 105204076Spjd res->hr_localpath)); 106204076Spjd return (-1); 107204076Spjd } 108204076Spjd if (ioctl(res->hr_localfd, DIOCGSECTORSIZE, 109204076Spjd &res->hr_local_sectorsize) < 0) { 110204076Spjd KEEP_ERRNO(pjdlog_errno(LOG_ERR, 111204076Spjd "Unable obtain provider %s sectorsize", 112204076Spjd res->hr_localpath)); 113204076Spjd return (-1); 114204076Spjd } 115204076Spjd } else if (S_ISREG(sb.st_mode)) { 116204076Spjd /* 117204076Spjd * We also support regular files for which we hardcode 118204076Spjd * sector size of 512 bytes. 119204076Spjd */ 120204076Spjd res->hr_local_mediasize = sb.st_size; 121204076Spjd res->hr_local_sectorsize = 512; 122204076Spjd } else { 123204076Spjd /* 124204076Spjd * We support no other file types. 125204076Spjd */ 126204076Spjd pjdlog_error("%s is neither GEOM provider nor regular file.", 127204076Spjd res->hr_localpath); 128204076Spjd errno = EFTYPE; 129204076Spjd return (-1); 130204076Spjd } 131204076Spjd return (0); 132204076Spjd} 133204076Spjd 134204076Spjdconst char * 135204076Spjdrole2str(int role) 136204076Spjd{ 137204076Spjd 138219864Spjd switch (role) { 139219864Spjd case HAST_ROLE_INIT: 140204076Spjd return ("init"); 141219864Spjd case HAST_ROLE_PRIMARY: 142204076Spjd return ("primary"); 143219864Spjd case HAST_ROLE_SECONDARY: 144204076Spjd return ("secondary"); 145204076Spjd } 146204076Spjd return ("unknown"); 147204076Spjd} 148218048Spjd 149218048Spjdint 150219847Spjddrop_privs(bool usecapsicum) 151218048Spjd{ 152218048Spjd struct passwd *pw; 153218048Spjd uid_t ruid, euid, suid; 154218048Spjd gid_t rgid, egid, sgid; 155218048Spjd gid_t gidset[1]; 156218048Spjd 157219847Spjd if (usecapsicum) { 158219847Spjd if (cap_enter() == 0) { 159219847Spjd pjdlog_debug(1, 160219847Spjd "Privileges successfully dropped using capsicum."); 161219847Spjd return (0); 162219847Spjd } 163219847Spjd pjdlog_errno(LOG_WARNING, "Unable to sandbox using capsicum"); 164219847Spjd } 165219847Spjd 166218048Spjd /* 167218048Spjd * According to getpwnam(3) we have to clear errno before calling the 168218048Spjd * function to be able to distinguish between an error and missing 169218048Spjd * entry (with is not treated as error by getpwnam(3)). 170218048Spjd */ 171218048Spjd errno = 0; 172218048Spjd pw = getpwnam(HAST_USER); 173218048Spjd if (pw == NULL) { 174218048Spjd if (errno != 0) { 175218048Spjd KEEP_ERRNO(pjdlog_errno(LOG_ERR, 176218048Spjd "Unable to find info about '%s' user", HAST_USER)); 177218048Spjd return (-1); 178218048Spjd } else { 179218048Spjd pjdlog_error("'%s' user doesn't exist.", HAST_USER); 180218048Spjd errno = ENOENT; 181218048Spjd return (-1); 182218048Spjd } 183218048Spjd } 184218048Spjd if (chroot(pw->pw_dir) == -1) { 185218048Spjd KEEP_ERRNO(pjdlog_errno(LOG_ERR, 186218048Spjd "Unable to change root directory to %s", pw->pw_dir)); 187218048Spjd return (-1); 188218048Spjd } 189218048Spjd PJDLOG_VERIFY(chdir("/") == 0); 190218048Spjd gidset[0] = pw->pw_gid; 191218048Spjd if (setgroups(1, gidset) == -1) { 192218048Spjd KEEP_ERRNO(pjdlog_errno(LOG_ERR, 193218048Spjd "Unable to set groups to gid %u", 194218048Spjd (unsigned int)pw->pw_gid)); 195218048Spjd return (-1); 196218048Spjd } 197218048Spjd if (setgid(pw->pw_gid) == -1) { 198218048Spjd KEEP_ERRNO(pjdlog_errno(LOG_ERR, "Unable to set gid to %u", 199218048Spjd (unsigned int)pw->pw_gid)); 200218048Spjd return (-1); 201218048Spjd } 202218048Spjd if (setuid(pw->pw_uid) == -1) { 203218048Spjd KEEP_ERRNO(pjdlog_errno(LOG_ERR, "Unable to set uid to %u", 204218048Spjd (unsigned int)pw->pw_uid)); 205218048Spjd return (-1); 206218048Spjd } 207218048Spjd 208218048Spjd /* 209218048Spjd * Better be sure that everything succeeded. 210218048Spjd */ 211218048Spjd PJDLOG_VERIFY(getresuid(&ruid, &euid, &suid) == 0); 212218048Spjd PJDLOG_VERIFY(ruid == pw->pw_uid); 213218048Spjd PJDLOG_VERIFY(euid == pw->pw_uid); 214218048Spjd PJDLOG_VERIFY(suid == pw->pw_uid); 215218048Spjd PJDLOG_VERIFY(getresgid(&rgid, &egid, &sgid) == 0); 216218048Spjd PJDLOG_VERIFY(rgid == pw->pw_gid); 217218048Spjd PJDLOG_VERIFY(egid == pw->pw_gid); 218218048Spjd PJDLOG_VERIFY(sgid == pw->pw_gid); 219218048Spjd PJDLOG_VERIFY(getgroups(0, NULL) == 1); 220218048Spjd PJDLOG_VERIFY(getgroups(1, gidset) == 1); 221218048Spjd PJDLOG_VERIFY(gidset[0] == pw->pw_gid); 222218048Spjd 223219847Spjd pjdlog_debug(1, 224219847Spjd "Privileges successfully dropped using chroot+setgid+setuid."); 225219847Spjd 226218048Spjd return (0); 227218048Spjd} 228