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 *===========================================================================*/
do_chmod(void)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 *===========================================================================*/
do_chown(void)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 *===========================================================================*/
do_umask(void)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 *===========================================================================*/
do_access(void)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 *===========================================================================*/
forbidden(struct fproc * rfp,struct vnode * vp,mode_t access_desired)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(struct vnode * vp)293 read_only(
294 struct vnode *vp /* ptr to inode whose file sys is to be cked */
295 )
296 {
297 /* Check to see if the file system on which the inode 'ip' resides is mounted
298 * read only. If so, return EROFS, else return OK.
299 */
300 assert(vp);
301 return(vp->v_vmnt && (vp->v_vmnt->m_flags & VMNT_READONLY) ? EROFS : OK);
302 }
303