1 /*- 2 * Copyright (c) 2006 nCircle Network Security, Inc. 3 * All rights reserved. 4 * 5 * This software was developed by Robert N. M. Watson for the TrustedBSD 6 * Project under contract to nCircle Network Security, Inc. 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 AUTHOR 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 AUTHOR, NCIRCLE NETWORK SECURITY, 21 * INC., OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 23 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 * 29 * $FreeBSD$ 30 */ 31 32 /* 33 * Confirm that privilege is required in the cases using chown(): 34 * 35 * - If the process euid does not match the file uid. 36 * 37 * - If the target uid is different than the current uid. 38 * 39 * - If the target gid changes and we the process is not a member of the new 40 * group. 41 */ 42 43 #include <sys/types.h> 44 #include <sys/stat.h> 45 46 #include <err.h> 47 #include <errno.h> 48 #include <stdio.h> 49 #include <unistd.h> 50 51 #include "main.h" 52 53 const gid_t gidset[] = {GID_WHEEL, GID_OWNER}; 54 55 void 56 priv_vfs_chown(void) 57 { 58 char fpath[1024]; 59 int error; 60 61 assert_root(); 62 63 /* 64 * Before beginning, set up group set for process. Place in wheel 65 * and owner groups; don't put in other group so that when we chown 66 * to the other group, it's as a non-member. 67 */ 68 if (setgroups(2, gidset) < 0) 69 err(-1, "setgroups(2, {%d, %d})", GID_WHEEL, GID_OWNER); 70 71 /* 72 * In the first pass, confirm that all works as desired with 73 * privilege. 74 * 75 * Check that chown when non-owner works fine. Do a no-op change to 76 * avoid other permission checks. Note that we can't request 77 * (-1, -1) and get an access control check, we have to request 78 * specific uid/gid that are not the same. 79 */ 80 setup_file(fpath, UID_OWNER, GID_OWNER, 0600); 81 if (chown(fpath, -1, GID_OWNER) < 0) { 82 warn("chown(%s, -1, %d) as root", fpath, GID_OWNER); 83 goto out; 84 } 85 (void)unlink(fpath); 86 87 /* 88 * Check that chown changing uid works with privilege. 89 */ 90 setup_file(fpath, UID_OWNER, GID_OWNER, 0600); 91 if (chown(fpath, UID_OTHER, -1) < 0) { 92 warn("chown(%s, %d, -1) as root", fpath, UID_OTHER); 93 goto out; 94 } 95 (void)unlink(fpath); 96 97 /* 98 * Check that can change the file group to one we are not a member of 99 * when running with privilege. 100 */ 101 setup_file(fpath, UID_OWNER, GID_OWNER, 0600); 102 if (chown(fpath, -1, GID_OTHER) < 0) { 103 warn("chown(%s, -1, %d) as root", fpath, GID_OTHER); 104 goto out; 105 } 106 (void)unlink(fpath); 107 108 /* 109 * Now, the same again, but without privilege. 110 * 111 * Confirm that we can't chown a file we don't own, even as a no-op. 112 */ 113 setup_file(fpath, UID_OWNER, GID_OWNER, 0600); 114 set_euid(UID_OTHER); 115 error = chown(fpath, -1, GID_OWNER); 116 if (error == 0) { 117 warnx("chown(%s, -1, %d) succeeded as !root, non-owner", 118 fpath, GID_OWNER); 119 goto out; 120 } 121 if (errno != EPERM) { 122 warn("chown(%s, -1, %d) wrong errno %d as !root, non-owner", 123 fpath, GID_OWNER, errno); 124 goto out; 125 } 126 set_euid(UID_ROOT); 127 (void)unlink(fpath); 128 129 /* 130 * Check that we can't change the uid of the file without privilege, 131 * even though we own the file. 132 */ 133 setup_file(fpath, UID_OWNER, GID_OWNER, 0600); 134 set_euid(UID_OWNER); 135 error = chown(fpath, UID_OTHER, -1); 136 if (error == 0) { 137 warnx("chown(%s, %d, -1) succeeded as !root", fpath, 138 UID_OTHER); 139 goto out; 140 } 141 if (errno != EPERM) { 142 warn("chown(%s, %d, -1) wrong errno %d as !root", fpath, 143 UID_OTHER, errno); 144 goto out; 145 } 146 set_euid(UID_ROOT); 147 (void)unlink(fpath); 148 149 /* 150 * Check that can't change the file group to one we are not a member 151 * of when running without privilege. 152 */ 153 setup_file(fpath, UID_OWNER, GID_OWNER, 0600); 154 set_euid(UID_OWNER); 155 error = chown(fpath, -1, GID_OTHER); 156 if (error == 0) { 157 warn("chown(%s, -1, %d) succeeded as !root", fpath, GID_OTHER); 158 goto out; 159 } 160 if (errno != EPERM) { 161 warn("chown(%s, -1, %d) wrong errno %d as !root", fpath, 162 errno, GID_OTHER); 163 goto out; 164 } 165 set_euid(UID_ROOT); 166 (void)unlink(fpath); 167 out: 168 (void)seteuid(UID_ROOT); 169 (void)unlink(fpath); 170 } 171