1 /* This file deals with protection in the file system. It contains the code 2 * for four system calls that relate to protection. 3 * 4 * The entry points into this file are 5 * do_chmod: perform the CHMOD and FCHMOD system calls 6 * do_chown: perform the CHOWN and FCHOWN system calls 7 * do_umask: perform the UMASK system call 8 * do_access: perform the ACCESS system call 9 */ 10 11 #include "fs.h" 12 #include <sys/stat.h> 13 #include <unistd.h> 14 #include <assert.h> 15 #include <minix/callnr.h> 16 #include "file.h" 17 #include "path.h" 18 #include <minix/vfsif.h> 19 #include "vnode.h" 20 #include "vmnt.h" 21 22 /*===========================================================================* 23 * do_chmod * 24 *===========================================================================*/ 25 int do_chmod(void) 26 { 27 /* Perform the chmod(name, mode) and fchmod(fd, mode) system calls. 28 * syscall might provide 'name' embedded in the message. 29 */ 30 31 struct filp *flp; 32 struct vnode *vp; 33 struct vmnt *vmp; 34 int r, rfd; 35 mode_t result_mode; 36 char fullpath[PATH_MAX]; 37 struct lookup resolve; 38 mode_t new_mode; 39 40 flp = NULL; 41 42 lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp, &vp); 43 resolve.l_vmnt_lock = VMNT_READ; 44 resolve.l_vnode_lock = VNODE_WRITE; 45 46 if (job_call_nr == VFS_CHMOD) { 47 new_mode = job_m_in.m_lc_vfs_path.mode; 48 /* Temporarily open the file */ 49 if (copy_path(fullpath, sizeof(fullpath)) != OK) 50 return(err_code); 51 if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code); 52 } else { /* call_nr == VFS_FCHMOD */ 53 rfd = job_m_in.m_lc_vfs_fchmod.fd; 54 new_mode = job_m_in.m_lc_vfs_fchmod.mode; 55 /* File is already opened; get a pointer to vnode from filp. */ 56 if ((flp = get_filp(rfd, VNODE_WRITE)) == NULL) return(err_code); 57 vp = flp->filp_vno; 58 assert(vp); 59 dup_vnode(vp); 60 } 61 62 assert(vp); 63 64 /* Only the owner or the super_user may change the mode of a file. 65 * No one may change the mode of a file on a read-only file system. 66 */ 67 if (vp->v_uid != fp->fp_effuid && fp->fp_effuid != SU_UID) 68 r = EPERM; 69 else 70 r = read_only(vp); 71 72 if (r == OK) { 73 /* Now make the change. Clear setgid bit if file is not in caller's 74 * group */ 75 if (fp->fp_effuid != SU_UID && vp->v_gid != fp->fp_effgid) 76 new_mode &= ~I_SET_GID_BIT; 77 78 r = req_chmod(vp->v_fs_e, vp->v_inode_nr, new_mode, &result_mode); 79 if (r == OK) 80 vp->v_mode = result_mode; 81 } 82 83 if (job_call_nr == VFS_CHMOD) { 84 unlock_vnode(vp); 85 unlock_vmnt(vmp); 86 } else { /* VFS_FCHMOD */ 87 unlock_filp(flp); 88 } 89 90 put_vnode(vp); 91 return(r); 92 } 93 94 95 /*===========================================================================* 96 * do_chown * 97 *===========================================================================*/ 98 int do_chown(void) 99 { 100 /* Perform the chown(path, owner, group) and fchmod(fd, owner, group) system 101 * calls. */ 102 struct filp *flp; 103 struct vnode *vp; 104 struct vmnt *vmp; 105 int r, rfd; 106 uid_t uid, new_uid; 107 gid_t gid, new_gid; 108 mode_t new_mode; 109 char fullpath[PATH_MAX]; 110 struct lookup resolve; 111 vir_bytes vname1; 112 size_t vname1_length; 113 114 flp = NULL; 115 uid = job_m_in.m_lc_vfs_chown.owner; 116 gid = job_m_in.m_lc_vfs_chown.group; 117 118 if (job_call_nr == VFS_CHOWN) { 119 vname1 = job_m_in.m_lc_vfs_chown.name; 120 vname1_length = job_m_in.m_lc_vfs_chown.len; 121 122 lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp, &vp); 123 resolve.l_vmnt_lock = VMNT_READ; 124 resolve.l_vnode_lock = VNODE_WRITE; 125 126 /* Temporarily open the file. */ 127 if (fetch_name(vname1, vname1_length, fullpath) != OK) 128 return(err_code); 129 if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code); 130 } else { /* call_nr == VFS_FCHOWN */ 131 rfd = job_m_in.m_lc_vfs_chown.fd; 132 133 /* File is already opened; get a pointer to the vnode from filp. */ 134 if ((flp = get_filp(rfd, VNODE_WRITE)) == NULL) 135 return(err_code); 136 vp = flp->filp_vno; 137 dup_vnode(vp); 138 } 139 140 r = read_only(vp); 141 if (r == OK) { 142 /* FS is R/W. Whether call is allowed depends on ownership, etc. */ 143 /* The super user can do anything, so check permissions only if we're 144 a regular user. */ 145 if (fp->fp_effuid != SU_UID) { 146 /* Regular users can only change groups of their own files. */ 147 if (vp->v_uid != fp->fp_effuid) r = EPERM; 148 if (vp->v_uid != uid) r = EPERM; /* no giving away */ 149 if (fp->fp_effgid != gid) r = EPERM; 150 } 151 } 152 153 if (r == OK) { 154 /* Do not change uid/gid if new uid/gid is -1. */ 155 new_uid = (uid == (uid_t)-1 ? vp->v_uid : uid); 156 new_gid = (gid == (gid_t)-1 ? vp->v_gid : gid); 157 158 if (new_uid > UID_MAX || new_gid > GID_MAX) 159 r = EINVAL; 160 else if ((r = req_chown(vp->v_fs_e, vp->v_inode_nr, new_uid, new_gid, 161 &new_mode)) == OK) { 162 vp->v_uid = new_uid; 163 vp->v_gid = new_gid; 164 vp->v_mode = new_mode; 165 } 166 } 167 168 if (job_call_nr == VFS_CHOWN) { 169 unlock_vnode(vp); 170 unlock_vmnt(vmp); 171 } else { /* VFS_FCHOWN */ 172 unlock_filp(flp); 173 } 174 175 put_vnode(vp); 176 return(r); 177 } 178 179 /*===========================================================================* 180 * do_umask * 181 *===========================================================================*/ 182 int do_umask(void) 183 { 184 /* Perform the umask(2) system call. */ 185 mode_t complement, new_umask; 186 187 new_umask = job_m_in.m_lc_vfs_umask.mask; 188 189 complement = ~fp->fp_umask; /* set 'r' to complement of old mask */ 190 fp->fp_umask = ~(new_umask & RWX_MODES); 191 return(complement); /* return complement of old mask */ 192 } 193 194 195 /*===========================================================================* 196 * do_access * 197 *===========================================================================*/ 198 int do_access(void) 199 { 200 /* Perform the access(name, mode) system call. 201 * syscall might provide 'name' embedded in the message. 202 */ 203 int r; 204 struct vnode *vp; 205 struct vmnt *vmp; 206 char fullpath[PATH_MAX]; 207 struct lookup resolve; 208 mode_t access; 209 210 access = job_m_in.m_lc_vfs_path.mode; 211 212 lookup_init(&resolve, fullpath, PATH_NOFLAGS, &vmp, &vp); 213 resolve.l_vmnt_lock = VMNT_READ; 214 resolve.l_vnode_lock = VNODE_READ; 215 216 /* First check to see if the mode is correct. */ 217 if ( (access & ~(R_OK | W_OK | X_OK)) != 0 && access != F_OK) 218 return(EINVAL); 219 220 /* Temporarily open the file. */ 221 if (copy_path(fullpath, sizeof(fullpath)) != OK) 222 return(err_code); 223 if ((vp = eat_path(&resolve, fp)) == NULL) return(err_code); 224 225 r = forbidden(fp, vp, access); 226 227 unlock_vnode(vp); 228 unlock_vmnt(vmp); 229 230 put_vnode(vp); 231 return(r); 232 } 233 234 235 /*===========================================================================* 236 * forbidden * 237 *===========================================================================*/ 238 int forbidden(struct fproc *rfp, struct vnode *vp, mode_t access_desired) 239 { 240 /* Given a pointer to an vnode, 'vp', and the access desired, determine 241 * if the access is allowed, and if not why not. The routine looks up the 242 * caller's uid in the 'fproc' table. If access is allowed, OK is returned 243 * if it is forbidden, EACCES is returned. 244 */ 245 246 register mode_t bits, perm_bits; 247 uid_t uid; 248 gid_t gid; 249 int r, shift; 250 251 if (vp->v_uid == (uid_t) -1 || vp->v_gid == (gid_t) -1) return(EACCES); 252 253 /* Isolate the relevant rwx bits from the mode. */ 254 bits = vp->v_mode; 255 uid = (job_call_nr == VFS_ACCESS ? rfp->fp_realuid : rfp->fp_effuid); 256 gid = (job_call_nr == VFS_ACCESS ? rfp->fp_realgid : rfp->fp_effgid); 257 258 if (uid == SU_UID) { 259 /* Grant read and write permission. Grant search permission for 260 * directories. Grant execute permission (for non-directories) if 261 * and only if one of the 'X' bits is set. 262 */ 263 if ( S_ISDIR(bits) || bits & ((X_BIT << 6) | (X_BIT << 3) | X_BIT)) 264 perm_bits = R_BIT | W_BIT | X_BIT; 265 else 266 perm_bits = R_BIT | W_BIT; 267 } else { 268 if (uid == vp->v_uid) shift = 6; /* owner */ 269 else if (gid == vp->v_gid) shift = 3; /* group */ 270 else if (in_group(fp, vp->v_gid) == OK) shift = 3; /* suppl. groups */ 271 else shift = 0; /* other */ 272 perm_bits = (bits >> shift) & (R_BIT | W_BIT | X_BIT); 273 } 274 275 /* If access desired is not a subset of what is allowed, it is refused. */ 276 r = OK; 277 if ((perm_bits | access_desired) != perm_bits) r = EACCES; 278 279 /* Check to see if someone is trying to write on a file system that is 280 * mounted read-only. 281 */ 282 if (r == OK) 283 if (access_desired & W_BIT) 284 r = read_only(vp); 285 286 return(r); 287 } 288 289 /*===========================================================================* 290 * read_only * 291 *===========================================================================*/ 292 int read_only(vp) 293 struct vnode *vp; /* ptr to inode whose file sys is to be cked */ 294 { 295 /* Check to see if the file system on which the inode 'ip' resides is mounted 296 * read only. If so, return EROFS, else return OK. 297 */ 298 assert(vp); 299 return(vp->v_vmnt && (vp->v_vmnt->m_flags & VMNT_READONLY) ? EROFS : OK); 300 } 301