1 /*- 2 * Copyright (c) 2010 The FreeBSD Foundation 3 * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net> 4 * All rights reserved. 5 * 6 * This software was developed by Pawel Jakub Dawidek under sponsorship from 7 * the FreeBSD Foundation. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 #include <sys/cdefs.h> 32 __FBSDID("$FreeBSD$"); 33 34 #include <sys/capability.h> 35 #include <sys/types.h> 36 #include <sys/disk.h> 37 #include <sys/ioctl.h> 38 #include <sys/stat.h> 39 40 #include <errno.h> 41 #include <fcntl.h> 42 #include <pwd.h> 43 #include <stdarg.h> 44 #include <stdbool.h> 45 #include <stdio.h> 46 #include <string.h> 47 #include <unistd.h> 48 49 #include <pjdlog.h> 50 51 #include "hast.h" 52 #include "subr.h" 53 54 int 55 vsnprlcat(char *str, size_t size, const char *fmt, va_list ap) 56 { 57 size_t len; 58 59 len = strlen(str); 60 return (vsnprintf(str + len, size - len, fmt, ap)); 61 } 62 63 int 64 snprlcat(char *str, size_t size, const char *fmt, ...) 65 { 66 va_list ap; 67 int result; 68 69 va_start(ap, fmt); 70 result = vsnprlcat(str, size, fmt, ap); 71 va_end(ap); 72 return (result); 73 } 74 75 int 76 provinfo(struct hast_resource *res, bool dowrite) 77 { 78 struct stat sb; 79 80 PJDLOG_ASSERT(res->hr_localpath != NULL && 81 res->hr_localpath[0] != '\0'); 82 83 if (res->hr_localfd == -1) { 84 res->hr_localfd = open(res->hr_localpath, 85 dowrite ? O_RDWR : O_RDONLY); 86 if (res->hr_localfd < 0) { 87 KEEP_ERRNO(pjdlog_errno(LOG_ERR, "Unable to open %s", 88 res->hr_localpath)); 89 return (-1); 90 } 91 } 92 if (fstat(res->hr_localfd, &sb) < 0) { 93 KEEP_ERRNO(pjdlog_errno(LOG_ERR, "Unable to stat %s", 94 res->hr_localpath)); 95 return (-1); 96 } 97 if (S_ISCHR(sb.st_mode)) { 98 /* 99 * If this is character device, it is most likely GEOM provider. 100 */ 101 if (ioctl(res->hr_localfd, DIOCGMEDIASIZE, 102 &res->hr_local_mediasize) < 0) { 103 KEEP_ERRNO(pjdlog_errno(LOG_ERR, 104 "Unable obtain provider %s mediasize", 105 res->hr_localpath)); 106 return (-1); 107 } 108 if (ioctl(res->hr_localfd, DIOCGSECTORSIZE, 109 &res->hr_local_sectorsize) < 0) { 110 KEEP_ERRNO(pjdlog_errno(LOG_ERR, 111 "Unable obtain provider %s sectorsize", 112 res->hr_localpath)); 113 return (-1); 114 } 115 } else if (S_ISREG(sb.st_mode)) { 116 /* 117 * We also support regular files for which we hardcode 118 * sector size of 512 bytes. 119 */ 120 res->hr_local_mediasize = sb.st_size; 121 res->hr_local_sectorsize = 512; 122 } else { 123 /* 124 * We support no other file types. 125 */ 126 pjdlog_error("%s is neither GEOM provider nor regular file.", 127 res->hr_localpath); 128 errno = EFTYPE; 129 return (-1); 130 } 131 return (0); 132 } 133 134 const char * 135 role2str(int role) 136 { 137 138 switch (role) { 139 case HAST_ROLE_INIT: 140 return ("init"); 141 case HAST_ROLE_PRIMARY: 142 return ("primary"); 143 case HAST_ROLE_SECONDARY: 144 return ("secondary"); 145 } 146 return ("unknown"); 147 } 148 149 int 150 drop_privs(bool usecapsicum) 151 { 152 struct passwd *pw; 153 uid_t ruid, euid, suid; 154 gid_t rgid, egid, sgid; 155 gid_t gidset[1]; 156 157 if (usecapsicum) { 158 if (cap_enter() == 0) { 159 pjdlog_debug(1, 160 "Privileges successfully dropped using capsicum."); 161 return (0); 162 } 163 pjdlog_errno(LOG_WARNING, "Unable to sandbox using capsicum"); 164 } 165 166 /* 167 * According to getpwnam(3) we have to clear errno before calling the 168 * function to be able to distinguish between an error and missing 169 * entry (with is not treated as error by getpwnam(3)). 170 */ 171 errno = 0; 172 pw = getpwnam(HAST_USER); 173 if (pw == NULL) { 174 if (errno != 0) { 175 KEEP_ERRNO(pjdlog_errno(LOG_ERR, 176 "Unable to find info about '%s' user", HAST_USER)); 177 return (-1); 178 } else { 179 pjdlog_error("'%s' user doesn't exist.", HAST_USER); 180 errno = ENOENT; 181 return (-1); 182 } 183 } 184 if (chroot(pw->pw_dir) == -1) { 185 KEEP_ERRNO(pjdlog_errno(LOG_ERR, 186 "Unable to change root directory to %s", pw->pw_dir)); 187 return (-1); 188 } 189 PJDLOG_VERIFY(chdir("/") == 0); 190 gidset[0] = pw->pw_gid; 191 if (setgroups(1, gidset) == -1) { 192 KEEP_ERRNO(pjdlog_errno(LOG_ERR, 193 "Unable to set groups to gid %u", 194 (unsigned int)pw->pw_gid)); 195 return (-1); 196 } 197 if (setgid(pw->pw_gid) == -1) { 198 KEEP_ERRNO(pjdlog_errno(LOG_ERR, "Unable to set gid to %u", 199 (unsigned int)pw->pw_gid)); 200 return (-1); 201 } 202 if (setuid(pw->pw_uid) == -1) { 203 KEEP_ERRNO(pjdlog_errno(LOG_ERR, "Unable to set uid to %u", 204 (unsigned int)pw->pw_uid)); 205 return (-1); 206 } 207 208 /* 209 * Better be sure that everything succeeded. 210 */ 211 PJDLOG_VERIFY(getresuid(&ruid, &euid, &suid) == 0); 212 PJDLOG_VERIFY(ruid == pw->pw_uid); 213 PJDLOG_VERIFY(euid == pw->pw_uid); 214 PJDLOG_VERIFY(suid == pw->pw_uid); 215 PJDLOG_VERIFY(getresgid(&rgid, &egid, &sgid) == 0); 216 PJDLOG_VERIFY(rgid == pw->pw_gid); 217 PJDLOG_VERIFY(egid == pw->pw_gid); 218 PJDLOG_VERIFY(sgid == pw->pw_gid); 219 PJDLOG_VERIFY(getgroups(0, NULL) == 1); 220 PJDLOG_VERIFY(getgroups(1, gidset) == 1); 221 PJDLOG_VERIFY(gidset[0] == pw->pw_gid); 222 223 pjdlog_debug(1, 224 "Privileges successfully dropped using chroot+setgid+setuid."); 225 226 return (0); 227 } 228