subr.c revision 219847
1/*- 2 * Copyright (c) 2010 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 <sys/cdefs.h> 31__FBSDID("$FreeBSD: head/sbin/hastd/subr.c 219847 2011-03-21 21:31:50Z pjd $"); 32 33#include <sys/capability.h> 34#include <sys/types.h> 35#include <sys/disk.h> 36#include <sys/ioctl.h> 37#include <sys/stat.h> 38 39#include <errno.h> 40#include <fcntl.h> 41#include <pwd.h> 42#include <stdarg.h> 43#include <stdbool.h> 44#include <stdio.h> 45#include <string.h> 46#include <unistd.h> 47 48#include <pjdlog.h> 49 50#include "hast.h" 51#include "subr.h" 52 53int 54vsnprlcat(char *str, size_t size, const char *fmt, va_list ap) 55{ 56 size_t len; 57 58 len = strlen(str); 59 return (vsnprintf(str + len, size - len, fmt, ap)); 60} 61 62int 63snprlcat(char *str, size_t size, const char *fmt, ...) 64{ 65 va_list ap; 66 int result; 67 68 va_start(ap, fmt); 69 result = vsnprlcat(str, size, fmt, ap); 70 va_end(ap); 71 return (result); 72} 73 74int 75provinfo(struct hast_resource *res, bool dowrite) 76{ 77 struct stat sb; 78 79 PJDLOG_ASSERT(res->hr_localpath != NULL && 80 res->hr_localpath[0] != '\0'); 81 82 if (res->hr_localfd == -1) { 83 res->hr_localfd = open(res->hr_localpath, 84 dowrite ? O_RDWR : O_RDONLY); 85 if (res->hr_localfd < 0) { 86 KEEP_ERRNO(pjdlog_errno(LOG_ERR, "Unable to open %s", 87 res->hr_localpath)); 88 return (-1); 89 } 90 } 91 if (fstat(res->hr_localfd, &sb) < 0) { 92 KEEP_ERRNO(pjdlog_errno(LOG_ERR, "Unable to stat %s", 93 res->hr_localpath)); 94 return (-1); 95 } 96 if (S_ISCHR(sb.st_mode)) { 97 /* 98 * If this is character device, it is most likely GEOM provider. 99 */ 100 if (ioctl(res->hr_localfd, DIOCGMEDIASIZE, 101 &res->hr_local_mediasize) < 0) { 102 KEEP_ERRNO(pjdlog_errno(LOG_ERR, 103 "Unable obtain provider %s mediasize", 104 res->hr_localpath)); 105 return (-1); 106 } 107 if (ioctl(res->hr_localfd, DIOCGSECTORSIZE, 108 &res->hr_local_sectorsize) < 0) { 109 KEEP_ERRNO(pjdlog_errno(LOG_ERR, 110 "Unable obtain provider %s sectorsize", 111 res->hr_localpath)); 112 return (-1); 113 } 114 } else if (S_ISREG(sb.st_mode)) { 115 /* 116 * We also support regular files for which we hardcode 117 * sector size of 512 bytes. 118 */ 119 res->hr_local_mediasize = sb.st_size; 120 res->hr_local_sectorsize = 512; 121 } else { 122 /* 123 * We support no other file types. 124 */ 125 pjdlog_error("%s is neither GEOM provider nor regular file.", 126 res->hr_localpath); 127 errno = EFTYPE; 128 return (-1); 129 } 130 return (0); 131} 132 133const char * 134role2str(int role) 135{ 136 137 switch (role) { 138 case HAST_ROLE_INIT: 139 return ("init"); 140 case HAST_ROLE_PRIMARY: 141 return ("primary"); 142 case HAST_ROLE_SECONDARY: 143 return ("secondary"); 144 } 145 return ("unknown"); 146} 147 148int 149drop_privs(bool usecapsicum) 150{ 151 struct passwd *pw; 152 uid_t ruid, euid, suid; 153 gid_t rgid, egid, sgid; 154 gid_t gidset[1]; 155 156 if (usecapsicum) { 157 if (cap_enter() == 0) { 158 pjdlog_debug(1, 159 "Privileges successfully dropped using capsicum."); 160 return (0); 161 } 162 pjdlog_errno(LOG_WARNING, "Unable to sandbox using capsicum"); 163 } 164 165 /* 166 * According to getpwnam(3) we have to clear errno before calling the 167 * function to be able to distinguish between an error and missing 168 * entry (with is not treated as error by getpwnam(3)). 169 */ 170 errno = 0; 171 pw = getpwnam(HAST_USER); 172 if (pw == NULL) { 173 if (errno != 0) { 174 KEEP_ERRNO(pjdlog_errno(LOG_ERR, 175 "Unable to find info about '%s' user", HAST_USER)); 176 return (-1); 177 } else { 178 pjdlog_error("'%s' user doesn't exist.", HAST_USER); 179 errno = ENOENT; 180 return (-1); 181 } 182 } 183 if (chroot(pw->pw_dir) == -1) { 184 KEEP_ERRNO(pjdlog_errno(LOG_ERR, 185 "Unable to change root directory to %s", pw->pw_dir)); 186 return (-1); 187 } 188 PJDLOG_VERIFY(chdir("/") == 0); 189 gidset[0] = pw->pw_gid; 190 if (setgroups(1, gidset) == -1) { 191 KEEP_ERRNO(pjdlog_errno(LOG_ERR, 192 "Unable to set groups to gid %u", 193 (unsigned int)pw->pw_gid)); 194 return (-1); 195 } 196 if (setgid(pw->pw_gid) == -1) { 197 KEEP_ERRNO(pjdlog_errno(LOG_ERR, "Unable to set gid to %u", 198 (unsigned int)pw->pw_gid)); 199 return (-1); 200 } 201 if (setuid(pw->pw_uid) == -1) { 202 KEEP_ERRNO(pjdlog_errno(LOG_ERR, "Unable to set uid to %u", 203 (unsigned int)pw->pw_uid)); 204 return (-1); 205 } 206 207 /* 208 * Better be sure that everything succeeded. 209 */ 210 PJDLOG_VERIFY(getresuid(&ruid, &euid, &suid) == 0); 211 PJDLOG_VERIFY(ruid == pw->pw_uid); 212 PJDLOG_VERIFY(euid == pw->pw_uid); 213 PJDLOG_VERIFY(suid == pw->pw_uid); 214 PJDLOG_VERIFY(getresgid(&rgid, &egid, &sgid) == 0); 215 PJDLOG_VERIFY(rgid == pw->pw_gid); 216 PJDLOG_VERIFY(egid == pw->pw_gid); 217 PJDLOG_VERIFY(sgid == pw->pw_gid); 218 PJDLOG_VERIFY(getgroups(0, NULL) == 1); 219 PJDLOG_VERIFY(getgroups(1, gidset) == 1); 220 PJDLOG_VERIFY(gidset[0] == pw->pw_gid); 221 222 pjdlog_debug(1, 223 "Privileges successfully dropped using chroot+setgid+setuid."); 224 225 return (0); 226} 227