Commit | Line | Data |
---|---|---|
805e021f CE |
1 | /* |
2 | * Copyright 2000, International Business Machines Corporation and others. | |
3 | * All Rights Reserved. | |
4 | * | |
5 | * This software has been released under the terms of the IBM Public | |
6 | * License. For details, see the LICENSE file in the top-level source | |
7 | * directory or online at http://www.openafs.org/dl/license10.html | |
8 | */ | |
9 | ||
10 | /* | |
11 | * afs_vnop_access.c - access vop ccess mode bit support for vnode operations. | |
12 | * | |
13 | * Implements: | |
14 | * afs_GetAccessBits | |
15 | * afs_AccessOK | |
16 | * afs_access | |
17 | * | |
18 | * Local: | |
19 | * fileModeMap (table) | |
20 | */ | |
21 | ||
22 | #include <afsconfig.h> | |
23 | #include "afs/param.h" | |
24 | ||
25 | ||
26 | #include "afs/sysincludes.h" /* Standard vendor system headers */ | |
27 | #include "afsincludes.h" /* Afs-based standard headers */ | |
28 | #include "afs/afs_stats.h" /* statistics */ | |
29 | #include "afs/afs_cbqueue.h" | |
30 | #include "afs/nfsclient.h" | |
31 | #include "afs/afs_osidnlc.h" | |
32 | ||
33 | #ifndef ANONYMOUSID | |
34 | #define ANONYMOUSID 32766 /* make sure this is same as in ptserver.h */ | |
35 | #endif | |
36 | ||
37 | ||
38 | ||
39 | ||
40 | /* access bits to turn off for various owner Unix mode values */ | |
41 | static char fileModeMap[8] = { | |
42 | PRSFS_READ | PRSFS_WRITE, | |
43 | PRSFS_READ | PRSFS_WRITE, | |
44 | PRSFS_READ, | |
45 | PRSFS_READ, | |
46 | PRSFS_WRITE, | |
47 | PRSFS_WRITE, | |
48 | 0, | |
49 | 0 | |
50 | }; | |
51 | ||
52 | /* avc must be held. Returns bit map of mode bits. Ignores file mode bits */ | |
53 | afs_int32 | |
54 | afs_GetAccessBits(struct vcache *avc, afs_int32 arights, | |
55 | struct vrequest *areq) | |
56 | { | |
57 | AFS_STATCNT(afs_GetAccessBits); | |
58 | /* see if anyuser has the required access bits */ | |
59 | if ((arights & avc->f.anyAccess) == arights) { | |
60 | return arights; | |
61 | } | |
62 | ||
63 | /* look in per-pag cache */ | |
64 | if (avc->Access) { /* not beautiful, but Sun's cc will tolerate it */ | |
65 | struct axscache *ac; | |
66 | ||
67 | ac = afs_FindAxs(avc->Access, areq->uid); | |
68 | if (ac) { | |
69 | return (arights & ac->axess); | |
70 | } | |
71 | } | |
72 | ||
73 | if (!(avc->f.states & CForeign)) { | |
74 | /* If there aren't any bits cached for this user (but the vnode | |
75 | * _is_ cached, obviously), make sure this user has valid tokens | |
76 | * before bothering with the RPC. */ | |
77 | struct unixuser *tu; | |
78 | tu = afs_FindUser(areq->uid, avc->f.fid.Cell, READ_LOCK); | |
79 | if (!tu) { | |
80 | return (arights & avc->f.anyAccess); | |
81 | } | |
82 | if (!(tu->states & UHasTokens) || (tu->states & UTokensBad)) { | |
83 | afs_PutUser(tu, READ_LOCK); | |
84 | return (arights & avc->f.anyAccess); | |
85 | } else { | |
86 | afs_PutUser(tu, READ_LOCK); | |
87 | } | |
88 | } | |
89 | ||
90 | if (AFS_IS_DISCONNECTED && !AFS_IN_SYNC) { | |
91 | /* If we get this far, we have to ask the network. But we can't, so | |
92 | * they're out of luck... */ | |
93 | return 0; | |
94 | } else | |
95 | { /* Ok, user has valid tokens, go ask the server. */ | |
96 | struct AFSFetchStatus OutStatus; | |
97 | afs_int32 code; | |
98 | ||
99 | code = afs_FetchStatus(avc, &avc->f.fid, areq, &OutStatus); | |
100 | return (code ? 0 : OutStatus.CallerAccess & arights); | |
101 | } | |
102 | } | |
103 | ||
104 | ||
105 | /* the new access ok function. AVC must be held but not locked. if avc is a | |
106 | * file, its parent need not be held, and should not be locked. */ | |
107 | ||
108 | int | |
109 | afs_AccessOK(struct vcache *avc, afs_int32 arights, struct vrequest *areq, | |
110 | afs_int32 check_mode_bits) | |
111 | { | |
112 | struct vcache *tvc; | |
113 | struct VenusFid dirFid; | |
114 | afs_int32 mask; | |
115 | afs_int32 dirBits; | |
116 | afs_int32 fileBits; | |
117 | ||
118 | AFS_STATCNT(afs_AccessOK); | |
119 | ||
120 | if ((vType(avc) == VDIR) || (avc->f.states & CForeign)) { | |
121 | /* rights are just those from acl */ | |
122 | if (afs_InReadDir(avc)) { | |
123 | /* if we are already in readdir, then they may have read and | |
124 | * lookup, and nothing else, and nevermind the real ACL. | |
125 | * Otherwise we might end up with problems trying to call | |
126 | * FetchStatus on the vnode readdir is working on, and that | |
127 | * would be a real mess. | |
128 | */ | |
129 | dirBits = PRSFS_LOOKUP | PRSFS_READ; | |
130 | return (arights == (dirBits & arights)); | |
131 | } | |
132 | return (arights == afs_GetAccessBits(avc, arights, areq)); | |
133 | } else { | |
134 | /* some rights come from dir and some from file. Specifically, you | |
135 | * have "a" rights to a file if you are its owner, which comes | |
136 | * back as "a" rights to the file. You have other rights just | |
137 | * from dir, but all are restricted by the file mode bit. Now, | |
138 | * if you have I and A rights to a file, we throw in R and W | |
139 | * rights for free. These rights will then be restricted by | |
140 | * the access mask. */ | |
141 | dirBits = 0; | |
142 | if (avc->f.parent.vnode) { | |
143 | dirFid.Cell = avc->f.fid.Cell; | |
144 | dirFid.Fid.Volume = avc->f.fid.Fid.Volume; | |
145 | dirFid.Fid.Vnode = avc->f.parent.vnode; | |
146 | dirFid.Fid.Unique = avc->f.parent.unique; | |
147 | /* Avoid this GetVCache call */ | |
148 | tvc = afs_GetVCache(&dirFid, areq, NULL, NULL); | |
149 | if (tvc) { | |
150 | dirBits = afs_GetAccessBits(tvc, arights, areq); | |
151 | afs_PutVCache(tvc); | |
152 | } | |
153 | } else | |
154 | dirBits = 0xffffffff; /* assume OK; this is a race condition */ | |
155 | if (arights & PRSFS_ADMINISTER) | |
156 | fileBits = afs_GetAccessBits(avc, arights, areq); | |
157 | else | |
158 | fileBits = 0; /* don't make call if results don't matter */ | |
159 | ||
160 | /* compute basic rights in fileBits, taking A from file bits */ | |
161 | fileBits = | |
162 | (fileBits & PRSFS_ADMINISTER) | (dirBits & ~PRSFS_ADMINISTER); | |
163 | ||
164 | /* for files, throw in R and W if have I and A (owner). This makes | |
165 | * insert-only dirs work properly */ | |
166 | if (vType(avc) != VDIR | |
167 | && (fileBits & (PRSFS_ADMINISTER | PRSFS_INSERT)) == | |
168 | (PRSFS_ADMINISTER | PRSFS_INSERT)) | |
169 | fileBits |= (PRSFS_READ | PRSFS_WRITE); | |
170 | ||
171 | if (check_mode_bits & CHECK_MODE_BITS) { | |
172 | /* owner mode bits are further restrictions on the access mode | |
173 | * The mode bits are mapped to protection bits through the | |
174 | * fileModeMap. If CMB_ALLOW_EXEC_AS_READ is set, it's from the | |
175 | * NFS translator and we don't know if it's a read or execute | |
176 | * on the NFS client, but both need to read the data. | |
177 | */ | |
178 | mask = (avc->f.m.Mode & 0700) >> 6; /* file restrictions to use */ | |
179 | fileBits &= ~fileModeMap[mask]; | |
180 | if (check_mode_bits & CMB_ALLOW_EXEC_AS_READ) { | |
181 | if (avc->f.m.Mode & 0100) | |
182 | fileBits |= PRSFS_READ; | |
183 | } | |
184 | } | |
185 | return ((fileBits & arights) == arights); /* true if all rights bits are on */ | |
186 | } | |
187 | } | |
188 | ||
189 | ||
190 | #if defined(AFS_SUN5_ENV) || (defined(AFS_SGI_ENV) && !defined(AFS_SGI65_ENV)) | |
191 | int | |
192 | afs_access(OSI_VC_DECL(avc), afs_int32 amode, int flags, | |
193 | afs_ucred_t *acred) | |
194 | #else | |
195 | int | |
196 | afs_access(OSI_VC_DECL(avc), afs_int32 amode, | |
197 | afs_ucred_t *acred) | |
198 | #endif | |
199 | { | |
200 | afs_int32 code; | |
201 | struct vrequest *treq = NULL; | |
202 | struct afs_fakestat_state fakestate; | |
203 | OSI_VC_CONVERT(avc); | |
204 | ||
205 | AFS_STATCNT(afs_access); | |
206 | afs_Trace3(afs_iclSetp, CM_TRACE_ACCESS, ICL_TYPE_POINTER, avc, | |
207 | ICL_TYPE_INT32, amode, ICL_TYPE_OFFSET, | |
208 | ICL_HANDLE_OFFSET(avc->f.m.Length)); | |
209 | ||
210 | afs_InitFakeStat(&fakestate); | |
211 | if ((code = afs_CreateReq(&treq, acred))) { | |
212 | return code; | |
213 | } | |
214 | ||
215 | AFS_DISCON_LOCK(); | |
216 | ||
217 | if (afs_fakestat_enable && avc->mvstat == AFS_MVSTAT_MTPT) { | |
218 | code = afs_TryEvalFakeStat(&avc, &fakestate, treq); | |
219 | if (code == 0 && avc->mvstat == AFS_MVSTAT_MTPT) { | |
220 | afs_PutFakeStat(&fakestate); | |
221 | AFS_DISCON_UNLOCK(); | |
222 | afs_DestroyReq(treq); | |
223 | return 0; | |
224 | } | |
225 | } else { | |
226 | code = afs_EvalFakeStat(&avc, &fakestate, treq); | |
227 | } | |
228 | ||
229 | if (code) { | |
230 | afs_PutFakeStat(&fakestate); | |
231 | AFS_DISCON_UNLOCK(); | |
232 | afs_DestroyReq(treq); | |
233 | return code; | |
234 | } | |
235 | ||
236 | if (vType(avc) != VDIR || !afs_InReadDir(avc)) { | |
237 | code = afs_VerifyVCache(avc, treq); | |
238 | if (code) { | |
239 | afs_PutFakeStat(&fakestate); | |
240 | AFS_DISCON_UNLOCK(); | |
241 | code = afs_CheckCode(code, treq, 16); | |
242 | afs_DestroyReq(treq); | |
243 | return code; | |
244 | } | |
245 | } | |
246 | ||
247 | /* if we're looking for write access and we have a read-only file system, report it */ | |
248 | if ((amode & VWRITE) && (avc->f.states & CRO)) { | |
249 | afs_PutFakeStat(&fakestate); | |
250 | AFS_DISCON_UNLOCK(); | |
251 | afs_DestroyReq(treq); | |
252 | return EROFS; | |
253 | } | |
254 | ||
255 | /* If we're looking for write access, and we're disconnected without logging, forget it */ | |
256 | if ((amode & VWRITE) && (AFS_IS_DISCONNECTED && !AFS_IS_DISCON_RW)) { | |
257 | afs_PutFakeStat(&fakestate); | |
258 | AFS_DISCON_UNLOCK(); | |
259 | /* printf("Network is down in afs_vnop_access\n"); */ | |
260 | afs_DestroyReq(treq); | |
261 | return ENETDOWN; | |
262 | } | |
263 | ||
264 | code = 1; /* Default from here on in is access ok. */ | |
265 | if (avc->f.states & CForeign) { | |
266 | /* In the dfs xlator the EXEC bit is mapped to LOOKUP */ | |
267 | if (amode & VEXEC) | |
268 | code = afs_AccessOK(avc, PRSFS_LOOKUP, treq, CHECK_MODE_BITS); | |
269 | if (code && (amode & VWRITE)) { | |
270 | code = afs_AccessOK(avc, PRSFS_WRITE, treq, CHECK_MODE_BITS); | |
271 | if (code && (vType(avc) == VDIR)) { | |
272 | if (code) | |
273 | code = | |
274 | afs_AccessOK(avc, PRSFS_INSERT, treq, | |
275 | CHECK_MODE_BITS); | |
276 | if (!code) | |
277 | code = | |
278 | afs_AccessOK(avc, PRSFS_DELETE, treq, | |
279 | CHECK_MODE_BITS); | |
280 | } | |
281 | } | |
282 | if (code && (amode & VREAD)) | |
283 | code = afs_AccessOK(avc, PRSFS_READ, treq, CHECK_MODE_BITS); | |
284 | } else { | |
285 | if (vType(avc) == VDIR) { | |
286 | if (amode & VEXEC) | |
287 | code = | |
288 | afs_AccessOK(avc, PRSFS_LOOKUP, treq, CHECK_MODE_BITS); | |
289 | if (code && (amode & VWRITE)) { | |
290 | code = | |
291 | afs_AccessOK(avc, PRSFS_INSERT, treq, CHECK_MODE_BITS); | |
292 | if (!code) | |
293 | code = | |
294 | afs_AccessOK(avc, PRSFS_DELETE, treq, | |
295 | CHECK_MODE_BITS); | |
296 | } | |
297 | if (code && (amode & VREAD)) | |
298 | code = | |
299 | afs_AccessOK(avc, PRSFS_LOOKUP, treq, CHECK_MODE_BITS); | |
300 | } else { | |
301 | if (amode & VEXEC) { | |
302 | code = afs_AccessOK(avc, PRSFS_READ, treq, CHECK_MODE_BITS); | |
303 | if (code) { | |
304 | if ((avc->f.m.Mode & 0100) == 0) | |
305 | code = 0; | |
306 | } else if (avc->f.m.Mode & 0100) | |
307 | code = 1; | |
308 | } | |
309 | if (code && (amode & VWRITE)) { | |
310 | code = afs_AccessOK(avc, PRSFS_WRITE, treq, CHECK_MODE_BITS); | |
311 | ||
312 | /* The above call fails when the NFS translator tries to copy | |
313 | ** a file with r--r--r-- permissions into a directory which | |
314 | ** has system:anyuser acl. This is because the destination file | |
315 | ** file is first created with r--r--r-- permissions through an | |
316 | ** unauthenticated connectin. hence, the above afs_AccessOK | |
317 | ** call returns failure. hence, we retry without any file | |
318 | ** mode bit checking */ | |
319 | if (!code && AFS_NFSXLATORREQ(acred) | |
320 | && avc->f.m.Owner == ANONYMOUSID) | |
321 | code = | |
322 | afs_AccessOK(avc, PRSFS_WRITE, treq, | |
323 | DONT_CHECK_MODE_BITS); | |
324 | } | |
325 | if (code && (amode & VREAD)) | |
326 | code = afs_AccessOK(avc, PRSFS_READ, treq, CHECK_MODE_BITS); | |
327 | } | |
328 | } | |
329 | afs_PutFakeStat(&fakestate); | |
330 | ||
331 | AFS_DISCON_UNLOCK(); | |
332 | ||
333 | if (code) { | |
334 | afs_DestroyReq(treq); | |
335 | return 0; /* if access is ok */ | |
336 | } else { | |
337 | code = afs_CheckCode(EACCES, treq, 17); /* failure code */ | |
338 | afs_DestroyReq(treq); | |
339 | return code; | |
340 | } | |
341 | } | |
342 | ||
343 | #if defined(UKERNEL) | |
344 | /* | |
345 | * afs_getRights | |
346 | * This function is just an interface to afs_GetAccessBits | |
347 | */ | |
348 | int | |
349 | afs_getRights(OSI_VC_DECL(avc), afs_int32 arights, | |
350 | afs_ucred_t *acred) | |
351 | { | |
352 | afs_int32 code; | |
353 | struct vrequest *treq = NULL; | |
354 | OSI_VC_CONVERT(avc); | |
355 | ||
356 | if ((code = afs_CreateReq(&treq, acred))) | |
357 | return code; | |
358 | ||
359 | code = afs_VerifyVCache(avc, treq); | |
360 | if (code) { | |
361 | code = afs_CheckCode(code, treq, 18); | |
362 | afs_DestroyReq(treq); | |
363 | return code; | |
364 | } | |
365 | ||
366 | code = afs_GetAccessBits(avc, arights, treq); | |
367 | afs_DestroyReq(treq); | |
368 | return code; | |
369 | } | |
370 | #endif /* defined(UKERNEL) */ |