subr.c revision 222224
1167891Syar/*- 2167891Syar * Copyright (c) 2010 The FreeBSD Foundation 3167891Syar * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net> 4167891Syar * All rights reserved. 5167891Syar * 6167891Syar * This software was developed by Pawel Jakub Dawidek under sponsorship from 7167891Syar * the FreeBSD Foundation. 8167891Syar * 9167891Syar * Redistribution and use in source and binary forms, with or without 10167891Syar * modification, are permitted provided that the following conditions 11167891Syar * are met: 12167891Syar * 1. Redistributions of source code must retain the above copyright 13167891Syar * notice, this list of conditions and the following disclaimer. 14167891Syar * 2. Redistributions in binary form must reproduce the above copyright 15167891Syar * notice, this list of conditions and the following disclaimer in the 16167891Syar * documentation and/or other materials provided with the distribution. 17167891Syar * 18167891Syar * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 19167891Syar * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20167891Syar * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21167891Syar * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 22167891Syar * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23167891Syar * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24167891Syar * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25167891Syar * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26167891Syar * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27167891Syar * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28167891Syar * SUCH DAMAGE. 29167891Syar */ 30167891Syar 31167891Syar#include <sys/cdefs.h> 32167891Syar__FBSDID("$FreeBSD: head/sbin/hastd/subr.c 222224 2011-05-23 20:59:50Z pjd $"); 33167891Syar 34167891Syar#include <sys/capability.h> 35167891Syar#include <sys/param.h> 36167891Syar#include <sys/disk.h> 37167891Syar#include <sys/ioctl.h> 38167891Syar#include <sys/jail.h> 39167891Syar#include <sys/stat.h> 40167891Syar 41167891Syar#include <errno.h> 42167891Syar#include <fcntl.h> 43167891Syar#include <pwd.h> 44167891Syar#include <stdarg.h> 45167891Syar#include <stdbool.h> 46167891Syar#include <stdio.h> 47167891Syar#include <string.h> 48167891Syar#include <unistd.h> 49167891Syar 50167891Syar#include <pjdlog.h> 51167891Syar 52167891Syar#include "hast.h" 53167891Syar#include "subr.h" 54167891Syar 55167891Syarint 56167891Syarvsnprlcat(char *str, size_t size, const char *fmt, va_list ap) 57167891Syar{ 58167891Syar size_t len; 59167891Syar 60167891Syar len = strlen(str); 61167891Syar return (vsnprintf(str + len, size - len, fmt, ap)); 62167891Syar} 63167891Syar 64167891Syarint 65167891Syarsnprlcat(char *str, size_t size, const char *fmt, ...) 66167891Syar{ 67167891Syar va_list ap; 68167891Syar int result; 69167891Syar 70167891Syar va_start(ap, fmt); 71167891Syar result = vsnprlcat(str, size, fmt, ap); 72167891Syar va_end(ap); 73167891Syar return (result); 74167891Syar} 75167891Syar 76167891Syarint 77167891Syarprovinfo(struct hast_resource *res, bool dowrite) 78167891Syar{ 79167891Syar struct stat sb; 80167891Syar 81167891Syar PJDLOG_ASSERT(res->hr_localpath != NULL && 82167891Syar res->hr_localpath[0] != '\0'); 83167891Syar 84167891Syar if (res->hr_localfd == -1) { 85167891Syar res->hr_localfd = open(res->hr_localpath, 86167891Syar dowrite ? O_RDWR : O_RDONLY); 87167891Syar if (res->hr_localfd < 0) { 88167891Syar KEEP_ERRNO(pjdlog_errno(LOG_ERR, "Unable to open %s", 89167891Syar res->hr_localpath)); 90167891Syar return (-1); 91175084Sgabor } 92167891Syar } 93167891Syar if (fstat(res->hr_localfd, &sb) < 0) { 94167891Syar KEEP_ERRNO(pjdlog_errno(LOG_ERR, "Unable to stat %s", 95167891Syar res->hr_localpath)); 96167891Syar return (-1); 97167891Syar } 98167891Syar if (S_ISCHR(sb.st_mode)) { 99167891Syar /* 100167891Syar * If this is character device, it is most likely GEOM provider. 101167891Syar */ 102167891Syar if (ioctl(res->hr_localfd, DIOCGMEDIASIZE, 103167891Syar &res->hr_local_mediasize) < 0) { 104167891Syar KEEP_ERRNO(pjdlog_errno(LOG_ERR, 105167891Syar "Unable obtain provider %s mediasize", 106167891Syar res->hr_localpath)); 107167891Syar return (-1); 108167891Syar } 109167891Syar if (ioctl(res->hr_localfd, DIOCGSECTORSIZE, 110167891Syar &res->hr_local_sectorsize) < 0) { 111167891Syar KEEP_ERRNO(pjdlog_errno(LOG_ERR, 112 "Unable obtain provider %s sectorsize", 113 res->hr_localpath)); 114 return (-1); 115 } 116 } else if (S_ISREG(sb.st_mode)) { 117 /* 118 * We also support regular files for which we hardcode 119 * sector size of 512 bytes. 120 */ 121 res->hr_local_mediasize = sb.st_size; 122 res->hr_local_sectorsize = 512; 123 } else { 124 /* 125 * We support no other file types. 126 */ 127 pjdlog_error("%s is neither GEOM provider nor regular file.", 128 res->hr_localpath); 129 errno = EFTYPE; 130 return (-1); 131 } 132 return (0); 133} 134 135const char * 136role2str(int role) 137{ 138 139 switch (role) { 140 case HAST_ROLE_INIT: 141 return ("init"); 142 case HAST_ROLE_PRIMARY: 143 return ("primary"); 144 case HAST_ROLE_SECONDARY: 145 return ("secondary"); 146 } 147 return ("unknown"); 148} 149 150int 151drop_privs(struct hast_resource *res) 152{ 153 char jailhost[sizeof(res->hr_name) * 2]; 154 struct jail jailst; 155 struct passwd *pw; 156 uid_t ruid, euid, suid; 157 gid_t rgid, egid, sgid; 158 gid_t gidset[1]; 159 bool capsicum, jailed; 160 161 /* 162 * According to getpwnam(3) we have to clear errno before calling the 163 * function to be able to distinguish between an error and missing 164 * entry (with is not treated as error by getpwnam(3)). 165 */ 166 errno = 0; 167 pw = getpwnam(HAST_USER); 168 if (pw == NULL) { 169 if (errno != 0) { 170 KEEP_ERRNO(pjdlog_errno(LOG_ERR, 171 "Unable to find info about '%s' user", HAST_USER)); 172 return (-1); 173 } else { 174 pjdlog_error("'%s' user doesn't exist.", HAST_USER); 175 errno = ENOENT; 176 return (-1); 177 } 178 } 179 180 bzero(&jailst, sizeof(jailst)); 181 jailst.version = JAIL_API_VERSION; 182 jailst.path = pw->pw_dir; 183 if (res == NULL) { 184 (void)snprintf(jailhost, sizeof(jailhost), "hastctl"); 185 } else { 186 (void)snprintf(jailhost, sizeof(jailhost), "hastd: %s (%s)", 187 res->hr_name, role2str(res->hr_role)); 188 } 189 jailst.hostname = jailhost; 190 jailst.jailname = NULL; 191 jailst.ip4s = 0; 192 jailst.ip4 = NULL; 193 jailst.ip6s = 0; 194 jailst.ip6 = NULL; 195 if (jail(&jailst) >= 0) { 196 jailed = true; 197 } else { 198 jailed = false; 199 pjdlog_errno(LOG_WARNING, 200 "Unable to jail to directory to %s", pw->pw_dir); 201 if (chroot(pw->pw_dir) == -1) { 202 KEEP_ERRNO(pjdlog_errno(LOG_ERR, 203 "Unable to change root directory to %s", 204 pw->pw_dir)); 205 return (-1); 206 } 207 } 208 PJDLOG_VERIFY(chdir("/") == 0); 209 gidset[0] = pw->pw_gid; 210 if (setgroups(1, gidset) == -1) { 211 KEEP_ERRNO(pjdlog_errno(LOG_ERR, 212 "Unable to set groups to gid %u", 213 (unsigned int)pw->pw_gid)); 214 return (-1); 215 } 216 if (setgid(pw->pw_gid) == -1) { 217 KEEP_ERRNO(pjdlog_errno(LOG_ERR, "Unable to set gid to %u", 218 (unsigned int)pw->pw_gid)); 219 return (-1); 220 } 221 if (setuid(pw->pw_uid) == -1) { 222 KEEP_ERRNO(pjdlog_errno(LOG_ERR, "Unable to set uid to %u", 223 (unsigned int)pw->pw_uid)); 224 return (-1); 225 } 226 227 /* 228 * Until capsicum doesn't allow ioctl(2) we cannot use it to sandbox 229 * primary and secondary worker processes, as primary uses GGATE 230 * ioctls and secondary uses ioctls to handle BIO_DELETE and BIO_FLUSH. 231 * For now capsicum is only used to sandbox hastctl. 232 */ 233 if (res == NULL) 234 capsicum = (cap_enter() == 0); 235 else 236 capsicum = false; 237 238 /* 239 * Better be sure that everything succeeded. 240 */ 241 PJDLOG_VERIFY(getresuid(&ruid, &euid, &suid) == 0); 242 PJDLOG_VERIFY(ruid == pw->pw_uid); 243 PJDLOG_VERIFY(euid == pw->pw_uid); 244 PJDLOG_VERIFY(suid == pw->pw_uid); 245 PJDLOG_VERIFY(getresgid(&rgid, &egid, &sgid) == 0); 246 PJDLOG_VERIFY(rgid == pw->pw_gid); 247 PJDLOG_VERIFY(egid == pw->pw_gid); 248 PJDLOG_VERIFY(sgid == pw->pw_gid); 249 PJDLOG_VERIFY(getgroups(0, NULL) == 1); 250 PJDLOG_VERIFY(getgroups(1, gidset) == 1); 251 PJDLOG_VERIFY(gidset[0] == pw->pw_gid); 252 253 pjdlog_debug(1, 254 "Privileges successfully dropped using %s%s+setgid+setuid.", 255 capsicum ? "capsicum+" : "", jailed ? "jail" : "chroot"); 256 257 return (0); 258} 259