xref: /minix/minix/servers/vfs/protect.c (revision fb9c64b2)
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
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