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