1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.
24  * All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 #pragma ident	"%Z%%M%	%I%	%E% SMI"
29 
30 #include <sys/param.h>
31 #include <sys/types.h>
32 #include <sys/systm.h>
33 #include <sys/cred.h>
34 #include <sys/proc.h>
35 #include <sys/user.h>
36 #include <sys/buf.h>
37 #include <sys/vfs.h>
38 #include <sys/vnode.h>
39 #include <sys/pathname.h>
40 #include <sys/uio.h>
41 #include <sys/file.h>
42 #include <sys/stat.h>
43 #include <sys/errno.h>
44 #include <sys/socket.h>
45 #include <sys/sysmacros.h>
46 #include <sys/siginfo.h>
47 #include <sys/tiuser.h>
48 #include <sys/statvfs.h>
49 #include <sys/t_kuser.h>
50 #include <sys/kmem.h>
51 #include <sys/kstat.h>
52 #include <sys/acl.h>
53 #include <sys/dirent.h>
54 #include <sys/cmn_err.h>
55 #include <sys/debug.h>
56 #include <sys/unistd.h>
57 #include <sys/vtrace.h>
58 #include <sys/mode.h>
59 
60 #include <rpc/types.h>
61 #include <rpc/auth.h>
62 #include <rpc/svc.h>
63 #include <rpc/xdr.h>
64 
65 #include <nfs/nfs.h>
66 #include <nfs/export.h>
67 #include <nfs/nfssys.h>
68 #include <nfs/nfs_clnt.h>
69 #include <nfs/nfs_acl.h>
70 
71 /*
72  * These are the interface routines for the server side of the
73  * NFS ACL server.  See the NFS ACL protocol specification
74  * for a description of this interface.
75  */
76 
77 /* ARGSUSED */
78 void
79 acl2_getacl(GETACL2args *args, GETACL2res *resp, struct exportinfo *exi,
80 	struct svc_req *req, cred_t *cr)
81 {
82 	int error;
83 	vnode_t *vp;
84 	vattr_t va;
85 
86 	vp = nfs_fhtovp(&args->fh, exi);
87 	if (vp == NULL) {
88 		resp->status = NFSERR_STALE;
89 		return;
90 	}
91 
92 	bzero((caddr_t)&resp->resok.acl, sizeof (resp->resok.acl));
93 
94 	resp->resok.acl.vsa_mask = args->mask;
95 
96 	error = VOP_GETSECATTR(vp, &resp->resok.acl, 0, cr);
97 
98 	if (error) {
99 		VN_RELE(vp);
100 		resp->status = puterrno(error);
101 		return;
102 	}
103 
104 	va.va_mask = AT_ALL;
105 	error = rfs4_delegated_getattr(vp, &va, 0, cr);
106 
107 	VN_RELE(vp);
108 
109 	/* check for overflowed values */
110 	if (!error) {
111 		error = vattr_to_nattr(&va, &resp->resok.attr);
112 	}
113 	if (error) {
114 		resp->status = puterrno(error);
115 		if (resp->resok.acl.vsa_aclcnt > 0 &&
116 		    resp->resok.acl.vsa_aclentp != NULL) {
117 			kmem_free((caddr_t)resp->resok.acl.vsa_aclentp,
118 			    resp->resok.acl.vsa_aclcnt * sizeof (aclent_t));
119 		}
120 		if (resp->resok.acl.vsa_dfaclcnt > 0 &&
121 		    resp->resok.acl.vsa_dfaclentp != NULL) {
122 			kmem_free((caddr_t)resp->resok.acl.vsa_dfaclentp,
123 			    resp->resok.acl.vsa_dfaclcnt * sizeof (aclent_t));
124 		}
125 		return;
126 	}
127 
128 	resp->status = NFS_OK;
129 	if (!(args->mask & NA_ACL)) {
130 		if (resp->resok.acl.vsa_aclcnt > 0 &&
131 		    resp->resok.acl.vsa_aclentp != NULL) {
132 			kmem_free((caddr_t)resp->resok.acl.vsa_aclentp,
133 			    resp->resok.acl.vsa_aclcnt * sizeof (aclent_t));
134 		}
135 		resp->resok.acl.vsa_aclentp = NULL;
136 	}
137 	if (!(args->mask & NA_DFACL)) {
138 		if (resp->resok.acl.vsa_dfaclcnt > 0 &&
139 		    resp->resok.acl.vsa_dfaclentp != NULL) {
140 			kmem_free((caddr_t)resp->resok.acl.vsa_dfaclentp,
141 			    resp->resok.acl.vsa_dfaclcnt * sizeof (aclent_t));
142 		}
143 		resp->resok.acl.vsa_dfaclentp = NULL;
144 	}
145 }
146 
147 fhandle_t *
148 acl2_getacl_getfh(GETACL2args *args)
149 {
150 
151 	return (&args->fh);
152 }
153 
154 void
155 acl2_getacl_free(GETACL2res *resp)
156 {
157 
158 	if (resp->status == NFS_OK) {
159 		if (resp->resok.acl.vsa_aclcnt > 0 &&
160 		    resp->resok.acl.vsa_aclentp != NULL) {
161 			kmem_free((caddr_t)resp->resok.acl.vsa_aclentp,
162 			    resp->resok.acl.vsa_aclcnt * sizeof (aclent_t));
163 		}
164 		if (resp->resok.acl.vsa_dfaclcnt > 0 &&
165 		    resp->resok.acl.vsa_dfaclentp != NULL) {
166 			kmem_free((caddr_t)resp->resok.acl.vsa_dfaclentp,
167 			    resp->resok.acl.vsa_dfaclcnt * sizeof (aclent_t));
168 		}
169 	}
170 }
171 
172 /* ARGSUSED */
173 void
174 acl2_setacl(SETACL2args *args, SETACL2res *resp, struct exportinfo *exi,
175 	struct svc_req *req, cred_t *cr)
176 {
177 	int error;
178 	vnode_t *vp;
179 	vattr_t va;
180 
181 	vp = nfs_fhtovp(&args->fh, exi);
182 	if (vp == NULL) {
183 		resp->status = NFSERR_STALE;
184 		return;
185 	}
186 
187 	if (rdonly(exi, req) || vn_is_readonly(vp)) {
188 		VN_RELE(vp);
189 		resp->status = NFSERR_ROFS;
190 		return;
191 	}
192 
193 	(void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, NULL);
194 	error = VOP_SETSECATTR(vp, &args->acl, 0, cr);
195 	if (error) {
196 		VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL);
197 		VN_RELE(vp);
198 		resp->status = puterrno(error);
199 		return;
200 	}
201 
202 	va.va_mask = AT_ALL;
203 	error = rfs4_delegated_getattr(vp, &va, 0, cr);
204 
205 	VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL);
206 	VN_RELE(vp);
207 
208 	/* check for overflowed values */
209 	if (!error) {
210 		error = vattr_to_nattr(&va, &resp->resok.attr);
211 	}
212 	if (error) {
213 		resp->status = puterrno(error);
214 		return;
215 	}
216 
217 	resp->status = NFS_OK;
218 }
219 
220 fhandle_t *
221 acl2_setacl_getfh(SETACL2args *args)
222 {
223 
224 	return (&args->fh);
225 }
226 
227 /* ARGSUSED */
228 void
229 acl2_getattr(GETATTR2args *args, GETATTR2res *resp, struct exportinfo *exi,
230 	struct svc_req *req, cred_t *cr)
231 {
232 	int error;
233 	vnode_t *vp;
234 	vattr_t va;
235 
236 	vp = nfs_fhtovp(&args->fh, exi);
237 	if (vp == NULL) {
238 		resp->status = NFSERR_STALE;
239 		return;
240 	}
241 
242 	va.va_mask = AT_ALL;
243 	error = rfs4_delegated_getattr(vp, &va, 0, cr);
244 
245 	VN_RELE(vp);
246 
247 	/* check for overflowed values */
248 	if (!error) {
249 		error = vattr_to_nattr(&va, &resp->resok.attr);
250 	}
251 	if (error) {
252 		resp->status = puterrno(error);
253 		return;
254 	}
255 
256 	resp->status = NFS_OK;
257 }
258 
259 fhandle_t *
260 acl2_getattr_getfh(GETATTR2args *args)
261 {
262 
263 	return (&args->fh);
264 }
265 
266 /* ARGSUSED */
267 void
268 acl2_access(ACCESS2args *args, ACCESS2res *resp, struct exportinfo *exi,
269 	struct svc_req *req, cred_t *cr)
270 {
271 	int error;
272 	vnode_t *vp;
273 	vattr_t va;
274 	int checkwriteperm;
275 
276 	vp = nfs_fhtovp(&args->fh, exi);
277 	if (vp == NULL) {
278 		resp->status = NFSERR_STALE;
279 		return;
280 	}
281 
282 	/*
283 	 * If the file system is exported read only, it is not appropriate
284 	 * to check write permissions for regular files and directories.
285 	 * Special files are interpreted by the client, so the underlying
286 	 * permissions are sent back to the client for interpretation.
287 	 */
288 	if (rdonly(exi, req) && (vp->v_type == VREG || vp->v_type == VDIR))
289 		checkwriteperm = 0;
290 	else
291 		checkwriteperm = 1;
292 
293 	/*
294 	 * We need the mode so that we can correctly determine access
295 	 * permissions relative to a mandatory lock file.  Access to
296 	 * mandatory lock files is denied on the server, so it might
297 	 * as well be reflected to the server during the open.
298 	 */
299 	va.va_mask = AT_MODE;
300 	error = VOP_GETATTR(vp, &va, 0, cr);
301 	if (error) {
302 		VN_RELE(vp);
303 		resp->status = puterrno(error);
304 		return;
305 	}
306 
307 	resp->resok.access = 0;
308 
309 	if (args->access & ACCESS2_READ) {
310 		error = VOP_ACCESS(vp, VREAD, 0, cr);
311 		if (!error && !MANDLOCK(vp, va.va_mode))
312 			resp->resok.access |= ACCESS2_READ;
313 	}
314 	if ((args->access & ACCESS2_LOOKUP) && vp->v_type == VDIR) {
315 		error = VOP_ACCESS(vp, VEXEC, 0, cr);
316 		if (!error)
317 			resp->resok.access |= ACCESS2_LOOKUP;
318 	}
319 	if (checkwriteperm &&
320 	    (args->access & (ACCESS2_MODIFY|ACCESS2_EXTEND))) {
321 		error = VOP_ACCESS(vp, VWRITE, 0, cr);
322 		if (!error && !MANDLOCK(vp, va.va_mode))
323 			resp->resok.access |=
324 			    (args->access & (ACCESS2_MODIFY|ACCESS2_EXTEND));
325 	}
326 	if (checkwriteperm &&
327 	    (args->access & ACCESS2_DELETE) && (vp->v_type == VDIR)) {
328 		error = VOP_ACCESS(vp, VWRITE, 0, cr);
329 		if (!error)
330 			resp->resok.access |= ACCESS2_DELETE;
331 	}
332 	if (args->access & ACCESS2_EXECUTE) {
333 		error = VOP_ACCESS(vp, VEXEC, 0, cr);
334 		if (!error && !MANDLOCK(vp, va.va_mode))
335 			resp->resok.access |= ACCESS2_EXECUTE;
336 	}
337 
338 	va.va_mask = AT_ALL;
339 	error = rfs4_delegated_getattr(vp, &va, 0, cr);
340 
341 	VN_RELE(vp);
342 
343 	/* check for overflowed values */
344 	if (!error) {
345 		error = vattr_to_nattr(&va, &resp->resok.attr);
346 	}
347 	if (error) {
348 		resp->status = puterrno(error);
349 		return;
350 	}
351 
352 	resp->status = NFS_OK;
353 }
354 
355 fhandle_t *
356 acl2_access_getfh(ACCESS2args *args)
357 {
358 
359 	return (&args->fh);
360 }
361 
362 /* ARGSUSED */
363 void
364 acl2_getxattrdir(GETXATTRDIR2args *args, GETXATTRDIR2res *resp,
365 	struct exportinfo *exi, struct svc_req *req, cred_t *cr)
366 {
367 	int error;
368 	int flags;
369 	vnode_t *vp, *avp;
370 
371 	vp = nfs_fhtovp(&args->fh, exi);
372 	if (vp == NULL) {
373 		resp->status = NFSERR_STALE;
374 		return;
375 	}
376 
377 	flags = LOOKUP_XATTR;
378 	if (args->create)
379 		flags |= CREATE_XATTR_DIR;
380 	else {
381 		ulong_t val = 0;
382 		error = VOP_PATHCONF(vp, _PC_XATTR_EXISTS, &val, cr);
383 		if (!error && val == 0) {
384 			VN_RELE(vp);
385 			resp->status = NFSERR_NOENT;
386 			return;
387 		}
388 	}
389 
390 	error = VOP_LOOKUP(vp, "", &avp, NULL, flags, NULL, cr);
391 	if (!error && avp == vp) {	/* lookup of "" on old FS? */
392 		error = EINVAL;
393 		VN_RELE(avp);
394 	}
395 	if (!error) {
396 		struct vattr va;
397 		va.va_mask = AT_ALL;
398 		error = rfs4_delegated_getattr(avp, &va, 0, cr);
399 		if (!error) {
400 			error = vattr_to_nattr(&va, &resp->resok.attr);
401 			if (!error)
402 				error = makefh(&resp->resok.fh, avp, exi);
403 		}
404 		VN_RELE(avp);
405 	}
406 
407 	VN_RELE(vp);
408 
409 	if (error) {
410 		resp->status = puterrno(error);
411 		return;
412 	}
413 	resp->status = NFS_OK;
414 }
415 
416 fhandle_t *
417 acl2_getxattrdir_getfh(GETXATTRDIR2args *args)
418 {
419 	return (&args->fh);
420 }
421 
422 /* ARGSUSED */
423 void
424 acl3_getacl(GETACL3args *args, GETACL3res *resp, struct exportinfo *exi,
425 	struct svc_req *req, cred_t *cr)
426 {
427 	int error;
428 	vnode_t *vp;
429 	vattr_t *vap;
430 	vattr_t va;
431 
432 	vap = NULL;
433 
434 	vp = nfs3_fhtovp(&args->fh, exi);
435 	if (vp == NULL) {
436 		error = ESTALE;
437 		goto out;
438 	}
439 
440 #ifdef DEBUG
441 	if (rfs3_do_post_op_attr) {
442 		va.va_mask = AT_ALL;
443 		vap = rfs4_delegated_getattr(vp, &va, 0, cr) ? NULL : &va;
444 	} else
445 		vap = NULL;
446 #else
447 	va.va_mask = AT_ALL;
448 	vap = rfs4_delegated_getattr(vp, &va, 0, cr) ? NULL : &va;
449 #endif
450 
451 	bzero((caddr_t)&resp->resok.acl, sizeof (resp->resok.acl));
452 
453 	resp->resok.acl.vsa_mask = args->mask;
454 
455 	error = VOP_GETSECATTR(vp, &resp->resok.acl, 0, cr);
456 
457 	if (error)
458 		goto out;
459 
460 #ifdef DEBUG
461 	if (rfs3_do_post_op_attr) {
462 		va.va_mask = AT_ALL;
463 		vap = rfs4_delegated_getattr(vp, &va, 0, cr) ? NULL : &va;
464 	} else
465 		vap = NULL;
466 #else
467 	va.va_mask = AT_ALL;
468 	vap = rfs4_delegated_getattr(vp, &va, 0, cr) ? NULL : &va;
469 #endif
470 
471 	VN_RELE(vp);
472 
473 	resp->status = NFS3_OK;
474 	vattr_to_post_op_attr(vap, &resp->resok.attr);
475 	if (!(args->mask & NA_ACL)) {
476 		if (resp->resok.acl.vsa_aclcnt > 0 &&
477 		    resp->resok.acl.vsa_aclentp != NULL) {
478 			kmem_free((caddr_t)resp->resok.acl.vsa_aclentp,
479 			    resp->resok.acl.vsa_aclcnt * sizeof (aclent_t));
480 		}
481 		resp->resok.acl.vsa_aclentp = NULL;
482 	}
483 	if (!(args->mask & NA_DFACL)) {
484 		if (resp->resok.acl.vsa_dfaclcnt > 0 &&
485 		    resp->resok.acl.vsa_dfaclentp != NULL) {
486 			kmem_free((caddr_t)resp->resok.acl.vsa_dfaclentp,
487 			    resp->resok.acl.vsa_dfaclcnt * sizeof (aclent_t));
488 		}
489 		resp->resok.acl.vsa_dfaclentp = NULL;
490 	}
491 	return;
492 
493 out:
494 	if (curthread->t_flag & T_WOULDBLOCK) {
495 		curthread->t_flag &= ~T_WOULDBLOCK;
496 		resp->status = NFS3ERR_JUKEBOX;
497 	} else
498 		resp->status = puterrno3(error);
499 out1:
500 	if (vp != NULL)
501 		VN_RELE(vp);
502 	vattr_to_post_op_attr(vap, &resp->resfail.attr);
503 }
504 
505 fhandle_t *
506 acl3_getacl_getfh(GETACL3args *args)
507 {
508 
509 	return ((fhandle_t *)&args->fh.fh3_u.nfs_fh3_i.fh3_i);
510 }
511 
512 void
513 acl3_getacl_free(GETACL3res *resp)
514 {
515 
516 	if (resp->status == NFS3_OK) {
517 		if (resp->resok.acl.vsa_aclcnt > 0 &&
518 		    resp->resok.acl.vsa_aclentp != NULL) {
519 			kmem_free((caddr_t)resp->resok.acl.vsa_aclentp,
520 			    resp->resok.acl.vsa_aclcnt * sizeof (aclent_t));
521 		}
522 		if (resp->resok.acl.vsa_dfaclcnt > 0 &&
523 		    resp->resok.acl.vsa_dfaclentp != NULL) {
524 			kmem_free((caddr_t)resp->resok.acl.vsa_dfaclentp,
525 			    resp->resok.acl.vsa_dfaclcnt * sizeof (aclent_t));
526 		}
527 	}
528 }
529 
530 /* ARGSUSED */
531 void
532 acl3_setacl(SETACL3args *args, SETACL3res *resp, struct exportinfo *exi,
533 	struct svc_req *req, cred_t *cr)
534 {
535 	int error;
536 	vnode_t *vp;
537 	vattr_t *vap;
538 	vattr_t va;
539 
540 	vap = NULL;
541 
542 	vp = nfs3_fhtovp(&args->fh, exi);
543 	if (vp == NULL) {
544 		error = ESTALE;
545 		goto out1;
546 	}
547 
548 	(void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, NULL);
549 
550 #ifdef DEBUG
551 	if (rfs3_do_post_op_attr) {
552 		va.va_mask = AT_ALL;
553 		vap = rfs4_delegated_getattr(vp, &va, 0, cr) ? NULL : &va;
554 	} else
555 		vap = NULL;
556 #else
557 	va.va_mask = AT_ALL;
558 	vap = rfs4_delegated_getattr(vp, &va, 0, cr) ? NULL : &va;
559 #endif
560 
561 	if (rdonly(exi, req) || vn_is_readonly(vp)) {
562 		resp->status = NFS3ERR_ROFS;
563 		goto out1;
564 	}
565 
566 	error = VOP_SETSECATTR(vp, &args->acl, 0, cr);
567 
568 #ifdef DEBUG
569 	if (rfs3_do_post_op_attr) {
570 		va.va_mask = AT_ALL;
571 		vap = rfs4_delegated_getattr(vp, &va, 0, cr) ? NULL : &va;
572 	} else
573 		vap = NULL;
574 #else
575 	va.va_mask = AT_ALL;
576 	vap = rfs4_delegated_getattr(vp, &va, 0, cr) ? NULL : &va;
577 #endif
578 
579 	if (error)
580 		goto out;
581 
582 	VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL);
583 	VN_RELE(vp);
584 
585 	resp->status = NFS3_OK;
586 	vattr_to_post_op_attr(vap, &resp->resok.attr);
587 	return;
588 
589 out:
590 	if (curthread->t_flag & T_WOULDBLOCK) {
591 		curthread->t_flag &= ~T_WOULDBLOCK;
592 		resp->status = NFS3ERR_JUKEBOX;
593 	} else
594 		resp->status = puterrno3(error);
595 out1:
596 	if (vp != NULL) {
597 		VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL);
598 		VN_RELE(vp);
599 	}
600 	vattr_to_post_op_attr(vap, &resp->resfail.attr);
601 }
602 
603 fhandle_t *
604 acl3_setacl_getfh(SETACL3args *args)
605 {
606 
607 	return ((fhandle_t *)&args->fh.fh3_u.nfs_fh3_i.fh3_i);
608 }
609 
610 /* ARGSUSED */
611 void
612 acl3_getxattrdir(GETXATTRDIR3args *args, GETXATTRDIR3res *resp,
613 	struct exportinfo *exi, struct svc_req *req, cred_t *cr)
614 {
615 	int error;
616 	int flags;
617 	vnode_t *vp, *avp;
618 
619 	vp = nfs3_fhtovp(&args->fh, exi);
620 	if (vp == NULL) {
621 		resp->status = NFS3ERR_STALE;
622 		return;
623 	}
624 
625 	flags = LOOKUP_XATTR;
626 	if (args->create)
627 		flags |= CREATE_XATTR_DIR;
628 	else {
629 		ulong_t val = 0;
630 		error = VOP_PATHCONF(vp, _PC_XATTR_EXISTS, &val, cr);
631 		if (!error && val == 0) {
632 			VN_RELE(vp);
633 			resp->status = NFS3ERR_NOENT;
634 			return;
635 		}
636 	}
637 
638 	error = VOP_LOOKUP(vp, "", &avp, NULL, flags, NULL, cr);
639 	if (!error && avp == vp) {	/* lookup of "" on old FS? */
640 		error = EINVAL;
641 		VN_RELE(avp);
642 	}
643 	if (!error) {
644 		struct vattr va;
645 		va.va_mask = AT_ALL;
646 		error = rfs4_delegated_getattr(avp, &va, 0, cr);
647 		if (!error) {
648 			vattr_to_post_op_attr(&va, &resp->resok.attr);
649 			error = makefh3(&resp->resok.fh, avp, exi);
650 		}
651 		VN_RELE(avp);
652 	}
653 
654 	VN_RELE(vp);
655 
656 	if (error) {
657 		resp->status = puterrno3(error);
658 		return;
659 	}
660 	resp->status = NFS3_OK;
661 }
662 
663 fhandle_t *
664 acl3_getxattrdir_getfh(GETXATTRDIR3args *args)
665 {
666 	return ((fhandle_t *)&args->fh.fh3_u.nfs_fh3_i.fh3_i);
667 }
668