diff -urNp current/TODO nfs4acl/TODO --- current/TODO 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/TODO 2009-05-26 17:15:43.000000000 +0200 @@ -0,0 +1,35 @@ +Things that need to be done before this goes into -CURRENT: + +- Don't use fpathconf(..., _PC_EXTENDED_SECURITY_NP); instead just call + acl_set_file(3) or acl_get_file(3) with ACL_TYPE_NFS4 and handle EOPNOTSUPP. + +- Decide how VAPPEND is supposed to work - always OR-ed with VWRITE, + or used alone. Fix stuff accordingly. + +- MFP4 of the above. + +- Decide what to do with MNT_RDONLY et a. Fix stuff. + +- MFP4 of the above. + +- Talk about semantics. Do whatever is needed. + +- Figure out whether this thing should be called 'nfs4' or 'nfsv4'. + Use this name consistently. + +- Review. + +Things to do, in no particular order: + +- Add support for NFSv4 ACLs to tar(1). + +- Make 'struct acl' variable size. + +- Benchmark things. + +- (maybe) Add a flag to inode to mark whether the file has ACL; + don't try to read ACL extatrr if the flag is not set. + +- Port Samba vfs_zfsacl module using libsunacl + (http://freebsd.org/~trasz/libsunacl.shar). + diff -urNp current/bin/chmod/chmod.c nfs4acl/bin/chmod/chmod.c --- current/bin/chmod/chmod.c 2009-04-29 09:21:56.000000000 +0200 +++ nfs4acl/bin/chmod/chmod.c 2009-05-26 17:15:43.000000000 +0200 @@ -54,6 +54,7 @@ __FBSDID("$FreeBSD: src/bin/chmod/chmod. #include void usage(void); +int may_have_nfs4acl(const FTSENT *ent); int main(int argc, char *argv[]) @@ -180,8 +181,15 @@ done: argv += optind; break; } newmode = getmode(set, p->fts_statp->st_mode); - if ((newmode & ALLPERMS) == (p->fts_statp->st_mode & ALLPERMS)) - continue; + /* + * With NFSv4 ACLs, it is possible that applying a mode + * identical to the one computed from an ACL will change + * that ACL. + */ + if (may_have_nfs4acl(p) == 0) { + if ((newmode & ALLPERMS) == (p->fts_statp->st_mode & ALLPERMS)) + continue; + } if ((*change_mode)(p->fts_accpath, newmode) && !fflag) { warn("%s", p->fts_path); rval = 1; @@ -219,3 +227,25 @@ usage(void) "usage: chmod [-fhv] [-R [-H | -L | -P]] mode file ...\n"); exit(1); } + +int +may_have_nfs4acl(const FTSENT *ent) +{ + int ret; + static dev_t previous_dev = (dev_t)-1; + static int supports_acls = -1; + + if (previous_dev != ent->fts_statp->st_dev) { + previous_dev = ent->fts_statp->st_dev; + supports_acls = 0; + + ret = pathconf(ent->fts_accpath, _PC_EXTENDED_SECURITY_NP); + if (ret > 0) + supports_acls = 1; + else if (ret < 0 && errno != EINVAL) + warn("%s", ent->fts_path); + } + + return (supports_acls); +} + diff -urNp current/bin/cp/utils.c nfs4acl/bin/cp/utils.c --- current/bin/cp/utils.c 2009-05-30 20:05:56.000000000 +0200 +++ nfs4acl/bin/cp/utils.c 2009-05-30 20:11:28.000000000 +0200 @@ -377,21 +377,57 @@ setfile(struct stat *fs, int fd) int preserve_fd_acls(int source_fd, int dest_fd) { - struct acl *aclp; acl_t acl; + acl_type_t source_type, dest_type; + int source_acl_supported = 0, dest_acl_supported = 0, trivial; + + if (fpathconf(source_fd, _PC_ACL_EXTENDED) == 1) { + source_acl_supported = 1; + source_type = ACL_TYPE_ACCESS; + } - if (fpathconf(source_fd, _PC_ACL_EXTENDED) != 1 || - fpathconf(dest_fd, _PC_ACL_EXTENDED) != 1) + if (fpathconf(source_fd, _PC_EXTENDED_SECURITY_NP) == 1) { + source_acl_supported = 1; + source_type = ACL_TYPE_NFS4; + } + + if (fpathconf(dest_fd, _PC_ACL_EXTENDED) == 1) { + dest_acl_supported = 1; + dest_type = ACL_TYPE_ACCESS; + } + + if (fpathconf(dest_fd, _PC_EXTENDED_SECURITY_NP) == 1) { + dest_acl_supported = 1; + dest_type = ACL_TYPE_NFS4; + } + + if (!source_acl_supported || !dest_acl_supported) return (0); - acl = acl_get_fd(source_fd); + + acl = acl_get_fd_np(source_fd, source_type); if (acl == NULL) { warn("failed to get acl entries while setting %s", to.p_path); return (1); } - aclp = &acl->ats_acl; - if (aclp->acl_cnt == 3) + + if (acl_is_trivial_np(acl, &trivial)) { + warn("acl_is_trivial() failed"); + return (1); + } + + if (trivial) + return (0); + + if (source_type != dest_type) { + warnx("ACL brand of source is %s, but destination is %s; " + "ACL not copied", + source_type == ACL_TYPE_NFS4 ? "NFSv4" : "POSIX.1e", + dest_type == ACL_TYPE_NFS4 ? "NFSv4" : "POSIX.1e"); + return (0); - if (acl_set_fd(dest_fd, acl) < 0) { + } + + if (acl_set_fd_np(dest_fd, acl, dest_type) < 0) { warn("failed to set acl entries for %s", to.p_path); return (1); } @@ -405,10 +441,41 @@ preserve_dir_acls(struct stat *fs, char int (*aclsetf)(const char *, acl_type_t, acl_t); struct acl *aclp; acl_t acl; + acl_type_t source_type, dest_type; + int source_acl_supported = 0, dest_acl_supported = 0; + + if (pathconf(source_dir, _PC_ACL_EXTENDED) == 1) { + source_acl_supported = 1; + source_type = ACL_TYPE_ACCESS; + } + + if (pathconf(source_dir, _PC_EXTENDED_SECURITY_NP) == 1) { + source_acl_supported = 1; + source_type = ACL_TYPE_NFS4; + } - if (pathconf(source_dir, _PC_ACL_EXTENDED) != 1 || - pathconf(dest_dir, _PC_ACL_EXTENDED) != 1) + if (pathconf(dest_dir, _PC_ACL_EXTENDED) == 1) { + dest_acl_supported = 1; + dest_type = ACL_TYPE_ACCESS; + } + + if (pathconf(dest_dir, _PC_EXTENDED_SECURITY_NP) == 1) { + dest_acl_supported = 1; + dest_type = ACL_TYPE_NFS4; + } + + if (!source_acl_supported || !dest_acl_supported) return (0); + + if (source_type != dest_type) { + warnx("ACL brand of source is %s, but destination is %s; " + "ACL not copied", + source_type == ACL_TYPE_NFS4 ? "NFSv4" : "POSIX.1e", + dest_type == ACL_TYPE_NFS4 ? "NFSv4" : "POSIX.1e"); + + return (0); + } + /* * If the file is a link we will not follow it */ @@ -419,31 +486,35 @@ preserve_dir_acls(struct stat *fs, char aclgetf = acl_get_file; aclsetf = acl_set_file; } - /* - * Even if there is no ACL_TYPE_DEFAULT entry here, a zero - * size ACL will be returned. So it is not safe to simply - * check the pointer to see if the default ACL is present. - */ - acl = aclgetf(source_dir, ACL_TYPE_DEFAULT); - if (acl == NULL) { - warn("failed to get default acl entries on %s", - source_dir); - return (1); - } - aclp = &acl->ats_acl; - if (aclp->acl_cnt != 0 && aclsetf(dest_dir, - ACL_TYPE_DEFAULT, acl) < 0) { - warn("failed to set default acl entries on %s", - dest_dir); - return (1); + + if (source_type != ACL_TYPE_NFS4) { + /* + * Even if there is no ACL_TYPE_DEFAULT entry here, a zero + * size ACL will be returned. So it is not safe to simply + * check the pointer to see if the default ACL is present. + */ + acl = aclgetf(source_dir, ACL_TYPE_DEFAULT); + if (acl == NULL) { + warn("failed to get default acl entries on %s", + source_dir); + return (1); + } + aclp = &acl->ats_acl; + if (aclp->acl_cnt != 0 && aclsetf(dest_dir, + ACL_TYPE_DEFAULT, acl) < 0) { + warn("failed to set default acl entries on %s", + dest_dir); + return (1); + } } - acl = aclgetf(source_dir, ACL_TYPE_ACCESS); + + acl = aclgetf(source_dir, source_type); if (acl == NULL) { warn("failed to get acl entries on %s", source_dir); return (1); } aclp = &acl->ats_acl; - if (aclsetf(dest_dir, ACL_TYPE_ACCESS, acl) < 0) { + if (aclsetf(dest_dir, dest_type, acl) < 0) { warn("failed to set acl entries on %s", dest_dir); return (1); } diff -urNp current/bin/getfacl/getfacl.1 nfs4acl/bin/getfacl/getfacl.1 --- current/bin/getfacl/getfacl.1 2009-04-29 09:21:58.000000000 +0200 +++ nfs4acl/bin/getfacl/getfacl.1 2009-05-26 17:15:43.000000000 +0200 @@ -38,7 +38,7 @@ .Nd get ACL information .Sh SYNOPSIS .Nm -.Op Fl dhq +.Op Fl dhinqv .Op Ar .Sh DESCRIPTION The @@ -61,13 +61,24 @@ The operation applies to the default ACL access ACL. An error is generated if a default ACL cannot be associated with .Ar file . +This option is not valid for NFSv4 ACLs. .It Fl h If the target of the operation is a symbolic link, return the ACL from the symbolic link itself rather than following the link. +.It Fl i +For NFSv4 ACLs, append numerical ID at the end of each entry containing +user or group name. +.It Fl n +Display user and group IDs numerically rather than converting to +a user or group name. +For POSIX.1e ACLs, this option does nothing. .It Fl q Do not write commented information about file name and ownership. This is useful when dealing with filenames with unprintable characters. +.It Fl v +For NFSv4 ACLs, display access mask and flags in a verbose form. +For POSIX.1e ACLs, this option does nothing. .El .Pp The following operand is available: diff -urNp current/bin/getfacl/getfacl.c nfs4acl/bin/getfacl/getfacl.c --- current/bin/getfacl/getfacl.c 2009-04-29 09:21:58.000000000 +0200 +++ nfs4acl/bin/getfacl/getfacl.c 2009-05-26 17:15:43.000000000 +0200 @@ -54,7 +54,7 @@ static void usage(void) { - fprintf(stderr, "getfacl [-dhq] [file ...]\n"); + fprintf(stderr, "getfacl [-dhnqv] [file ...]\n"); } static char * @@ -175,19 +175,20 @@ acl_from_stat(struct stat sb) } static int -print_acl(char *path, acl_type_t type, int hflag, int qflag) +print_acl(char *path, acl_type_t type, int hflag, int iflag, int nflag, + int qflag, int vflag) { struct stat sb; acl_t acl; char *acl_text; - int error; + int error, flags = 0; if (hflag) error = lstat(path, &sb); else error = stat(path, &sb); if (error == -1) { - warn("%s", path); + warn("%s: stat() failed", path); return(-1); } @@ -196,6 +197,16 @@ print_acl(char *path, acl_type_t type, i else more_than_one++; + if (pathconf(path, _PC_EXTENDED_SECURITY_NP)) { + if (type == ACL_TYPE_DEFAULT) { + warnx("%s: there are no default entries in NFSv4 ACLs", + path); + return (-1); + } + + type = ACL_TYPE_NFS4; + } + if (!qflag) printf("# file: %s\n# owner: %s\n# group: %s\n", path, getuname(sb.st_uid), getgname(sb.st_gid)); @@ -210,18 +221,27 @@ print_acl(char *path, acl_type_t type, i return(-1); } errno = 0; - if (type != ACL_TYPE_ACCESS) + if (type != ACL_TYPE_ACCESS && type != ACL_TYPE_NFS4) return(0); acl = acl_from_stat(sb); if (!acl) { - warn("acl_from_stat()"); + warn("%s: acl_from_stat() failed", path); return(-1); } } - acl_text = acl_to_text(acl, 0); + if (iflag) + flags |= ACL_TEXT_APPEND_ID; + + if (nflag) + flags |= ACL_TEXT_NUMERIC_IDS; + + if (vflag) + flags |= ACL_TEXT_VERBOSE; + + acl_text = acl_to_text_np(acl, 0, flags); if (!acl_text) { - warn("%s", path); + warn("%s: acl_to_text_np() failed", path); return(-1); } @@ -234,7 +254,8 @@ print_acl(char *path, acl_type_t type, i } static int -print_acl_from_stdin(acl_type_t type, int hflag, int qflag) +print_acl_from_stdin(acl_type_t type, int hflag, int iflag, int nflag, + int qflag, int vflag) { char *p, pathname[PATH_MAX]; int carried_error = 0; @@ -242,7 +263,8 @@ print_acl_from_stdin(acl_type_t type, in while (fgets(pathname, (int)sizeof(pathname), stdin)) { if ((p = strchr(pathname, '\n')) != NULL) *p = '\0'; - if (print_acl(pathname, type, hflag, qflag) == -1) { + if (print_acl(pathname, type, hflag, iflag, nflag, + qflag, vflag) == -1) { carried_error = -1; } } @@ -256,11 +278,14 @@ main(int argc, char *argv[]) acl_type_t type = ACL_TYPE_ACCESS; int carried_error = 0; int ch, error, i; - int hflag, qflag; + int hflag, iflag, qflag, nflag, vflag; hflag = 0; + iflag = 0; qflag = 0; - while ((ch = getopt(argc, argv, "dhq")) != -1) + nflag = 0; + vflag = 0; + while ((ch = getopt(argc, argv, "dhinqv")) != -1) switch(ch) { case 'd': type = ACL_TYPE_DEFAULT; @@ -268,9 +293,18 @@ main(int argc, char *argv[]) case 'h': hflag = 1; break; + case 'i': + iflag = 1; + break; + case 'n': + nflag = 1; + break; case 'q': qflag = 1; break; + case 'v': + vflag = 1; + break; default: usage(); return(-1); @@ -279,17 +313,20 @@ main(int argc, char *argv[]) argv += optind; if (argc == 0) { - error = print_acl_from_stdin(type, hflag, qflag); + error = print_acl_from_stdin(type, hflag, iflag, nflag, + qflag, vflag); return(error ? 1 : 0); } for (i = 0; i < argc; i++) { if (!strcmp(argv[i], "-")) { - error = print_acl_from_stdin(type, hflag, qflag); + error = print_acl_from_stdin(type, hflag, iflag, nflag, + qflag, vflag); if (error == -1) carried_error = -1; } else { - error = print_acl(argv[i], type, hflag, qflag); + error = print_acl(argv[i], type, hflag, iflag, nflag, + qflag, vflag); if (error == -1) carried_error = -1; } diff -urNp current/bin/ls/print.c nfs4acl/bin/ls/print.c --- current/bin/ls/print.c 2009-04-29 09:21:58.000000000 +0200 +++ nfs4acl/bin/ls/print.c 2009-04-29 09:30:28.000000000 +0200 @@ -616,9 +616,8 @@ static void aclmode(char *buf, const FTSENT *p, int *haveacls) { char name[MAXPATHLEN + 1]; - int entries, ret; + int type = ACL_TYPE_ACCESS, ret, trivial; acl_t facl; - acl_entry_t ae; /* * Add a + after the standard rwxrwxrwx mode if the file has an @@ -638,30 +637,48 @@ aclmode(char *buf, const FTSENT *p, int *haveacls = 1; return; } - if ((ret = pathconf(name, _PC_ACL_EXTENDED)) <= 0) { - if (ret < 0 && errno != EINVAL) - warn("%s", name); - else - *haveacls = 0; + + *haveacls = 0; + + ret = pathconf(name, _PC_ACL_EXTENDED); + if (ret > 0) { + type = ACL_TYPE_ACCESS; + *haveacls = 1; + } else if (ret < 0 && errno != EINVAL) { + warn("%s", name); + return; + } + + ret = pathconf(name, _PC_EXTENDED_SECURITY_NP); + if (ret > 0) { + type = ACL_TYPE_NFS4; + *haveacls = 1; + } else if (ret < 0 && errno != EINVAL) { + warn("%s", name); return; } - *haveacls = 1; - if ((facl = acl_get_file(name, ACL_TYPE_ACCESS)) != NULL) { - if (acl_get_entry(facl, ACL_FIRST_ENTRY, &ae) == 1) { - entries = 1; - while (acl_get_entry(facl, ACL_NEXT_ENTRY, &ae) == 1) - if (++entries > 3) - break; - /* - * POSIX.1e requires that ACLs of type ACL_TYPE_ACCESS - * must have at least three entries (owner, group, - * and other). So anything with more than 3 ACLs looks - * interesting to us. - */ - if (entries > 3) - buf[10] = '+'; + + if (*haveacls == 0) + return; + + if ((facl = acl_get_file(name, type)) != NULL) { + if (acl_is_trivial_np(facl, &trivial)) { + warn("%s", name); + return; } + + if (!trivial) + buf[10] = '+'; + acl_free(facl); - } else + } else { + /* + * We were denied access. So, obviously, there + * is some ACL denying READ_ACL there. + */ + if (errno == EPERM || errno == EACCES) + buf[10] = '+'; + warn("%s", name); + } } diff -urNp current/bin/mv/mv.c nfs4acl/bin/mv/mv.c --- current/bin/mv/mv.c 2009-04-29 09:21:58.000000000 +0200 +++ nfs4acl/bin/mv/mv.c 2009-05-26 17:15:43.000000000 +0200 @@ -74,6 +74,8 @@ static int copy(const char *, const char static int do_move(const char *, const char *); static int fastcopy(const char *, const char *, struct stat *); static void usage(void); +static int preserve_fd_acls(int source_fd, int dest_fd, const char *source_path, + const char *dest_path); int main(int argc, char *argv[]) @@ -260,7 +262,6 @@ fastcopy(const char *from, const char *t struct timeval tval[2]; static u_int blen; static char *bp; - acl_t acl; mode_t oldmode; int nread, from_fd, to_fd; @@ -311,23 +312,18 @@ err: if (unlink(to)) sbp->st_mode &= ~(S_ISUID | S_ISGID); } } + if (fchmod(to_fd, sbp->st_mode)) + warn("%s: set mode (was: 0%03o)", to, oldmode); + /* * POSIX 1003.2c states that if _POSIX_ACL_EXTENDED is in effect * for dest_file, then its ACLs shall reflect the ACLs of the * source_file. */ - if (fpathconf(to_fd, _PC_ACL_EXTENDED) == 1 && - fpathconf(from_fd, _PC_ACL_EXTENDED) == 1) { - acl = acl_get_fd(from_fd); - if (acl == NULL) - warn("failed to get acl entries while setting %s", - from); - else if (acl_set_fd(to_fd, acl) < 0) - warn("failed to set acl entries for %s", to); - } + preserve_fd_acls(from_fd, to_fd, from, to); + (void)close(from_fd); - if (fchmod(to_fd, sbp->st_mode)) - warn("%s: set mode (was: 0%03o)", to, oldmode); + /* * XXX * NFS doesn't support chflags; ignore errors unless there's reason @@ -438,6 +434,67 @@ copy(const char *from, const char *to) return (0); } +static int +preserve_fd_acls(int source_fd, int dest_fd, const char *source_path, + const char *dest_path) +{ + acl_t acl; + acl_type_t source_type, dest_type; + int source_acl_supported = 0, dest_acl_supported = 0, trivial; + + if (fpathconf(source_fd, _PC_ACL_EXTENDED) == 1) { + source_acl_supported = 1; + source_type = ACL_TYPE_ACCESS; + } + + if (fpathconf(source_fd, _PC_EXTENDED_SECURITY_NP) == 1) { + source_acl_supported = 1; + source_type = ACL_TYPE_NFS4; + } + + if (fpathconf(dest_fd, _PC_ACL_EXTENDED) == 1) { + dest_acl_supported = 1; + dest_type = ACL_TYPE_ACCESS; + } + + if (fpathconf(dest_fd, _PC_EXTENDED_SECURITY_NP) == 1) { + dest_acl_supported = 1; + dest_type = ACL_TYPE_NFS4; + } + + if (!source_acl_supported || !dest_acl_supported) + return (0); + + acl = acl_get_fd_np(source_fd, source_type); + if (acl == NULL) { + warn("failed to get acl entries while setting %s", source_path); + return (1); + } + + if (acl_is_trivial_np(acl, &trivial)) { + warn("acl_is_trivial() failed"); + return (1); + } + + if (trivial) + return (0); + + if (source_type != dest_type) { + warnx("ACL brand of source is %s, but destination is %s; " + "ACL not copied", + source_type == ACL_TYPE_NFS4 ? "NFSv4" : "POSIX.1e", + dest_type == ACL_TYPE_NFS4 ? "NFSv4" : "POSIX.1e"); + + return (0); + } + + if (acl_set_fd_np(dest_fd, acl, dest_type) < 0) { + warn("failed to set acl entries for %s", dest_path); + return (1); + } + return (0); +} + static void usage(void) { diff -urNp current/bin/setfacl/Makefile nfs4acl/bin/setfacl/Makefile --- current/bin/setfacl/Makefile 2009-04-29 09:21:59.000000000 +0200 +++ nfs4acl/bin/setfacl/Makefile 2009-05-23 08:37:49.000000000 +0200 @@ -1,6 +1,7 @@ # $FreeBSD: src/bin/setfacl/Makefile,v 1.8 2004/06/13 19:22:53 obrien Exp $ PROG= setfacl +CFLAGS+=-D_ACL_PRIVATE SRCS= file.c mask.c merge.c remove.c setfacl.c util.c .include diff -urNp current/bin/setfacl/mask.c nfs4acl/bin/setfacl/mask.c --- current/bin/setfacl/mask.c 2009-04-29 09:21:59.000000000 +0200 +++ nfs4acl/bin/setfacl/mask.c 2009-04-29 09:30:28.000000000 +0200 @@ -40,7 +40,7 @@ __FBSDID("$FreeBSD: src/bin/setfacl/mask /* set the appropriate mask the given ACL's */ int -set_acl_mask(acl_t *prev_acl) +set_acl_mask(acl_t *prev_acl, const char *filename) { acl_entry_t entry; acl_t acl; @@ -59,7 +59,7 @@ set_acl_mask(acl_t *prev_acl) acl = acl_dup(*prev_acl); if (acl == NULL) - err(1, "acl_dup() failed"); + err(1, "%s: acl_dup() failed", filename); if (n_flag == 0) { /* @@ -70,7 +70,7 @@ set_acl_mask(acl_t *prev_acl) * class in the resulting ACL */ if (acl_calc_mask(&acl)) { - warn("acl_calc_mask() failed"); + warn("%s: acl_calc_mask() failed", filename); acl_free(acl); return (-1); } @@ -86,7 +86,8 @@ set_acl_mask(acl_t *prev_acl) while (acl_get_entry(acl, entry_id, &entry) == 1) { entry_id = ACL_NEXT_ENTRY; if (acl_get_tag_type(entry, &tag) == -1) - err(1, "acl_get_tag_type() failed"); + err(1, "%s: acl_get_tag_type() failed", + filename); if (tag == ACL_MASK) { acl_free(acl); @@ -100,7 +101,7 @@ set_acl_mask(acl_t *prev_acl) * file, then write an error message to standard error and * continue with the next file. */ - warnx("warning: no mask entry"); + warnx("%s: warning: no mask entry", filename); acl_free(acl); return (0); } diff -urNp current/bin/setfacl/merge.c nfs4acl/bin/setfacl/merge.c --- current/bin/setfacl/merge.c 2009-04-29 09:21:59.000000000 +0200 +++ nfs4acl/bin/setfacl/merge.c 2009-05-26 17:15:43.000000000 +0200 @@ -36,12 +36,15 @@ __FBSDID("$FreeBSD: src/bin/setfacl/merg #include "setfacl.h" -static int merge_user_group(acl_entry_t *entry, acl_entry_t *entry_new); +static int merge_user_group(acl_entry_t *entry, acl_entry_t *entry_new, + int acl_brand); static int -merge_user_group(acl_entry_t *entry, acl_entry_t *entry_new) +merge_user_group(acl_entry_t *entry, acl_entry_t *entry_new, int acl_brand) { acl_permset_t permset; + acl_entry_type_t entry_type; + acl_flagset_t flagset; int have_entry; uid_t *id, *id_new; @@ -59,6 +62,18 @@ merge_user_group(acl_entry_t *entry, acl err(1, "acl_get_permset() failed"); if (acl_set_permset(*entry_new, permset) == -1) err(1, "acl_set_permset() failed"); + + if (acl_brand == ACL_BRAND_NFS4) { + if (acl_get_entry_type_np(*entry, &entry_type)) + err(1, "acl_get_entry_type_np() failed"); + if (acl_set_entry_type_np(*entry_new, entry_type)) + err(1, "acl_set_entry_type_np() failed"); + if (acl_get_flagset_np(*entry, &flagset)) + err(1, "acl_get_flagset_np() failed"); + if (acl_set_flagset_np(*entry_new, flagset)) + err(1, "acl_set_flagset_np() failed"); + } + have_entry = 1; } acl_free(id); @@ -71,20 +86,31 @@ merge_user_group(acl_entry_t *entry, acl * merge an ACL into existing file's ACL */ int -merge_acl(acl_t acl, acl_t *prev_acl) +merge_acl(acl_t acl, acl_t *prev_acl, const char *filename) { acl_entry_t entry, entry_new; acl_permset_t permset; acl_t acl_new; acl_tag_t tag, tag_new; - int entry_id, entry_id_new, have_entry; + acl_entry_type_t entry_type, entry_type_new; + acl_flagset_t flagset; + int entry_id, entry_id_new, have_entry, entry_number = 0; + int acl_brand, prev_acl_brand; + + acl_get_brand_np(acl, &acl_brand); + acl_get_brand_np(*prev_acl, &prev_acl_brand); + + if (acl_brand != prev_acl_brand) { + warnx("%s: branding mismatch; existing ACL is %s, " + "entry to be merged is %s", filename, + prev_acl_brand == ACL_BRAND_NFS4 ? "NFSv4" : "POSIX.1e", + acl_brand == ACL_BRAND_NFS4 ? "NFSv4" : "POSIX.1e"); + return (-1); + } - if (acl_type == ACL_TYPE_ACCESS) - acl_new = acl_dup(prev_acl[ACCESS_ACL]); - else - acl_new = acl_dup(prev_acl[DEFAULT_ACL]); + acl_new = acl_dup(*prev_acl); if (acl_new == NULL) - err(1, "acl_dup() failed"); + err(1, "%s: acl_dup() failed", filename); entry_id = ACL_FIRST_ENTRY; @@ -94,28 +120,45 @@ merge_acl(acl_t acl, acl_t *prev_acl) /* keep track of existing ACL_MASK entries */ if (acl_get_tag_type(entry, &tag) == -1) - err(1, "acl_get_tag_type() failed - invalid ACL entry"); + err(1, "%s: acl_get_tag_type() failed - " + "invalid ACL entry", filename); if (tag == ACL_MASK) have_mask = 1; /* check against the existing ACL entries */ entry_id_new = ACL_FIRST_ENTRY; - while (have_entry == 0 && - acl_get_entry(acl_new, entry_id_new, &entry_new) == 1) { + while (acl_get_entry(acl_new, entry_id_new, &entry_new) == 1) { entry_id_new = ACL_NEXT_ENTRY; if (acl_get_tag_type(entry, &tag) == -1) - err(1, "acl_get_tag_type() failed"); + err(1, "%s: acl_get_tag_type() failed", + filename); if (acl_get_tag_type(entry_new, &tag_new) == -1) - err(1, "acl_get_tag_type() failed"); + err(1, "%s: acl_get_tag_type() failed", + filename); if (tag != tag_new) continue; + /* + * For NFSv4, in addition to "tag" and "id" we also + * compare "entry_type". + */ + if (acl_brand == ACL_BRAND_NFS4) { + if (acl_get_entry_type_np(entry, &entry_type)) + err(1, "%s: acl_get_entry_type_np() " + "failed", filename); + if (acl_get_entry_type_np(entry_new, &entry_type_new)) + err(1, "%s: acl_get_entry_type_np() " + "failed", filename); + if (entry_type != entry_type_new) + continue; + } + switch(tag) { case ACL_USER: case ACL_GROUP: have_entry = merge_user_group(&entry, - &entry_new); + &entry_new, acl_brand); if (have_entry == 0) break; /* FALLTHROUGH */ @@ -123,37 +166,131 @@ merge_acl(acl_t acl, acl_t *prev_acl) case ACL_GROUP_OBJ: case ACL_OTHER: case ACL_MASK: + case ACL_EVERYONE: if (acl_get_permset(entry, &permset) == -1) - err(1, "acl_get_permset() failed"); + err(1, "%s: acl_get_permset() failed", + filename); if (acl_set_permset(entry_new, permset) == -1) - err(1, "acl_set_permset() failed"); + err(1, "%s: acl_set_permset() failed", + filename); + + if (acl_brand == ACL_BRAND_NFS4) { + if (acl_get_entry_type_np(entry, &entry_type)) + err(1, "%s: acl_get_entry_type_np() failed", + filename); + if (acl_set_entry_type_np(entry_new, entry_type)) + err(1, "%s: acl_set_entry_type_np() failed", + filename); + if (acl_get_flagset_np(entry, &flagset)) + err(1, "%s: acl_get_flagset_np() failed", + filename); + if (acl_set_flagset_np(entry_new, flagset)) + err(1, "%s: acl_set_flagset_np() failed", + filename); + } have_entry = 1; break; default: /* should never be here */ - errx(1, "Invalid tag type: %i", tag); + errx(1, "%s: invalid tag type: %i", filename, tag); break; } } /* if this entry has not been found, it must be new */ if (have_entry == 0) { - if (acl_create_entry(&acl_new, &entry_new) == -1) { - acl_free(acl_new); - return (-1); + + /* + * NFSv4 ACL entries must be prepended to the ACL. + * Appending them at the end makes no sense, since + * in most cases they wouldn't even get evaluated. + */ + if (acl_brand == ACL_BRAND_NFS4) { + if (acl_create_entry_np(&acl_new, &entry_new, entry_number) == -1) { + warn("%s: acl_create_entry_np() failed", filename); + acl_free(acl_new); + return (-1); + } + /* + * Without this increment, adding several + * entries at once, for example + * "setfacl -m user:1:r:allow,user:2:r:allow", + * would make them appear in reverse order. + */ + entry_number++; + } else { + if (acl_create_entry(&acl_new, &entry_new) == -1) { + warn("%s: acl_create_entry() failed", filename); + acl_free(acl_new); + return (-1); + } } if (acl_copy_entry(entry_new, entry) == -1) - err(1, "acl_copy_entry() failed"); + err(1, "%s: acl_copy_entry() failed", filename); } } - if (acl_type == ACL_TYPE_ACCESS) { - acl_free(prev_acl[ACCESS_ACL]); - prev_acl[ACCESS_ACL] = acl_new; - } else { - acl_free(prev_acl[DEFAULT_ACL]); - prev_acl[DEFAULT_ACL] = acl_new; + acl_free(*prev_acl); + *prev_acl = acl_new; + + return (0); +} + +int +add_acl(acl_t acl, uint entry_number, acl_t *prev_acl, const char *filename) +{ + acl_entry_t entry, entry_new; + acl_t acl_new; + int entry_id, acl_brand, prev_acl_brand; + + acl_get_brand_np(acl, &acl_brand); + acl_get_brand_np(*prev_acl, &prev_acl_brand); + + if (prev_acl_brand != ACL_BRAND_NFS4) { + warnx("%s: the '-a' option is only applicable to NFSv4 ACLs", + filename); + return (-1); + } + + if (acl_brand != ACL_BRAND_NFS4) { + warnx("%s: branding mismatch; existing ACL is NFSv4, " + "entry to be added is %s", filename, + acl_brand == ACL_BRAND_NFS4 ? "NFSv4" : "POSIX.1e"); + return (-1); + } + + acl_new = acl_dup(*prev_acl); + if (acl_new == NULL) + err(1, "%s: acl_dup() failed", filename); + + entry_id = ACL_FIRST_ENTRY; + + while (acl_get_entry(acl, entry_id, &entry) == 1) { + entry_id = ACL_NEXT_ENTRY; + + if (acl_create_entry_np(&acl_new, &entry_new, entry_number) == -1) { + if (entry_number >= acl_new->ats_acl.acl_cnt) + warnx("%s: invalid entry number", filename); + else + warn("%s: acl_create_entry_np() failed", filename); + acl_free(acl_new); + return (-1); + } + + /* + * Without this increment, adding several + * entries at once, for example + * "setfacl -m user:1:r:allow,user:2:r:allow", + * would make them appear in reverse order. + */ + entry_number++; + + if (acl_copy_entry(entry_new, entry) == -1) + err(1, "%s: acl_copy_entry() failed", filename); } + acl_free(*prev_acl); + *prev_acl = acl_new; + return (0); } diff -urNp current/bin/setfacl/remove.c nfs4acl/bin/setfacl/remove.c --- current/bin/setfacl/remove.c 2009-04-29 09:21:59.000000000 +0200 +++ nfs4acl/bin/setfacl/remove.c 2009-05-26 17:15:43.000000000 +0200 @@ -41,21 +41,31 @@ __FBSDID("$FreeBSD: src/bin/setfacl/remo * remove ACL entries from an ACL */ int -remove_acl(acl_t acl, acl_t *prev_acl) +remove_acl(acl_t acl, acl_t *prev_acl, const char *filename) { acl_entry_t entry; acl_t acl_new; acl_tag_t tag; - int carried_error, entry_id; + int carried_error, entry_id, acl_brand, prev_acl_brand; + + carried_error = 0; + + acl_get_brand_np(acl, &acl_brand); + acl_get_brand_np(*prev_acl, &prev_acl_brand); + + if (acl_brand != prev_acl_brand) { + warnx("%s: branding mismatch; existing ACL is %s, " + "entry to be removed is %s", filename, + prev_acl_brand == ACL_BRAND_NFS4 ? "NFSv4" : "POSIX.1e", + acl_brand == ACL_BRAND_NFS4 ? "NFSv4" : "POSIX.1e"); + return (-1); + } carried_error = 0; - if (acl_type == ACL_TYPE_ACCESS) - acl_new = acl_dup(prev_acl[ACCESS_ACL]); - else - acl_new = acl_dup(prev_acl[DEFAULT_ACL]); + acl_new = acl_dup(*prev_acl); if (acl_new == NULL) - err(1, "acl_dup() failed"); + err(1, "%s: acl_dup() failed", filename); tag = ACL_UNDEFINED_TAG; @@ -64,23 +74,72 @@ remove_acl(acl_t acl, acl_t *prev_acl) while (acl_get_entry(acl, entry_id, &entry) == 1) { entry_id = ACL_NEXT_ENTRY; if (acl_get_tag_type(entry, &tag) == -1) - err(1, "acl_get_tag_type() failed"); + err(1, "%s: acl_get_tag_type() failed", filename); if (tag == ACL_MASK) have_mask++; if (acl_delete_entry(acl_new, entry) == -1) { carried_error++; - warnx("cannot remove non-existent acl entry"); + warnx("%s: cannot remove non-existent ACL entry", + filename); } } - if (acl_type == ACL_TYPE_ACCESS) { - acl_free(prev_acl[ACCESS_ACL]); - prev_acl[ACCESS_ACL] = acl_new; - } else { - acl_free(prev_acl[DEFAULT_ACL]); - prev_acl[DEFAULT_ACL] = acl_new; + acl_free(*prev_acl); + *prev_acl = acl_new; + + if (carried_error) + return (-1); + + return (0); +} + +int +remove_by_number(uint entry_number, acl_t *prev_acl, const char *filename) +{ + acl_entry_t entry; + acl_t acl_new; + acl_tag_t tag; + int carried_error, entry_id; + uint i; + + carried_error = 0; + + acl_new = acl_dup(*prev_acl); + if (acl_new == NULL) + err(1, "%s: acl_dup() failed", filename); + + tag = ACL_UNDEFINED_TAG; + + /* + * Find out whether we're removing the mask entry, + * to behave the same as the routine above. + * + * XXX: Is this loop actually needed? + */ + entry_id = ACL_FIRST_ENTRY; + i = 0; + while (acl_get_entry(acl_new, entry_id, &entry) == 1) { + entry_id = ACL_NEXT_ENTRY; + if (i != entry_number) + continue; + if (acl_get_tag_type(entry, &tag) == -1) + err(1, "%s: acl_get_tag_type() failed", filename); + if (tag == ACL_MASK) + have_mask++; + } + + if (acl_delete_entry_np(acl_new, entry_number) == -1) { + carried_error++; + + if (entry_number >= (uint)acl_new->ats_acl.acl_cnt) + warnx("%s: invalid entry number", filename); + else + warn("%s: acl_delete_entry_np() failed", filename); } + acl_free(*prev_acl); + *prev_acl = acl_new; + if (carried_error) return (-1); @@ -91,18 +150,13 @@ remove_acl(acl_t acl, acl_t *prev_acl) * remove default entries */ int -remove_default(acl_t *prev_acl) +remove_default(acl_t *prev_acl, const char *filename) { + acl_free(*prev_acl); + *prev_acl = acl_init(ACL_MAX_ENTRIES); + if (*prev_acl == NULL) + err(1, "%s: acl_init() failed", filename); - if (prev_acl[1]) { - acl_free(prev_acl[1]); - prev_acl[1] = acl_init(ACL_MAX_ENTRIES); - if (prev_acl[1] == NULL) - err(1, "acl_init() failed"); - } else { - warn("cannot remove default ACL"); - return (-1); - } return (0); } @@ -110,71 +164,15 @@ remove_default(acl_t *prev_acl) * remove extended entries */ void -remove_ext(acl_t *prev_acl) +remove_ext(acl_t *prev_acl, const char *filename) { - acl_t acl_new, acl_old; - acl_entry_t entry, entry_new; - acl_permset_t perm; - acl_tag_t tag; - int entry_id, have_mask_entry; - - if (acl_type == ACL_TYPE_ACCESS) - acl_old = acl_dup(prev_acl[ACCESS_ACL]); - else - acl_old = acl_dup(prev_acl[DEFAULT_ACL]); - if (acl_old == NULL) - err(1, "acl_dup() failed"); + acl_t acl_new; - have_mask_entry = 0; - acl_new = acl_init(ACL_MAX_ENTRIES); + acl_new = acl_strip_np(*prev_acl, !n_flag); if (acl_new == NULL) - err(1, "acl_init() failed"); - tag = ACL_UNDEFINED_TAG; - - /* only save the default user/group/other entries */ - entry_id = ACL_FIRST_ENTRY; - while (acl_get_entry(acl_old, entry_id, &entry) == 1) { - entry_id = ACL_NEXT_ENTRY; - - if (acl_get_tag_type(entry, &tag) == -1) - err(1, "acl_get_tag_type() failed"); + err(1, "%s: acl_strip_np() failed", filename); - switch(tag) { - case ACL_USER_OBJ: - case ACL_GROUP_OBJ: - case ACL_OTHER: - if (acl_get_tag_type(entry, &tag) == -1) - err(1, "acl_get_tag_type() failed"); - if (acl_get_permset(entry, &perm) == -1) - err(1, "acl_get_permset() failed"); - if (acl_create_entry(&acl_new, &entry_new) == -1) - err(1, "acl_create_entry() failed"); - if (acl_set_tag_type(entry_new, tag) == -1) - err(1, "acl_set_tag_type() failed"); - if (acl_set_permset(entry_new, perm) == -1) - err(1, "acl_get_permset() failed"); - if (acl_copy_entry(entry_new, entry) == -1) - err(1, "acl_copy_entry() failed"); - break; - case ACL_MASK: - have_mask_entry = 1; - break; - default: - break; - } - } - if (have_mask_entry && n_flag == 0) { - if (acl_calc_mask(&acl_new) == -1) - err(1, "acl_calc_mask() failed"); - } else { - have_mask = 1; - } - - if (acl_type == ACL_TYPE_ACCESS) { - acl_free(prev_acl[ACCESS_ACL]); - prev_acl[ACCESS_ACL] = acl_new; - } else { - acl_free(prev_acl[DEFAULT_ACL]); - prev_acl[DEFAULT_ACL] = acl_new; - } + acl_free(*prev_acl); + *prev_acl = acl_new; } + diff -urNp current/bin/setfacl/setfacl.1 nfs4acl/bin/setfacl/setfacl.1 --- current/bin/setfacl/setfacl.1 2009-04-29 09:21:59.000000000 +0200 +++ nfs4acl/bin/setfacl/setfacl.1 2009-05-26 17:15:43.000000000 +0200 @@ -34,9 +34,10 @@ .Sh SYNOPSIS .Nm .Op Fl bdhkn +.Op Fl a Ar position entries .Op Fl m Ar entries .Op Fl M Ar file -.Op Fl x Ar entries +.Op Fl x Ar entries | position .Op Fl X Ar file .Op Ar .Sh DESCRIPTION @@ -50,9 +51,19 @@ the file names are taken from the standa .Pp The following options are available: .Bl -tag -width indent +.It Fl a Ar position entries +Modify the ACL on the specified files by inserting new +ACL entries +specified in +.Ar entries , +starting at position +.Ar position , +counting from zero. +This option is only applicable to NFSv4 ACLs. .It Fl b -Remove all ACL entries except for the three required entries. -If the ACL contains a +Remove all ACL entries except for the three required entries +(POSIX.1e ACLs) or six "canonical" entries (NFSv4 ACLs). +If the POSIX.1e ACL contains a .Dq Li mask entry, the permissions of the .Dq Li group @@ -66,7 +77,7 @@ entries of the current ACL. The operations apply to the default ACL entries instead of access ACL entries. Currently only directories may have -default ACL's. +default ACL's. This option is not applicable to NFSv4 ACLs. .It Fl h If the target of the operation is a symbolic link, perform the operation on the symbolic link itself, rather than following the link. @@ -77,7 +88,7 @@ is not considered an error if the specif any default ACL entries. An error will be reported if any of the specified files cannot have a default entry (i.e.\& -non-directories). +non-directories). This option is not applicable to NFSv4 ACLs. .It Fl m Ar entries Modify the ACL entries on the specified files by adding new entries and modifying existing ACL entries with the ACL entries @@ -95,11 +106,15 @@ is the input is taken from stdin. .It Fl n Do not recalculate the permissions associated with the ACL -mask entry. -.It Fl x Ar entries -Remove the ACL entries specified in +mask entry. This option is not applicable to NFSv4 ACLs. +.It Fl x Ar entries | position +If .Ar entries +is specified, remove the ACL entries specified there from the access or default ACL of the specified files. +Otherwise, remove entry at index +.Ar position , +counting from zero. .It Fl X Ar file Remove the ACL entries specified in the file .Ar file @@ -108,8 +123,8 @@ from the access or default ACL of the sp .Pp The above options are evaluated in the order specified on the command-line. -.Sh ACL ENTRIES -An ACL entry contains three colon-separated fields: +.Sh POSIX.1e ACL ENTRIES +A POSIX.1E ACL entry contains three colon-separated fields: an ACL tag, an ACL qualifier, and discretionary access permissions: .Bl -tag -width indent @@ -223,7 +238,7 @@ previously specified; whitespace is igno .Ql # is ignored (comments). .Pp -When ACL entries are evaluated, the access check algorithm checks +When POSIX.1e ACL entries are evaluated, the access check algorithm checks the ACL entries in the following order: file owner, .Dq Li user ACL entries, file owning group, @@ -243,13 +258,110 @@ ACL entries for user, group, other and m For more details see the examples below. Default ACLs can be created by using .Fl d . +.Sh NFSv4 ACL ENTRIES +An NFSv4 ACL entry contains four or five colon-separated fields: an ACL tag, +an ACL qualifier (only for +.Dq Li user +and +.Dq Li group +tags), discretionary access permissions, ACL inheritance flags, and ACL type: +.Bl -tag -width indent +.It Ar "ACL tag" +The ACL tag specifies the ACL entry type and consists of +one of the following: +.Dq Li user +or +.Ql u +specifying the access +granted to the specified user; +.Dq Li group +or +.Ql g +specifying the access granted to the specified group; +.Dq Li owner@ +specifying the access granted to the owner of the file; +.Dq Li group@ +specifying the access granted to the file owning group; +.Dq Li everyone@ +specifying everyone. Note that +.Dq Li everyone@ +is not the same as traditional Unix +.Dq Li other +- it means, +literally, everyone, including file owner and owning group. +.It Ar "ACL qualifier" +The ACL qualifier field describes the user or group associated with +the ACL entry. +It may consist of one of the following: uid or +user name, or gid or group name. In entries whose tag type is +one of +.Dq Li owner@ , +.Dq Li group@ , +or +.Dq Li everyone@ , +this field is ommited altogether, including the trailing comma. +.It Ar "access permissions" +The access permissions field contains up to one of each of +the following: +.Ql r , +.Ql w , +.Ql x , +.Ql p , +.Ql d , +.Ql D , +.Ql a , +.Ql A , +.Ql R , +.Ql W , +.Ql c , +.Ql C , +.Ql o , +and +.Ql S +to set read_data, write_data, execute, append_data, delete_child, delete, +read_attributes, write_attributes, read_xattr, write_xattr, read_acl, write_acl, +write_owner, and synchronize permissions, respectively. This section obviously +needs to be rewritten. +Each of these may be excluded +or replaced with a +.Ql - +character to indicate no access. +.It Ar "ACL inheritance flags" +The ACL inheritance flags field contains up to one of each of +the following: +.Ql f , +.Ql d , +.Ql i , +.Ql n , +to set file_inherit, dir_inherit, inherit_only, and no_propagate flags, +respectively. Inheritance flags may be only set on directories. +.It Ar "ACL type" +The ACL type field is either +.Dq Li allow +or +.Dq Li deny . +.El +.Pp +ACL entries applied from a file using the +.Fl M +or +.Fl X +options shall be of the following form: one ACL entry per line, as +previously specified; whitespace is ignored; any text after a +.Ql # +is ignored (comments). +.Pp +NFSv4 ACL entries are evaluated in their visible order. +.Pp +Multiple ACL entries specified on the command line are +separated by commas. .Sh EXIT STATUS .Ex -std .Sh EXAMPLES .Dl setfacl -d -m u::rwx,g::rx,o::rx,mask::rwx dir .Dl setfacl -d -m g:admins:rwx dir .Pp -The first command sets the mandatory elements of the default ACL. +The first command sets the mandatory elements of the POSIX.1e default ACL. The second command specifies that users in group admins can have read, write, and execute permissions for directory named "dir". It should be noted that any files or directories created underneath "dir" will @@ -259,9 +371,13 @@ inherit these default ACLs upon creation .Pp Sets read, write, and execute permissions for the .Pa file -owner's ACL entry and read and write permissions for group mail on +owner's POSIX.1e ACL entry and read and write permissions for group mail on .Pa file . .Pp +.Dl setfacl -m owner@:rwxp::allow,g:mail:rwp::allow file +.Pp +Semantically equal to the example above, but for NFSv4 ACL. +.Pp .Dl setfacl -M file1 file2 .Pp Sets/updates the ACL entries contained in @@ -271,10 +387,15 @@ on .Pp .Dl setfacl -x g:mail:rw file .Pp -Remove the group mail ACL entry containing read/write permissions +Remove the group mail POSIX.1e ACL entry containing read/write permissions from .Pa file . .Pp +.Dl setfacl -x0 file +.Pp +Remove the first entry from the NFSv4 ACL from +.Pa file . +.Pp .Dl setfacl -bn file .Pp Remove all diff -urNp current/bin/setfacl/setfacl.c nfs4acl/bin/setfacl/setfacl.c --- current/bin/setfacl/setfacl.c 2009-04-29 09:21:59.000000000 +0200 +++ nfs4acl/bin/setfacl/setfacl.c 2009-05-26 17:15:43.000000000 +0200 @@ -41,9 +41,9 @@ __FBSDID("$FreeBSD: src/bin/setfacl/setf #include "setfacl.h" -static void add_filename(const char *filename); -static acl_t *get_file_acls(const char *filename); -static void usage(void); +static void add_filename(const char *filename); +static acl_t get_file_acl(const char *filename, acl_type_t type, int h_flag); +static void usage(void); static void add_filename(const char *filename) @@ -59,35 +59,48 @@ add_filename(const char *filename) TAILQ_INSERT_TAIL(&filelist, file, next); } -static acl_t * -get_file_acls(const char *filename) +static acl_t +get_file_acl(const char *filename, acl_type_t type, int h_flag) { - acl_t *acl; + acl_t acl = NULL; struct stat sb; - if (stat(filename, &sb) == -1) { - warn("stat() of %s failed", filename); - return (NULL); + switch (type) { + case ACL_TYPE_ACCESS: + case ACL_TYPE_NFS4: + if (h_flag) + acl = acl_get_link_np(filename, type); + else + acl = acl_get_file(filename, type); + break; + + case ACL_TYPE_DEFAULT: + if (stat(filename, &sb) == -1) { + warn("%s: stat() failed", filename); + return (NULL); + } + + if (S_ISDIR(sb.st_mode) == 0) { + warnx("%s: default ACL may only be set on a directory", + filename); + return (NULL); + } + + if (h_flag) + acl = acl_get_link_np(filename, ACL_TYPE_DEFAULT); + else + acl = acl_get_file(filename, ACL_TYPE_DEFAULT); + break; } - acl = zmalloc(sizeof(acl_t) * 2); - if (h_flag) - acl[ACCESS_ACL] = acl_get_link_np(filename, ACL_TYPE_ACCESS); - else - acl[ACCESS_ACL] = acl_get_file(filename, ACL_TYPE_ACCESS); - if (acl[ACCESS_ACL] == NULL) - err(1, "acl_get_file() failed"); - if (S_ISDIR(sb.st_mode)) { + if (acl == NULL) { if (h_flag) - acl[DEFAULT_ACL] = acl_get_link_np(filename, - ACL_TYPE_DEFAULT); + warn("%s: acl_get_link_np() failed", filename); else - acl[DEFAULT_ACL] = acl_get_file(filename, - ACL_TYPE_DEFAULT); - if (acl[DEFAULT_ACL] == NULL) - err(1, "acl_get_file() failed"); - } else - acl[DEFAULT_ACL] = NULL; + warn("%s: acl_get_file() failed", filename); + + return (NULL); + } return (acl); } @@ -96,20 +109,23 @@ static void usage(void) { - fprintf(stderr, "usage: setfacl [-bdhkn] [-m entries] [-M file] " - "[-x entries] [-X file] [file ...]\n"); + fprintf(stderr, "usage: setfacl [-bdhkn] [-a position entries] " + "[-m entries] [-M file] [-x entries] [-X file] [file ...]\n"); exit(1); } int main(int argc, char *argv[]) { - acl_t *acl, final_acl; + acl_t acl; + acl_type_t acl_type; char filename[PATH_MAX]; - int local_error, carried_error, ch, i; + int local_error, carried_error, ch, i, entry_number; + int h_flag; struct sf_file *file; struct sf_entry *entry; const char *fn_dup; + char *end; acl_type = ACL_TYPE_ACCESS; carried_error = local_error = 0; @@ -118,13 +134,13 @@ main(int argc, char *argv[]) TAILQ_INIT(&entrylist); TAILQ_INIT(&filelist); - while ((ch = getopt(argc, argv, "M:X:bdhkm:nx:")) != -1) + while ((ch = getopt(argc, argv, "M:X:a:bdhkm:nx:")) != -1) switch(ch) { case 'M': entry = zmalloc(sizeof(struct sf_entry)); entry->acl = get_acl_from_file(optarg); if (entry->acl == NULL) - err(1, "get_acl_from_file() failed"); + err(1, "%s: get_acl_from_file() failed", optarg); entry->op = OP_MERGE_ACL; TAILQ_INSERT_TAIL(&entrylist, entry, next); break; @@ -134,6 +150,25 @@ main(int argc, char *argv[]) entry->op = OP_REMOVE_ACL; TAILQ_INSERT_TAIL(&entrylist, entry, next); break; + case 'a': + entry = zmalloc(sizeof(struct sf_entry)); + + entry_number = strtol(optarg, &end, 10); + if (end - optarg != (int)strlen(optarg)) + errx(1, "%s: invalid entry number", optarg); + if (entry_number < 0) + errx(1, "%s: entry number cannot be less than zero", optarg); + entry->entry_number = entry_number; + + if (argv[optind] == NULL) + errx(1, "missing ACL"); + entry->acl = acl_from_text(argv[optind]); + if (entry->acl == NULL) + err(1, "%s", argv[optind]); + optind++; + entry->op = OP_ADD_ACL; + TAILQ_INSERT_TAIL(&entrylist, entry, next); + break; case 'b': entry = zmalloc(sizeof(struct sf_entry)); entry->op = OP_REMOVE_EXT; @@ -163,10 +198,18 @@ main(int argc, char *argv[]) break; case 'x': entry = zmalloc(sizeof(struct sf_entry)); - entry->acl = acl_from_text(optarg); - if (entry->acl == NULL) - err(1, "%s", optarg); - entry->op = OP_REMOVE_ACL; + entry_number = strtol(optarg, &end, 10); + if (end - optarg == (int)strlen(optarg)) { + if (entry_number < 0) + errx(1, "%s: entry number cannot be less than zero", optarg); + entry->entry_number = entry_number; + entry->op = OP_REMOVE_BY_NUMBER; + } else { + entry->acl = acl_from_text(optarg); + if (entry->acl == NULL) + err(1, "%s", optarg); + entry->op = OP_REMOVE_ACL; + } TAILQ_INSERT_TAIL(&entrylist, entry, next); break; default: @@ -199,16 +242,23 @@ main(int argc, char *argv[]) /* cycle through each file */ TAILQ_FOREACH(file, &filelist, next) { - /* get our initial access and default ACL's */ - acl = get_file_acls(file->filename); + local_error = 0; + + if (pathconf(file->filename, _PC_EXTENDED_SECURITY_NP)) { + if (acl_type == ACL_TYPE_DEFAULT) { + warnx("%s: there are no default entries " + "in NFSv4 ACLs", file->filename); + continue; + } + + acl_type = ACL_TYPE_NFS4; + + } else if (acl_type == ACL_TYPE_NFS4) + acl_type = ACL_TYPE_ACCESS; + + acl = get_file_acl(file->filename, acl_type, h_flag); if (acl == NULL) continue; - if ((acl_type == ACL_TYPE_DEFAULT) && !acl[1]) { - warnx("Default ACL not valid for %s", file->filename); - continue; - } - - local_error = 0; /* cycle through each option */ TAILQ_FOREACH(entry, &entrylist, next) { @@ -216,24 +266,44 @@ main(int argc, char *argv[]) continue; switch(entry->op) { + case OP_ADD_ACL: + local_error += add_acl(entry->acl, + entry->entry_number, &acl, file->filename); + break; case OP_MERGE_ACL: - local_error += merge_acl(entry->acl, acl); + local_error += merge_acl(entry->acl, &acl, + file->filename); need_mask = 1; break; case OP_REMOVE_EXT: - remove_ext(acl); + remove_ext(&acl, file->filename); need_mask = 0; break; case OP_REMOVE_DEF: + if (acl_type == ACL_TYPE_NFS4) { + warnx("%s: there are no default entries in NFSv4 ACLs; " + "cannot remove", file->filename); + local_error++; + break; + } if (acl_delete_def_file(file->filename) == -1) { - warn("acl_delete_def_file() failed"); + warn("%s: acl_delete_def_file() failed", + file->filename); local_error++; } - local_error += remove_default(acl); + if (acl_type == ACL_TYPE_DEFAULT) + local_error += remove_default(&acl, + file->filename); need_mask = 0; break; case OP_REMOVE_ACL: - local_error += remove_acl(entry->acl, acl); + local_error += remove_acl(entry->acl, &acl, + file->filename); + need_mask = 1; + break; + case OP_REMOVE_BY_NUMBER: + local_error += remove_by_number(entry->entry_number, + &acl, file->filename); need_mask = 1; break; } @@ -245,36 +315,32 @@ main(int argc, char *argv[]) continue; } - if (acl_type == ACL_TYPE_ACCESS) { - final_acl = acl[ACCESS_ACL]; - acl_free(acl[DEFAULT_ACL]); - } else { - final_acl = acl[DEFAULT_ACL]; - acl_free(acl[ACCESS_ACL]); - } + if (acl_type == ACL_TYPE_NFS4) + need_mask = 0; - if (need_mask && (set_acl_mask(&final_acl) == -1)) { - warnx("failed to set ACL mask on %s", file->filename); + if (need_mask && (set_acl_mask(&acl, + file->filename) == -1)) { + warnx("%s: failed to set ACL mask", file->filename); carried_error++; } else if (h_flag) { if (acl_set_link_np(file->filename, acl_type, - final_acl) == -1) { + acl) == -1) { carried_error++; - warn("acl_set_link_np() failed for %s", + warn("%s: acl_set_link_np() failed", file->filename); } } else { if (acl_set_file(file->filename, acl_type, - final_acl) == -1) { + acl) == -1) { carried_error++; - warn("acl_set_file() failed for %s", + warn("%s: acl_set_file() failed", file->filename); } } - acl_free(final_acl); - free(acl); + acl_free(acl); } return (carried_error); } + diff -urNp current/bin/setfacl/setfacl.h nfs4acl/bin/setfacl/setfacl.h --- current/bin/setfacl/setfacl.h 2009-04-29 09:21:59.000000000 +0200 +++ nfs4acl/bin/setfacl/setfacl.h 2009-04-29 09:30:29.000000000 +0200 @@ -38,15 +38,14 @@ #define OP_REMOVE_DEF 0x01 /* remove default acl's (-k) */ #define OP_REMOVE_EXT 0x02 /* remove extended acl's (-b) */ #define OP_REMOVE_ACL 0x03 /* remove acl's (-xX) */ - -/* ACL types for the acl array */ -#define ACCESS_ACL 0 -#define DEFAULT_ACL 1 +#define OP_REMOVE_BY_NUMBER 0x04 /* remove acl's (-xX) by acl entry number */ +#define OP_ADD_ACL 0x05 /* add acls entries at a given position */ /* TAILQ entry for acl operations */ struct sf_entry { uint op; acl_t acl; + uint entry_number; TAILQ_ENTRY(sf_entry) next; }; TAILQ_HEAD(, sf_entry) entrylist; @@ -61,21 +60,21 @@ TAILQ_HEAD(, sf_file) filelist; /* files.c */ acl_t get_acl_from_file(const char *filename); /* merge.c */ -int merge_acl(acl_t acl, acl_t *prev_acl); +int merge_acl(acl_t acl, acl_t *prev_acl, const char *filename); +int add_acl(acl_t acl, uint entry_number, acl_t *prev_acl, const char *filename); /* remove.c */ -int remove_acl(acl_t acl, acl_t *prev_acl); -int remove_default(acl_t *prev_acl); -void remove_ext(acl_t *prev_acl); +int remove_acl(acl_t acl, acl_t *prev_acl, const char *filename); +int remove_by_number(uint entry_number, acl_t *prev_acl, const char *filename); +int remove_default(acl_t *prev_acl, const char *filename); +void remove_ext(acl_t *prev_acl, const char *filename); /* mask.c */ -int set_acl_mask(acl_t *prev_acl); +int set_acl_mask(acl_t *prev_acl, const char *filename); /* util.c */ void *zmalloc(size_t size); -acl_type_t acl_type; uint have_mask; uint need_mask; uint have_stdin; -uint h_flag; uint n_flag; #endif /* _SETFACL_H */ diff -urNp current/lib/libc/posix1e/Makefile.inc nfs4acl/lib/libc/posix1e/Makefile.inc --- current/lib/libc/posix1e/Makefile.inc 2009-05-23 07:49:39.000000000 +0200 +++ nfs4acl/lib/libc/posix1e/Makefile.inc 2009-05-23 08:37:49.000000000 +0200 @@ -4,52 +4,74 @@ CFLAGS+=-D_ACL_PRIVATE -SRCS+= acl_calc_mask.c \ +# Copy kern/subr_acl_nfs4.c to the libc object directory. +subr_acl_nfs4.c: ${.CURDIR}/../../sys/kern/subr_acl_nfs4.c + cat ${.ALLSRC} > ${.TARGET} + +SRCS+= acl_branding.c \ + acl_calc_mask.c \ acl_copy.c \ acl_compat.c \ acl_delete.c \ acl_delete_entry.c \ acl_entry.c \ + acl_flag.c \ acl_free.c \ acl_from_text.c \ + acl_from_text_nfs4.c \ acl_get.c \ acl_init.c \ acl_perm.c \ acl_set.c \ + acl_strip.c \ acl_support.c \ + acl_support_nfs4.c \ acl_to_text.c \ + acl_to_text_nfs4.c \ acl_valid.c \ extattr.c \ mac.c \ mac_exec.c \ mac_get.c \ - mac_set.c + mac_set.c \ + subr_acl_nfs4.c SYM_MAPS+=${.CURDIR}/posix1e/Symbol.map MAN+= acl.3 \ + acl_add_flag_np.3 \ acl_add_perm.3 \ acl_calc_mask.3 \ + acl_clear_flags_np.3 \ acl_clear_perms.3 \ acl_copy_entry.3 \ acl_create_entry.3 \ acl_delete.3 \ acl_delete_entry.3 \ + acl_delete_flag_np.3 \ acl_delete_perm.3 \ acl_dup.3 \ acl_free.3 \ acl_from_text.3 \ acl_get.3 \ + acl_get_brand_np.3 \ acl_get_entry.3 \ + acl_get_entry_type_np.3 \ + acl_get_flagset_np.3 \ + acl_get_flag_np.3 \ acl_get_permset.3 \ acl_get_perm_np.3 \ acl_get_qualifier.3 \ acl_get_tag_type.3 \ acl_init.3 \ + acl_is_trivial_np.3 \ acl_set.3 \ + acl_set_entry_type_np.3 \ + acl_set_flagset_np.3 \ acl_set_permset.3 \ acl_set_qualifier.3 \ acl_set_tag_type.3 \ + acl_strip_np.3 \ acl_to_text.3 \ acl_valid.3 \ extattr.3 \ @@ -63,15 +85,18 @@ MAN+= acl.3 \ mac_text.3 \ posix1e.3 -MLINKS+=acl_delete.3 acl_delete_def_file.3 \ +MLINKS+=acl_create_entry.3 acl_create_entry_np.3\ + acl_delete.3 acl_delete_def_file.3 \ acl_delete.3 acl_delete_file_np.3 \ acl_delete.3 acl_delete_fd_np.3 \ + acl_delete_entry.3 acl_delete_entry_np.3\ acl_get.3 acl_get_file.3 \ acl_get.3 acl_get_fd.3 \ acl_get.3 acl_get_fd_np.3 \ acl_set.3 acl_set_file.3 \ acl_set.3 acl_set_fd.3 \ acl_set.3 acl_set_fd_np.3 \ + acl_to_text.3 acl_to_text_np.3 \ acl_valid.3 acl_valid_file_np.3 \ acl_valid.3 acl_valid_fd_np.3 \ extattr.3 extattr_namespace_to_string.3 \ diff -urNp current/lib/libc/posix1e/Symbol.map nfs4acl/lib/libc/posix1e/Symbol.map --- current/lib/libc/posix1e/Symbol.map 2009-05-23 07:49:39.000000000 +0200 +++ nfs4acl/lib/libc/posix1e/Symbol.map 2009-05-25 19:14:28.000000000 +0200 @@ -66,7 +66,21 @@ FBSD_1.0 { }; FBSD_1.1 { + acl_add_flag_np; acl_add_perm; + acl_clear_flags_np; + acl_create_entry_np; + acl_delete_entry_np; + acl_delete_flag_np; acl_delete_perm; + acl_get_brand_np; + acl_get_entry_type_np; + acl_get_flag_np; + acl_get_flagset_np; acl_get_perm_np; + acl_is_trivial_np; + acl_set_entry_type_np; + acl_set_flagset_np; + acl_strip_np; + acl_to_text_np; }; diff -urNp current/lib/libc/posix1e/acl.3 nfs4acl/lib/libc/posix1e/acl.3 --- current/lib/libc/posix1e/acl.3 2009-05-23 17:12:25.000000000 +0200 +++ nfs4acl/lib/libc/posix1e/acl.3 2009-05-23 17:18:23.000000000 +0200 @@ -59,6 +59,10 @@ all of these support routines are implem .Pp Available functions, sorted by behavior, include: .Bl -tag -width indent +.It Fn acl_add_flag_np +This function is described in +.Xr acl_add_flag_np 3 , +and may be used to add flags to a flagset. .It Fn acl_add_perm This function is described in .Xr acl_add_perm 3 , @@ -70,6 +74,10 @@ and may be used to calculate and set the the .Dv ACL_MASK entry. +.It Fn acl_clear_flags_np +This function is described in +.Xr acl_clear_flags_np 3 , +and may be used to clear all flags from a flagset. .It Fn acl_clear_perms This function is described in .Xr acl_clear_perms 3 , @@ -78,8 +86,11 @@ and may be used to clear all permissions This function is described in .Xr acl_copy_entry 3 , and may be used to copy the contents of an ACL entry. -.It Fn acl_create_entry -This function is described in +.It Xo +.Fn acl_create_entry , +.Fn acl_create_entry_np +.Xc +These functions are described in .Xr acl_create_entry 3 , and may be used to create an empty entry in an ACL. .It Xo @@ -92,10 +103,17 @@ and may be used to create an empty entry These functions are described in .Xr acl_delete 3 , and may be used to delete ACLs from file system objects. -.It Fn acl_delete_entry -This function is described in +.It Xo +.Fn acl_delete_entry , +.Fn acl_delete_entry_np , +.Xc +This functions are described in .Xr acl_delete_entry 3 , and may be used to delete an entry from an ACL. +.It Fn acl_delete_flag_np +This function is described in +.Xr acl_delete_flag_np 3 , +and may be used to delete flags from a flagset. .It Fn acl_delete_perm This function is described in .Xr acl_delete_perm 3 , @@ -126,6 +144,14 @@ and may be used to retrieve a designated These functions are described in .Xr acl_get 3 , and may be used to retrieve ACLs from file system objects. +.It Fn acl_get_entry_type_np +This function is described in +.Xr acl_get_entry_type_np 3 , +and may be used to retrieve an ACL type from an ACL entry. +.It Fn acl_get_flagset_np +This function is described in +.Xr acl_get_flagset_np 3 , +and may be used to retrieve a flagset from an ACL entry. .It Fn acl_get_permset This function is described in .Xr acl_get_permset 3 , @@ -142,6 +168,10 @@ and may be used to retrieve the tag type This function is described in .Xr acl_init 3 , and may be used to allocate a fresh (empty) ACL structure. +.It Fn acl_is_trivial_np +This function is described in +.Xr acl_is_trivial_np 3 , +and may be used to find out whether ACL is trivial. .It Xo .Fn acl_set_fd , .Fn acl_set_fd_np , @@ -151,6 +181,14 @@ and may be used to allocate a fresh (emp These functions are described in .Xr acl_set 3 , and may be used to assign an ACL to a file system object. +.It Fn acl_set_entry_type_np +This function is described in +.Xr acl_set_entry_type_np 3 , +and may be used to set the ACL type of an ACL entry. +.It Fn acl_set_flagset_np +This function is described in +.Xr acl_set_flagset_np 3 , +and may be used to set the flags of an ACL entry from a flagset. .It Fn acl_set_permset This function is described in .Xr acl_set_permset 3 , @@ -163,8 +201,15 @@ and may be used to set the qualifier of This function is described in .Xr acl_set_tag_type 3 , and may be used to set the tag type of an ACL. -.It Fn acl_to_text -This function is described in +.It Fn acl_strip_np +This function is describe din +.Xr acl-strip_np 3 , +and may be used to remove extended entries from an ACL. +.It Xo +.Fn acl_to_text , +.Fn acl_to_text_np +.Xc +These functions are described in .Xr acl_to_text 3 , and may be used to generate a text-form of a POSIX.1e semantics ACL. .It Xo @@ -189,25 +234,34 @@ library. .Sh SEE ALSO .Xr getfacl 1 , .Xr setfacl 1 , +.Xr acl_add_flag_np 3 , .Xr acl_add_perm 3 , .Xr acl_calc_mask 3 , +.Xr acl_clear_flags_np 3 , .Xr acl_clear_perms 3 , .Xr acl_copy_entry 3 , .Xr acl_create_entry 3 , .Xr acl_delete_entry 3 , +.Xr acl_delete_flag_np 3 , .Xr acl_delete_perm 3 , .Xr acl_dup 3 , .Xr acl_free 3 , .Xr acl_from_text 3 , .Xr acl_get 3 , +.Xr acl_get_entry_type_np 3 , +.Xr acl_get_flagset_np 3 , .Xr acl_get_permset 3 , .Xr acl_get_qualifier 3 , .Xr acl_get_tag_type 3 , .Xr acl_init 3 , +.Xr acl_is_trivial_np 3 , .Xr acl_set 3 , +.Xr acl_set_entry_type_np 3 , +.Xr acl_set_flagset_np 3 , .Xr acl_set_permset 3 , .Xr acl_set_qualifier 3 , .Xr acl_set_tag_type 3 , +.Xr acl_strip_np 3 , .Xr acl_to_text 3 , .Xr acl_valid 3 , .Xr posix1e 3 , diff -urNp current/lib/libc/posix1e/acl_add_flag_np.3 nfs4acl/lib/libc/posix1e/acl_add_flag_np.3 --- current/lib/libc/posix1e/acl_add_flag_np.3 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_add_flag_np.3 2009-05-24 21:07:38.000000000 +0200 @@ -0,0 +1,98 @@ +.\"- +.\" Copyright (c) 2008, 2009 Edward Tomasz Napierala +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR THE VOICES IN HIS HEAD BE +.\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd March 10, 2001 +.Dt ACL_ADD_FLAG_NP 3 +.Os +.Sh NAME +.Nm acl_add_flag_np +.Nd add flags to a flagset +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In sys/types.h +.In sys/acl.h +.Ft int +.Fn acl_add_flag_np "acl_flagset_t flagset_d" "acl_flag_t flag" +.Sh DESCRIPTION +The +.Fn acl_add_flag_np +function +is a non-portable call that adds the flags contained in +.Fa flags +to the flagset +.Fa flagset_d . +.Pp +Note: it is not considered an error to attempt to add flags +that already exist in the flagset. +.Pp +Valid values are: +.Pp +.Bl -column -offset 3n "ACL_ENTRY_NO_PROPAGATE_INHERIT" +.It ACL_ENTRY_FILE_INHERIT Will be inherited by files. +.It ACL_ENTRY_DIRECTORY_INHERIT Will be inherited by directories. +.It ACL_ENTRY_NO_PROPAGATE_INHERIT Will not propagate. +.It ACL_ENTRY_INHERIT_ONLY Inherit-only. +.El +.Sh RETURN VALUES +.Rv -std acl_add_flag_np +.Sh ERRORS +The +.Fn acl_add_flag_np +function fails if: +.Bl -tag -width Er +.It Bq Er EINVAL +Argument +.Fa flagset_d +is not a valid descriptor for a flagset within an ACL entry. +Argument +.Fa flag +does not contain a valid +.Vt acl_flag_t +value. +.El +.Sh SEE ALSO +.Xr acl 3 , +.Xr acl_clear_flags_np 3 , +.Xr acl_delete_flag_np 3 , +.Xr acl_get_flagset_np 3 , +.Xr acl_set_flagset_np 3 , +.Xr posix1e 3 +.Sh STANDARDS +POSIX.1e is described in IEEE POSIX.1e draft 17. +.Sh HISTORY +POSIX.1e support was introduced in +.Fx 4.0 . +The +.Fn acl_add_flag_np +function was added in +.Fx 8.0 . +.Sh AUTHORS +The +.Fn acl_add_flag_np +function was written by +.An Edward Tomasz Napierala Aq trasz@FreeBSD.org . diff -urNp current/lib/libc/posix1e/acl_add_perm.3 nfs4acl/lib/libc/posix1e/acl_add_perm.3 --- current/lib/libc/posix1e/acl_add_perm.3 2009-05-23 17:12:25.000000000 +0200 +++ nfs4acl/lib/libc/posix1e/acl_add_perm.3 2009-05-26 17:15:43.000000000 +0200 @@ -52,11 +52,43 @@ that already exist in the permission set .Pp For POSIX.1e ACLs, valid values are: .Pp -.Bl -column -offset 3n "ACL_EXECUTE" +.Bl -column -offset 3n "ACL_WRITE_NAMED_ATTRS" .It ACL_EXECUTE Execute permission .It ACL_WRITE Write permission .It ACL_READ Read permission .El +.Pp +For NFSv4 ACLs, valid values are: +.Pp +.Bl -column -offset 3n "ACL_WRITE_NAMED_ATTRS" +.It ACL_READ_DATA Read permission +.It ACL_LIST_DIRECTORY Same as ACL_READ_DATA +.It ACL_WRITE_DATA Write permission, or permission to create files +.It ACL_ADD_FILE Same as ACL_READ_DATA +.It ACL_APPEND_DATA Permission to create directories. Ignored for files +.It ACL_ADD_SUBDIRECTORY Same as ACL_APPEND_DATA +.It ACL_READ_NAMED_ATTRS Ignored +.It ACL_WRITE_NAMED_ATTRS Ignored +.It ACL_EXECUTE Execute permission +.It ACL_DELETE_CHILD Permission to delete files and subdirectories +.It ACL_READ_ATTRIBUTES Permission to read basic attributes +.It ACL_WRITE_ATTRIBUTES Permission to change basic attributes +.It ACL_DELETE Permission to delete the object this ACL is placed on +.It ACL_READ_ACL Permission to read ACL +.It ACL_WRITE_ACL Permission to change the ACL and file mode +.It ACL_SYNCHRONIZE Ignored +.El +.Pp +Calling +.Fn acl_add_perm +with +.Fa perm +equal to ACL_WRITE or ACL_READ brands the ACL as POSIX. +Calling it with ACL_READ_DATA, ACL_LIST_DIRECTORY, ACL_WRITE_DATA, +ACL_ADD_FILE, ACL_APPEND_DATA, ACL_ADD_SUBDIRECTORY, ACL_READ_NAMED_ATTRS, +ACL_WRITE_NAMED_ATTRS, ACL_DELETE_CHILD, ACL_READ_ATTRIBUTES, +ACL_WRITE_ATTRIBUTES, ACL_DELETE, ACL_READ_ACL, ACL_WRITE_ACL +or ACL_SYNCHRONIZE brands the ACL as NFSv4. .Sh RETURN VALUES .Rv -std acl_add_perm .Sh ERRORS @@ -73,11 +105,13 @@ Argument does not contain a valid .Vt acl_perm_t value. +ACL is already branded differently. .El .Sh SEE ALSO .Xr acl 3 , .Xr acl_clear_perms 3 , .Xr acl_delete_perm 3 , +.Xr acl_get_brand_np 3 , .Xr acl_get_permset 3 , .Xr acl_set_permset 3 , .Xr posix1e 3 diff -urNp current/lib/libc/posix1e/acl_branding.c nfs4acl/lib/libc/posix1e/acl_branding.c --- current/lib/libc/posix1e/acl_branding.c 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_branding.c 2009-05-26 18:53:59.000000000 +0200 @@ -0,0 +1,166 @@ +/*- + * Copyright (c) 2008, 2009 Edward Tomasz NapieraÅ‚a + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include "acl_support.h" + +/* + * An ugly detail of the implementation - fortunately not visible + * to the API users - is the "branding": libc needs to keep track + * of what "brand" ACL is: NFSv4, POSIX.1e or unknown. It happens + * automatically - for example, during acl_get_file(3) ACL gets + * branded according to the "type" argument; during acl_set_permset + * ACL, if its brand is unknown it gets branded as NFSv4 if any of the + * NFSv4 permissions that are not valid for POSIX.1e ACL are set etc. + * Branding information is used for printing out the ACL (acl_to_text(3)), + * veryfying acl_set_whatever arguments (checking against setting + * bits that are valid only for NFSv4 in ACL branded as POSIX.1e) etc. + */ + +static acl_t +entry2acl(acl_entry_t entry) +{ + acl_t aclp; + + aclp = (acl_t)(((long)entry >> _ACL_T_ALIGNMENT_BITS) << _ACL_T_ALIGNMENT_BITS); + + return (aclp); +} + +/* + * Return brand of an ACL. + */ +int +_acl_brand(const acl_t acl) +{ + + return (acl->ats_brand); +} + +int +_entry_brand(const acl_entry_t entry) +{ + + return (_acl_brand(entry2acl(entry))); +} + +/* + * Return 1, iff branding ACL as "brand" is ok. + */ +int +_acl_brand_may_be(const acl_t acl, int brand) +{ + + if (_acl_brand(acl) == ACL_BRAND_UNKNOWN) + return (1); + + if (_acl_brand(acl) == brand) + return (1); + + return (0); +} + +int +_entry_brand_may_be(const acl_entry_t entry, int brand) +{ + + return (_acl_brand_may_be(entry2acl(entry), brand)); +} + +/* + * Brand ACL as "brand". + */ +void +_acl_brand_as(acl_t acl, int brand) +{ + + assert(_acl_brand_may_be(acl, brand)); + + acl->ats_brand = brand; +} + +void +_entry_brand_as(const acl_entry_t entry, int brand) +{ + + _acl_brand_as(entry2acl(entry), brand); +} + +int +_acl_type_not_valid_for_acl(const acl_t acl, acl_type_t type) +{ + + switch (_acl_brand(acl)) { + case ACL_BRAND_NFS4: + if (type == ACL_TYPE_NFS4) + return (0); + break; + + case ACL_BRAND_POSIX: + if (type == ACL_TYPE_ACCESS || type == ACL_TYPE_DEFAULT) + return (0); + break; + } + + return (-1); +} + +void +_acl_brand_from_type(acl_t acl, acl_type_t type) +{ + + switch (type) { + case ACL_TYPE_NFS4: + _acl_brand_as(acl, ACL_BRAND_NFS4); + break; + case ACL_TYPE_ACCESS: + case ACL_TYPE_DEFAULT: + _acl_brand_as(acl, ACL_BRAND_POSIX); + break; + default: + /* XXX: What to do here? */ + break; + } +} + +int +acl_get_brand_np(acl_t acl, int *brand_p) +{ + + if (acl == NULL || brand_p == NULL) { + errno = EINVAL; + return (-1); + } + *brand_p = _acl_brand(acl); + + return (0); +} diff -urNp current/lib/libc/posix1e/acl_calc_mask.c nfs4acl/lib/libc/posix1e/acl_calc_mask.c --- current/lib/libc/posix1e/acl_calc_mask.c 2009-04-29 09:27:01.000000000 +0200 +++ nfs4acl/lib/libc/posix1e/acl_calc_mask.c 2009-05-25 19:30:06.000000000 +0200 @@ -35,6 +35,8 @@ __FBSDID("$FreeBSD: src/lib/libc/posix1e #include #include +#include "acl_support.h" + /* * acl_calc_mask() (23.4.2): calculate and set the permissions * associated with the ACL_MASK ACL entry. If the ACL already @@ -48,6 +50,12 @@ acl_calc_mask(acl_t *acl_p) acl_t acl_new; int i, mask_mode, mask_num; + if (!_acl_brand_may_be(*acl_p, ACL_BRAND_POSIX)) { + errno = EINVAL; + return (-1); + } + _acl_brand_as(*acl_p, ACL_BRAND_POSIX); + /* * (23.4.2.4) requires acl_p to point to a pointer to a valid ACL. * Since one of the primary reasons to use this function would be diff -urNp current/lib/libc/posix1e/acl_clear_flags_np.3 nfs4acl/lib/libc/posix1e/acl_clear_flags_np.3 --- current/lib/libc/posix1e/acl_clear_flags_np.3 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_clear_flags_np.3 2009-05-24 21:07:38.000000000 +0200 @@ -0,0 +1,79 @@ +.\"- +.\" Copyright (c) 2008, 2009 Edward Tomasz Napierala +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR THE VOICES IN HIS HEAD BE +.\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd March 10, 2001 +.Dt ACL_CLEAR_FLAGS_NP 3 +.Os +.Sh NAME +.Nm acl_clear_flags_np +.Nd clear flags from a flagset +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In sys/types.h +.In sys/acl.h +.Ft int +.Fn acl_clear_flags_np "acl_flagset_t flagset_d" +.Sh DESCRIPTION +The +.Fn acl_clear_flags_np +function +is a non-portable call that clears all flags from flagset +.Fa flagset_d . +.Sh RETURN VALUES +.Rv -std acl_clear_flags_np +.Sh ERRORS +The +.Fn acl_clear_flags_np +function fails if: +.Bl -tag -width Er +.It Bq Er EINVAL +Argument +.Fa flagset_d +is not a valid descriptor for a flagset. +.El +.Sh SEE ALSO +.Xr acl 3 , +.Xr acl_add_flag_np 3 , +.Xr acl_delete_flag_np 3 , +.Xr acl_get_flagset_np 3 , +.Xr acl_set_flagset_np 3 , +.Xr posix1e 3 +.Sh STANDARDS +POSIX.1e is described in IEEE POSIX.1e draft 17. +.Sh HISTORY +POSIX.1e support was introduced in +.Fx 4.0 . +The +.Fn acl_clear_flags_np +function was added in +.Fx 5.0 . +.Sh AUTHORS +The +.Fn acl_clear_flags_np +function was written by +.An Edward Tomasz Napierala Aq trasz@FreeBSD.org . diff -urNp current/lib/libc/posix1e/acl_copy.c nfs4acl/lib/libc/posix1e/acl_copy.c --- current/lib/libc/posix1e/acl_copy.c 2009-04-29 09:27:01.000000000 +0200 +++ nfs4acl/lib/libc/posix1e/acl_copy.c 2009-05-23 08:37:49.000000000 +0200 @@ -35,6 +35,8 @@ __FBSDID("$FreeBSD: src/lib/libc/posix1e #include #include +#include "acl_support.h" + /* * acl_copy_entry() (23.4.4): copy the contents of ACL entry src_d to * ACL entry dest_d @@ -48,9 +50,21 @@ acl_copy_entry(acl_entry_t dest_d, acl_e return (-1); } - dest_d->ae_tag = src_d->ae_tag; - dest_d->ae_id = src_d->ae_id; + /* + * Can we brand the new entry the same as the source entry? + */ + if (!_entry_brand_may_be(dest_d, _entry_brand(src_d))) { + errno = EINVAL; + return (-1); + } + + _entry_brand_as(dest_d, _entry_brand(src_d)); + + dest_d->ae_tag = src_d->ae_tag; + dest_d->ae_id = src_d->ae_id; dest_d->ae_perm = src_d->ae_perm; + dest_d->ae_entry_type = src_d->ae_entry_type; + dest_d->ae_flags = src_d->ae_flags; return (0); } diff -urNp current/lib/libc/posix1e/acl_create_entry.3 nfs4acl/lib/libc/posix1e/acl_create_entry.3 --- current/lib/libc/posix1e/acl_create_entry.3 2009-04-29 09:27:01.000000000 +0200 +++ nfs4acl/lib/libc/posix1e/acl_create_entry.3 2009-04-29 09:35:23.000000000 +0200 @@ -30,6 +30,7 @@ .Os .Sh NAME .Nm acl_create_entry +.Nm acl_create_entry_np .Nd create a new ACL entry .Sh LIBRARY .Lb libc @@ -38,6 +39,8 @@ .In sys/acl.h .Ft int .Fn acl_create_entry "acl_t *acl_p" "acl_entry_t *entry_p" +.Ft int +.Fn acl_create_entry_np "acl_t *acl_p" "acl_entry_t *entry_p" "int index" .Sh DESCRIPTION The .Fn acl_create_entry @@ -45,6 +48,16 @@ function is a POSIX.1e call that creates a new ACL entry in the ACL pointed to by .Fa acl_p . +The +.Fn acl_create_entry_np +function is a non-portable version that creates the ACL entry +at position +.Fa index . +Positions are numbered starting from zero, i.e. calling +.Fn acl_create_entry_np +with +.Fa index +argument equal to zero will prepend the entry to the ACL. .Sh RETURN VALUES .Rv -std acl_create_entry .Sh ERRORS @@ -56,6 +69,9 @@ function fails if: Argument .Fa acl_p does not point to a pointer to a valid ACL. +Argument +.Fa index +is out of bounds. .It Bq Er ENOMEM The ACL working storage requires more memory than is allowed by the hardware or system-imposed memory diff -urNp current/lib/libc/posix1e/acl_delete_entry.3 nfs4acl/lib/libc/posix1e/acl_delete_entry.3 --- current/lib/libc/posix1e/acl_delete_entry.3 2009-04-29 09:27:01.000000000 +0200 +++ nfs4acl/lib/libc/posix1e/acl_delete_entry.3 2009-04-29 09:35:23.000000000 +0200 @@ -29,7 +29,8 @@ .Dt ACL_DELETE_ENTRY 3 .Os .Sh NAME -.Nm acl_delete_entry +.Nm acl_delete_entry , +.Nm acl_delete_entry_np , .Nd delete an ACL entry from an ACL .Sh LIBRARY .Lb libc @@ -38,6 +39,8 @@ .In sys/acl.h .Ft int .Fn acl_delete_entry "acl_t acl" "acl_entry_t entry_d" +.Ft int +.Fn acl_delete_entry_np "acl_t acl" "int index" .Sh DESCRIPTION The .Fn acl_delete_entry @@ -46,6 +49,18 @@ is a POSIX.1e call that removes the ACL .Fa entry_d from ACL .Fa acl . +The +.Fn acl_delete_entry_np +function is a non-portable version that removes the ACL entry +at position +.Fa index +from ACL +.Fa acl . +Positions are numbered starting from zero, i.e. calling +.Fn acl_delete_entry_np +with +.Fa index +argument equal to zero will remove the first ACL entry. .Sh RETURN VALUES .Rv -std acl_delete_entry .Sh ERRORS @@ -61,6 +76,9 @@ Argument .Fa entry_d is not a valid descriptor for an ACL entry in .Fa acl . +Argument +.Fa index +is out of bounds. .El .Sh SEE ALSO .Xr acl 3 , diff -urNp current/lib/libc/posix1e/acl_delete_entry.c nfs4acl/lib/libc/posix1e/acl_delete_entry.c --- current/lib/libc/posix1e/acl_delete_entry.c 2009-04-29 09:27:01.000000000 +0200 +++ nfs4acl/lib/libc/posix1e/acl_delete_entry.c 2009-05-25 19:30:06.000000000 +0200 @@ -33,6 +33,39 @@ __FBSDID("$FreeBSD: src/lib/libc/posix1e #include "un-namespace.h" #include #include +#include + +#include "acl_support.h" + +static int +_entry_matches(const acl_entry_t a, const acl_entry_t b) +{ + /* + * There is a semantical difference here between NFSv4 and POSIX + * draft ACLs. In POSIX, there may be only one entry for the particular + * user or group. In NFSv4 ACL, there may be any number of them. We're + * trying to be more specific here in that case. + */ + switch (_entry_brand(a)) { + case ACL_BRAND_NFS4: + if (a->ae_tag != b->ae_tag || a->ae_entry_type != b->ae_entry_type) + return (0); + + /* If ae_ids matter, compare them as well. */ + if (a->ae_tag == ACL_USER || a->ae_tag == ACL_GROUP) { + if (a->ae_id != b->ae_id) + return (0); + } + + return (1); + + default: + if ((a->ae_tag == b->ae_tag) && (a->ae_id == b->ae_id)) + return (1); + } + + return (0); +} /* * acl_delete_entry() (23.4.9): remove the ACL entry indicated by entry_d @@ -42,7 +75,7 @@ int acl_delete_entry(acl_t acl, acl_entry_t entry_d) { struct acl *acl_int; - int i; + int i, j, found = 0; if (acl == NULL || entry_d == NULL) { errno = EINVAL; @@ -51,29 +84,74 @@ acl_delete_entry(acl_t acl, acl_entry_t acl_int = &acl->ats_acl; + if (_entry_brand(entry_d) != _acl_brand(acl)) { + errno = EINVAL; + return (-1); + } + if ((acl->ats_acl.acl_cnt < 1) || (acl->ats_acl.acl_cnt > ACL_MAX_ENTRIES)) { errno = EINVAL; return (-1); } - for (i = 0; i < acl->ats_acl.acl_cnt; i++) { - /* if this is our entry... */ - if ((acl->ats_acl.acl_entry[i].ae_tag == entry_d->ae_tag) && - (acl->ats_acl.acl_entry[i].ae_id == entry_d->ae_id)) { + for (i = 0; i < acl->ats_acl.acl_cnt;) { + if (_entry_matches(&(acl->ats_acl.acl_entry[i]), entry_d)) { /* ...shift the remaining entries... */ - for (; i < acl->ats_acl.acl_cnt - 1; ++i) - acl->ats_acl.acl_entry[i] = - acl->ats_acl.acl_entry[i+1]; + for (j = i; j < acl->ats_acl.acl_cnt - 1; ++j) + acl->ats_acl.acl_entry[j] = + acl->ats_acl.acl_entry[j+1]; /* ...drop the count and zero the unused entry... */ acl->ats_acl.acl_cnt--; - bzero(&acl->ats_acl.acl_entry[i], + bzero(&acl->ats_acl.acl_entry[j], sizeof(struct acl_entry)); acl->ats_cur_entry = 0; - return (0); - } + + /* Continue with the loop to remove all maching entries. */ + found = 1; + } else + i++; } + if (found) + return (0); errno = EINVAL; return (-1); } + +int +acl_delete_entry_np(acl_t acl, int offset) +{ + struct acl *acl_int; + int i; + + if (acl == NULL) { + errno = EINVAL; + return (-1); + } + + acl_int = &acl->ats_acl; + + if (offset < 0 || offset >= acl_int->acl_cnt) { + errno = EINVAL; + return (-1); + } + + if ((acl->ats_acl.acl_cnt < 1) || + (acl->ats_acl.acl_cnt > ACL_MAX_ENTRIES)) { + errno = EINVAL; + return (-1); + } + + /* ...shift the remaining entries... */ + for (i = offset; i < acl->ats_acl.acl_cnt - 1; ++i) + acl->ats_acl.acl_entry[i] = + acl->ats_acl.acl_entry[i+1]; + /* ...drop the count and zero the unused entry... */ + acl->ats_acl.acl_cnt--; + bzero(&acl->ats_acl.acl_entry[i], + sizeof(struct acl_entry)); + acl->ats_cur_entry = 0; + + return (0); +} diff -urNp current/lib/libc/posix1e/acl_delete_flag_np.3 nfs4acl/lib/libc/posix1e/acl_delete_flag_np.3 --- current/lib/libc/posix1e/acl_delete_flag_np.3 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_delete_flag_np.3 2009-05-24 21:07:38.000000000 +0200 @@ -0,0 +1,84 @@ +.\"- +.\" Copyright (c) 2008, 2009 Edward Tomasz Napierala +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR THE VOICES IN HIS HEAD BE +.\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd March 10, 2001 +.Dt ACL_DELETE_FLAG_NP 3 +.Os +.Sh NAME +.Nm acl_delete_flag_np +.Nd delete flags from a flagset +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In sys/types.h +.In sys/acl.h +.Ft int +.Fn acl_delete_flag_np "acl_flagset_t flagset_d" "acl_flag_t flag" +.Sh DESCRIPTION +The +.Fn acl_delete_flag_np +function +is a non-portable call that removes specific flags from flagset +.Fa flags . +.Sh RETURN VALUES +.Rv -std acl_delete_flag_np +.Sh ERRORS +The +.Fn acl_delete_flag_np +function fails if: +.Bl -tag -width Er +.It Bq Er EINVAL +Argument +.Fa flagset_d +is not a valid descriptor for a flagset. +Argument +.Fa flag +does not contain a valid +.Vt acl_flag_t +value. +.El +.Sh SEE ALSO +.Xr acl 3 , +.Xr acl_add_flag_np 3 , +.Xr acl_clear_flag_nps 3 , +.Xr acl_get_flagset_np 3 , +.Xr acl_set_flagset_np 3 , +.Xr posix1e 3 +.Sh STANDARDS +POSIX.1e is described in IEEE POSIX.1e draft 17. +.Sh HISTORY +POSIX.1e support was introduced in +.Fx 4.0 . +The +.Fn acl_delete_flag_np +function was added in +.Fx 8.0 . +.Sh AUTHORS +The +.Fn acl_delete_flag_np +function was written by +.An Edward Tomasz Napierala Aq trasz@FreeBSD.org . diff -urNp current/lib/libc/posix1e/acl_entry.c nfs4acl/lib/libc/posix1e/acl_entry.c --- current/lib/libc/posix1e/acl_entry.c 2009-05-29 19:54:13.000000000 +0200 +++ nfs4acl/lib/libc/posix1e/acl_entry.c 2009-05-29 20:25:32.000000000 +0200 @@ -74,6 +74,48 @@ acl_create_entry(acl_t *acl_p, acl_entry return (0); } +int +acl_create_entry_np(acl_t *acl_p, acl_entry_t *entry_p, int offset) +{ + int i; + struct acl *acl_int; + + if (acl_p == NULL) { + errno = EINVAL; + return (-1); + } + + acl_int = &(*acl_p)->ats_acl; + + if ((acl_int->acl_cnt + 1 >= ACL_MAX_ENTRIES) || (acl_int->acl_cnt < 0)) { + errno = EINVAL; + return (-1); + } + + if (offset < 0 || offset >= acl_int->acl_cnt) { + errno = EINVAL; + return (-1); + } + + /* Make room for the new entry. */ + for (i = acl_int->acl_cnt; i > offset; i--) + acl_int->acl_entry[i] = acl_int->acl_entry[i - 1]; + + acl_int->acl_cnt++; + + *entry_p = &acl_int->acl_entry[offset]; + + (**entry_p).ae_tag = ACL_UNDEFINED_TAG; + (**entry_p).ae_id = ACL_UNDEFINED_ID; + (**entry_p).ae_perm = ACL_PERM_NONE; + (**entry_p).ae_entry_type = 0; + (**entry_p).ae_flags= 0; + + (*acl_p)->ats_cur_entry = 0; + + return (0); +} + /* * acl_get_entry() (23.4.14): returns an ACL entry from an ACL * indicated by entry_id. diff -urNp current/lib/libc/posix1e/acl_flag.c nfs4acl/lib/libc/posix1e/acl_flag.c --- current/lib/libc/posix1e/acl_flag.c 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_flag.c 2009-05-26 11:21:36.000000000 +0200 @@ -0,0 +1,155 @@ +/*- + * Copyright (c) 2008, 2009 Edward Tomasz NapieraÅ‚a + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include "acl_support.h" + +static int +_flag_is_invalid(acl_flag_t flag) +{ + + if ((flag & ACL_FLAGS_BITS) == flag) + return (0); + + errno = EINVAL; + + return (1); +} + +int +acl_add_flag_np(acl_flagset_t flagset_d, acl_flag_t flag) +{ + + if (flagset_d == NULL) { + errno = EINVAL; + return (-1); + } + + if (_flag_is_invalid(flag)) + return (-1); + + *flagset_d |= flag; + + return (0); +} + +int +acl_clear_flags_np(acl_flagset_t flagset_d) +{ + + if (flagset_d == NULL) { + errno = EINVAL; + return (-1); + } + + *flagset_d |= 0; + + return (0); +} + +int +acl_delete_flag_np(acl_flagset_t flagset_d, acl_flag_t flag) +{ + + if (flagset_d == NULL) { + errno = EINVAL; + return (-1); + } + + if (_flag_is_invalid(flag)) + return (-1); + + *flagset_d &= ~flag; + + return (0); +} + +int +acl_get_flag_np(acl_flagset_t flagset_d, acl_flag_t flag) +{ + + if (flagset_d == NULL) { + errno = EINVAL; + return (-1); + } + + if (_flag_is_invalid(flag)) + return (-1); + + if (*flagset_d & flag) + return (1); + + return (0); +} + +int +acl_get_flagset_np(acl_entry_t entry_d, acl_flagset_t *flagset_p) +{ + + if (entry_d == NULL || flagset_p == NULL) { + errno = EINVAL; + return (-1); + } + + if (!_entry_brand_may_be(entry_d, ACL_BRAND_NFS4)) { + errno = EINVAL; + return (-1); + } + + *flagset_p = &entry_d->ae_flags; + + return (0); +} + +int +acl_set_flagset_np(acl_entry_t entry_d, acl_flagset_t flagset_d) +{ + + if (entry_d == NULL) { + errno = EINVAL; + return (-1); + } + + if (!_entry_brand_may_be(entry_d, ACL_BRAND_NFS4)) { + errno = EINVAL; + return (-1); + } + + _entry_brand_as(entry_d, ACL_BRAND_NFS4); + + if (_flag_is_invalid(*flagset_d)) + return (-1); + + entry_d->ae_flags = *flagset_d; + + return (0); +} diff -urNp current/lib/libc/posix1e/acl_from_text.c nfs4acl/lib/libc/posix1e/acl_from_text.c --- current/lib/libc/posix1e/acl_from_text.c 2009-04-29 09:27:01.000000000 +0200 +++ nfs4acl/lib/libc/posix1e/acl_from_text.c 2009-04-29 09:35:23.000000000 +0200 @@ -40,45 +40,15 @@ __FBSDID("$FreeBSD: src/lib/libc/posix1e #include #include #include +#include #include "acl_support.h" static int _posix1e_acl_name_to_id(acl_tag_t tag, char *name, uid_t *id); static acl_tag_t acl_string_to_tag(char *tag, char *qualifier); -static char *string_skip_whitespace(char *string); -static void string_trim_trailing_whitespace(char *string); -static char * -string_skip_whitespace(char *string) -{ - - while (*string && ((*string == ' ') || (*string == '\t'))) { - string++; - } - return (string); -} - -static void -string_trim_trailing_whitespace(char *string) -{ - char *end; - - if (*string == '\0') - return; - - end = string + strlen(string) - 1; - - while (end != string) { - if ((*end == ' ') || (*end == '\t')) { - *end = '\0'; - end--; - } else { - return; - } - } - - return; -} +int _nfs4_acl_entry_from_text(acl_t aclp, char *entry); +int _text_could_be_nfs4_acl(const char *entry); static acl_tag_t acl_string_to_tag(char *tag, char *qualifier) @@ -109,6 +79,112 @@ acl_string_to_tag(char *tag, char *quali } } +static int +_posix1e_acl_entry_from_text(acl_t aclp, char *entry) +{ + acl_tag_t t; + acl_perm_t p; + char *tag, *qualifier, *permission; + uid_t id; + int error; + + assert(_acl_brand(aclp) == ACL_BRAND_POSIX); + + /* Split into three ':' delimited fields. */ + tag = strsep(&entry, ":"); + if (tag == NULL) { + errno = EINVAL; + return (-1); + } + tag = string_skip_whitespace(tag); + if ((*tag == '\0') && (!entry)) { + /* + * Is an entirely comment line, skip to next + * comma. + */ + return (0); + } + string_trim_trailing_whitespace(tag); + + qualifier = strsep(&entry, ":"); + if (qualifier == NULL) { + errno = EINVAL; + return (-1); + } + qualifier = string_skip_whitespace(qualifier); + string_trim_trailing_whitespace(qualifier); + + permission = strsep(&entry, ":"); + if (permission == NULL || entry) { + errno = EINVAL; + return (-1); + } + permission = string_skip_whitespace(permission); + string_trim_trailing_whitespace(permission); + + t = acl_string_to_tag(tag, qualifier); + if (t == -1) { + errno = EINVAL; + return (-1); + } + + error = _posix1e_acl_string_to_perm(permission, &p); + if (error == -1) { + errno = EINVAL; + return (-1); + } + + switch(t) { + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + case ACL_MASK: + case ACL_OTHER: + if (*qualifier != '\0') { + errno = EINVAL; + return (-1); + } + id = 0; + break; + + case ACL_USER: + case ACL_GROUP: + error = _posix1e_acl_name_to_id(t, qualifier, + &id); + if (error == -1) + return (-1); + break; + + default: + errno = EINVAL; + return (-1); + } + + error = _posix1e_acl_add_entry(aclp, t, id, p); + if (error == -1) + return (-1); + + return (0); +} + +static int +_text_is_nfs4_entry(const char *entry) +{ + int count = 0; + + assert(strlen(entry) > 0); + + while (*entry != '\0') { + if (*entry == ':' || *entry == '@') + count++; + entry++; + } + + if (count <= 2) + return (0); + + return (1); +} + /* * acl_from_text -- Convert a string into an ACL. * Postpone most validity checking until the end and call acl_valid() to do @@ -117,20 +193,16 @@ acl_string_to_tag(char *tag, char *quali acl_t acl_from_text(const char *buf_p) { - acl_tag_t t; - acl_perm_t p; acl_t acl; char *mybuf_p, *line, *cur, *notcomment, *comment, *entry; - char *tag, *qualifier, *permission; int error; - uid_t id; /* Local copy we can mess up. */ mybuf_p = strdup(buf_p); if (mybuf_p == NULL) return(NULL); - acl = acl_init(3); + acl = acl_init(3); /* XXX: WTF, 3? */ if (acl == NULL) { free(mybuf_p); return(NULL); @@ -145,77 +217,33 @@ acl_from_text(const char *buf_p) /* Inner loop: delimit at ',' boundaries. */ while ((entry = strsep(¬comment, ","))) { - /* Now split into three ':' delimited fields. */ - tag = strsep(&entry, ":"); - if (tag == NULL) { - errno = EINVAL; - goto error_label; - } - tag = string_skip_whitespace(tag); - if ((*tag == '\0') && (!entry)) { - /* - * Is an entirely comment line, skip to next - * comma. - */ - continue; - } - string_trim_trailing_whitespace(tag); - qualifier = strsep(&entry, ":"); - if (qualifier == NULL) { - errno = EINVAL; - goto error_label; - } - qualifier = string_skip_whitespace(qualifier); - string_trim_trailing_whitespace(qualifier); - - permission = strsep(&entry, ":"); - if (permission == NULL || entry) { - errno = EINVAL; - goto error_label; - } - permission = string_skip_whitespace(permission); - string_trim_trailing_whitespace(permission); + /* Skip empty lines. */ + if (strlen(string_skip_whitespace(entry)) == 0) + continue; - t = acl_string_to_tag(tag, qualifier); - if (t == -1) { - errno = EINVAL; - goto error_label; + if (_acl_brand(acl) == ACL_BRAND_UNKNOWN) { + if (_text_is_nfs4_entry(entry)) + _acl_brand_as(acl, ACL_BRAND_NFS4); + else + _acl_brand_as(acl, ACL_BRAND_POSIX); } - error = _posix1e_acl_string_to_perm(permission, &p); - if (error == -1) { - errno = EINVAL; - goto error_label; - } - - switch(t) { - case ACL_USER_OBJ: - case ACL_GROUP_OBJ: - case ACL_MASK: - case ACL_OTHER: - if (*qualifier != '\0') { - errno = EINVAL; - goto error_label; - } - id = 0; + switch (_acl_brand(acl)) { + case ACL_BRAND_NFS4: + error = _nfs4_acl_entry_from_text(acl, entry); break; - case ACL_USER: - case ACL_GROUP: - error = _posix1e_acl_name_to_id(t, qualifier, - &id); - if (error == -1) - goto error_label; + case ACL_BRAND_POSIX: + error = _posix1e_acl_entry_from_text(acl, entry); break; default: - errno = EINVAL; - goto error_label; + error = EINVAL; + break; } - error = _posix1e_acl_add_entry(acl, t, id, p); - if (error == -1) + if (error) goto error_label; } } diff -urNp current/lib/libc/posix1e/acl_from_text_nfs4.c nfs4acl/lib/libc/posix1e/acl_from_text_nfs4.c --- current/lib/libc/posix1e/acl_from_text_nfs4.c 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_from_text_nfs4.c 2009-05-26 11:21:36.000000000 +0200 @@ -0,0 +1,312 @@ +/*- + * Copyright (c) 2008, 2009 Edward Tomasz NapieraÅ‚a + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "acl_support.h" + +#define MAX_ENTRY_LENGTH 512 + +/* + * Parse the tag field of ACL entry passed as "str". If qualifier + * needs to follow, then the variable referenced by "need_qualifier" + * is set to 1, otherwise it's set to 0. + */ +static int +parse_tag(const char *str, acl_entry_t entry, int *need_qualifier) +{ + + assert(need_qualifier != NULL); + *need_qualifier = 0; + + if (strcmp(str, "owner@") == 0) + return (acl_set_tag_type(entry, ACL_USER_OBJ)); + if (strcmp(str, "group@") == 0) + return (acl_set_tag_type(entry, ACL_GROUP_OBJ)); + if (strcmp(str, "everyone@") == 0) + return (acl_set_tag_type(entry, ACL_EVERYONE)); + + *need_qualifier = 1; + + if (strcmp(str, "user") == 0 || strcmp(str, "u") == 0) + return (acl_set_tag_type(entry, ACL_USER)); + if (strcmp(str, "group") == 0 || strcmp(str, "g") == 0) + return (acl_set_tag_type(entry, ACL_GROUP)); + + warnx("malformed ACL: invalid \"tag\" field"); + + return (-1); +} + +/* + * Parse the qualifier field of ACL entry passed as "str". + * If user or group name cannot be resolved, then the variable + * referenced by "need_qualifier" is set to 1. + */ +static int +parse_qualifier(char *str, acl_entry_t entry, int *need_qualifier) +{ + int qualifier_length, error; + id_t id; + char *end; + struct passwd *pwd; + struct group *grp; + acl_tag_t tag; + + assert(need_qualifier != NULL); + *need_qualifier = 0; + + qualifier_length = strlen(str); + + if (qualifier_length == 0) { + warnx("malformed ACL: empty \"qualifier\" field"); + return (-1); + } + + /* XXX: Can we assume that valid username never begins with a digit? */ + if (isdigit(str[0])) { + id = strtod(str, &end); + + if (end - str != qualifier_length) { + warnx("malformed ACL: trailing characters " + "after numerical id"); + return (-1); + } + + return (acl_set_qualifier(entry, &id)); + } + + error = acl_get_tag_type(entry, &tag); + if (error) + return (error); + + assert(tag == ACL_USER || tag == ACL_GROUP); + + if (tag == ACL_USER) { + /* XXX: Thread-unsafe. */ + pwd = getpwnam(str); + if (pwd == NULL) { + *need_qualifier = 1; + return (0); + } + + return (acl_set_qualifier(entry, &(pwd->pw_uid))); + } + + /* XXX: Thread-unsafe. */ + grp = getgrnam(str); + if (grp == NULL) { + *need_qualifier = 1; + return (0); + } + + return (acl_set_qualifier(entry, &(grp->gr_gid))); +} + +static int +parse_access_mask(char *str, acl_entry_t entry) +{ + int error; + acl_perm_t perm; + + error = _nfs4_parse_access_mask(str, &perm); + if (error) + return (error); + + error = acl_set_permset(entry, &perm); + + return (error); +} + +static int +parse_flags(char *str, acl_entry_t entry) +{ + int error; + acl_flag_t flags; + + error = _nfs4_parse_flags(str, &flags); + if (error) + return (error); + + error = acl_set_flagset_np(entry, &flags); + + return (error); +} + +static int +parse_entry_type(const char *str, acl_entry_t entry) +{ + + if (strcmp(str, "allow") == 0) + return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_ALLOW)); + if (strcmp(str, "deny") == 0) + return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_DENY)); + if (strcmp(str, "audit") == 0) + return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_AUDIT)); + if (strcmp(str, "alarm") == 0) + return (acl_set_entry_type_np(entry, ACL_ENTRY_TYPE_ALARM)); + + warnx("malformed ACL: invalid \"type\" field"); + + return (-1); +} + +static int +parse_appended_id(char *str, acl_entry_t entry) +{ + int qualifier_length; + char *end; + id_t id; + + qualifier_length = strlen(str); + if (qualifier_length == 0) { + warnx("malformed ACL: \"appended id\" field present, " + "but empty"); + return (-1); + } + + id = strtod(str, &end); + if (end - str != qualifier_length) { + warnx("malformed ACL: appended id is not a number"); + return (-1); + } + + return (acl_set_qualifier(entry, &id)); +} + +static int +number_of_colons(const char *str) +{ + int count = 0; + + while (*str != '\0') { + if (*str == ':') + count++; + + str++; + } + + return (count); +} + +int +_nfs4_acl_entry_from_text(acl_t aclp, char *str) +{ + int error, need_qualifier; + acl_entry_t entry; + char *field, *qualifier_field; + + error = acl_create_entry(&aclp, &entry); + if (error) + return (error); + + assert(_entry_brand(entry) == ACL_BRAND_NFS4); + + if (str == NULL) + goto truncated_entry; + field = strsep(&str, ":"); + + field = string_skip_whitespace(field); + if ((*field == '\0') && (!str)) { + /* + * Is an entirely comment line, skip to next + * comma. + */ + return (0); + } + + error = parse_tag(field, entry, &need_qualifier); + if (error) + goto malformed_field; + + if (need_qualifier) { + if (str == NULL) + goto truncated_entry; + qualifier_field = field = strsep(&str, ":"); + error = parse_qualifier(field, entry, &need_qualifier); + if (error) + goto malformed_field; + } + + if (str == NULL) + goto truncated_entry; + field = strsep(&str, ":"); + error = parse_access_mask(field, entry); + if (error) + goto malformed_field; + + if (str == NULL) + goto truncated_entry; + /* Do we have "flags" field? */ + if (number_of_colons(str) > 0) { + field = strsep(&str, ":"); + error = parse_flags(field, entry); + if (error) + goto malformed_field; + } + + if (str == NULL) + goto truncated_entry; + field = strsep(&str, ":"); + error = parse_entry_type(field, entry); + if (error) + goto malformed_field; + + if (need_qualifier) { + if (str == NULL) { + warnx("malformed ACL: unknown user or group name " + "\"%s\"", qualifier_field); + goto truncated_entry; + } + + error = parse_appended_id(str, entry); + if (error) + goto malformed_field; + } + + return (0); + +truncated_entry: +malformed_field: + acl_delete_entry(aclp, entry); + errno = EINVAL; + return (-1); +} diff -urNp current/lib/libc/posix1e/acl_get.3 nfs4acl/lib/libc/posix1e/acl_get.3 --- current/lib/libc/posix1e/acl_get.3 2009-05-23 17:12:25.000000000 +0200 +++ nfs4acl/lib/libc/posix1e/acl_get.3 2009-05-26 17:15:43.000000000 +0200 @@ -99,7 +99,10 @@ argument are: .Bl -column -offset 3n "ACL_TYPE_DEFAULT" .It ACL_TYPE_ACCESS POSIX.1e access ACL .It ACL_TYPE_DEFAULT POSIX.1e default ACL +.It ACL_TYPE_NFS4 NFSv4 ACL .El +.Pp +The ACL returned will be branded accordingly. .Sh IMPLEMENTATION NOTES .Fx Ns 's support for POSIX.1e interfaces and features is still under @@ -146,6 +149,7 @@ The file system does not support ACL ret .Xr acl 3 , .Xr acl_free 3 , .Xr acl_get 3 , +.Xr acl_get_brand_np 3 , .Xr acl_set 3 , .Xr posix1e 3 .Sh STANDARDS diff -urNp current/lib/libc/posix1e/acl_get.c nfs4acl/lib/libc/posix1e/acl_get.c --- current/lib/libc/posix1e/acl_get.c 2009-05-23 07:49:39.000000000 +0200 +++ nfs4acl/lib/libc/posix1e/acl_get.c 2009-05-25 19:30:06.000000000 +0200 @@ -49,6 +49,7 @@ __FBSDID("$FreeBSD: src/lib/libc/posix1e #include #include #include +#include #include "acl_support.h" @@ -69,6 +70,9 @@ acl_get_file(const char *path_p, acl_typ return (NULL); } + aclp->ats_acl.acl_maxcnt = ACL_MAX_ENTRIES; + _acl_brand_from_type(aclp, type); + return (aclp); } @@ -89,26 +93,19 @@ acl_get_link_np(const char *path_p, acl_ return (NULL); } + aclp->ats_acl.acl_maxcnt = ACL_MAX_ENTRIES; + _acl_brand_from_type(aclp, type); + return (aclp); } acl_t acl_get_fd(int fd) { - acl_t aclp; - int error; - - aclp = acl_init(ACL_MAX_ENTRIES); - if (aclp == NULL) - return (NULL); + if (fpathconf(fd, _PC_EXTENDED_SECURITY_NP)) + return (acl_get_fd_np(fd, ACL_TYPE_NFS4)); - error = ___acl_get_fd(fd, ACL_TYPE_ACCESS, &aclp->ats_acl); - if (error) { - acl_free(aclp); - return (NULL); - } - - return (aclp); + return (acl_get_fd_np(fd, ACL_TYPE_ACCESS)); } acl_t @@ -128,6 +125,9 @@ acl_get_fd_np(int fd, acl_type_t type) return (NULL); } + aclp->ats_acl.acl_maxcnt = ACL_MAX_ENTRIES; + _acl_brand_from_type(aclp, type); + return (aclp); } @@ -218,3 +218,22 @@ acl_get_tag_type(acl_entry_t entry_d, ac return (0); } + +int +acl_get_entry_type_np(acl_entry_t entry_d, acl_entry_type_t *entry_type_p) +{ + + if (entry_d == NULL || entry_type_p == NULL) { + errno = EINVAL; + return (-1); + } + + if (!_entry_brand_may_be(entry_d, ACL_BRAND_NFS4)) { + errno = EINVAL; + return (-1); + } + + *entry_type_p = entry_d->ae_entry_type; + + return (0); +} diff -urNp current/lib/libc/posix1e/acl_get_brand_np.3 nfs4acl/lib/libc/posix1e/acl_get_brand_np.3 --- current/lib/libc/posix1e/acl_get_brand_np.3 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_get_brand_np.3 2009-05-26 17:15:43.000000000 +0200 @@ -0,0 +1,86 @@ +.\"- +.\" Copyright (c) 2008, 2009 Edward Tomasz Napierala +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR THE VOICES IN HIS HEAD BE +.\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd March 10, 2001 +.Dt ACL_GET_BRAND_NP 3 +.Os +.Sh NAME +.Nm acl_get_brand_np +.Nd retrieve the ACL brand from an ACL entry +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In sys/types.h +.In sys/acl.h +.Ft int +.Fn acl_get_brand_np "acl_t acl" "int *brand_p" +.Sh DESCRIPTION +The +.Fn acl_get_brand_np +function +is a non-portable call that returns the ACL brand for the ACL +.Fa acl . +Upon successful completion, the location referred to by the argument +.Fa brand_p +will be set to the ACL brand of the ACL +.Fa acl . +.Pp +Branding is an internal mechanism intended to prevent mixing POSIX.1e +and NFSv4 entries by mistake. +It's also used by the libc to determine how to print out the ACL. +The first call to function that is specific for one particular brand - POSIX.1e +or NFSv4 - "brands" the ACL. +After that, calling function specific to another brand will result in error. +.Sh RETURN VALUES +.Rv -std acl_get_brand_np +.Sh ERRORS +The +.Fn acl_get_brand_np +function fails if: +.Bl -tag -width Er +.It Bq Er EINVAL +Argument +.Fa acl +does not point to a valid ACL. +.El +.Sh SEE ALSO +.Xr acl 3 , +.Xr posix1e 3 +.Sh STANDARDS +POSIX.1e is described in IEEE POSIX.1e draft 17. +.Sh HISTORY +POSIX.1e support was introduced in +.Fx 4.0 . +The +.Fn acl_get_brand_np +function was added in +.Fx 8.0 . +.Sh AUTHORS +The +.Fn acl_get_brand_np +function was written by +.An Edward Tomasz Napierala Aq trasz@FreeBSD.org . diff -urNp current/lib/libc/posix1e/acl_get_entry_type_np.3 nfs4acl/lib/libc/posix1e/acl_get_entry_type_np.3 --- current/lib/libc/posix1e/acl_get_entry_type_np.3 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_get_entry_type_np.3 2009-05-26 17:15:43.000000000 +0200 @@ -0,0 +1,80 @@ +.\"- +.\" Copyright (c) 2008, 2009 Edward Tomasz Napierala +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR THE VOICES IN HIS HEAD BE +.\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd March 10, 2001 +.Dt ACL_GET_ENTRY_TYPE_NP 3 +.Os +.Sh NAME +.Nm acl_get_entry_type_np +.Nd retrieve the ACL type from an NFSv4 ACL entry +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In sys/types.h +.In sys/acl.h +.Ft int +.Fn acl_get_entry_type_np "acl_entry_t entry_d" "acl_entry_type_t *entry_type_p" +.Sh DESCRIPTION +The +.Fn acl_get_entry_type_np +function +is a non-portable call that returns the ACL type for the NFSv4 ACL entry +.Fa entry_d . +Upon successful completion, the location referred to by the argument +.Fa entry_type_p +will be set to the ACL type of the ACL entry +.Fa entry_d . +.Sh RETURN VALUES +.Rv -std acl_get_entry_type_np +.Sh ERRORS +The +.Fn acl_get_entry_type_np +function fails if: +.Bl -tag -width Er +.It Bq Er EINVAL +Argument +.Fa entry_d +is not a valid descriptor for an NFSv4 ACL entry; +.El +.Sh SEE ALSO +.Xr acl 3 , +.Xr acl_set_entry_type_np 3 , +.Xr posix1e 3 +.Sh STANDARDS +POSIX.1e is described in IEEE POSIX.1e draft 17. +.Sh HISTORY +POSIX.1e support was introduced in +.Fx 4.0 . +The +.Fn acl_get_entry_type_np +function was added in +.Fx 8.0 . +.Sh AUTHORS +The +.Fn acl_get_entry_type_np +function was written by +.An Edward Tomasz Napierala Aq trasz@FreeBSD.org . diff -urNp current/lib/libc/posix1e/acl_get_flag_np.3 nfs4acl/lib/libc/posix1e/acl_get_flag_np.3 --- current/lib/libc/posix1e/acl_get_flag_np.3 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_get_flag_np.3 2009-05-24 21:07:38.000000000 +0200 @@ -0,0 +1,94 @@ +.\"- +.\" Copyright (c) 2008, 2009 Edward Tomasz Napierala +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR THE VOICES IN HIS HEAD BE +.\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd April 10, 2001 +.Dt ACL_GET_FLAG_NP 3 +.Os +.Sh NAME +.Nm acl_get_flag_np +.Nd check if a flag is set in a flagset +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In sys/types.h +.In sys/acl.h +.Ft int +.Fn acl_get_flag_np "acl_flagset_t flagset_d" "acl_flag_t flag" +.Sh DESCRIPTION +The +.Fn acl_get_flag_np +function +is a non-portable function that checks if a flag is set in +a flagset. +.Sh RETURN VALUES +If the flag in +.Fa flag +is set in the flagset +.Fa flagset_d , +a value of +1 +is returned, otherwise a value of +0 +is returned. +.Sh ERRORS +If any of the following conditions occur, the +.Fn acl_get_flag_np +function will return a value of +\-1 +and set global variable +.Va errno +to the corresponding value: +.Bl -tag -width Er +.It Bq Er EINVAL +Argument +.Fa flag +does not contain a valid ACL flag or argument +.Fa flagset_d +is not a valid ACL flagset. +.El +.Sh SEE ALSO +.Xr acl 3 , +.Xr acl_add_flag_np 3 , +.Xr acl_clear_flags_np 3 , +.Xr acl_delete_flag_np 3 , +.Xr acl_get_flagset_np 3 , +.Xr acl_set_flagset_np 3 , +.Xr posix1e 3 +.Sh STANDARDS +POSIX.1e is described in IEEE POSIX.1e draft 17. +.Sh HISTORY +POSIX.1e support was introduced in +.Fx 4.0 . +The +.Fn acl_get_flag_np +function was added in +.Fx 8.0 . +.Sh AUTHORS +The +.Fn acl_get_flag_np +function was written by +.An Edward Tomasz Napierala Aq trasz@FreeBSD.org . diff -urNp current/lib/libc/posix1e/acl_get_flagset_np.3 nfs4acl/lib/libc/posix1e/acl_get_flagset_np.3 --- current/lib/libc/posix1e/acl_get_flagset_np.3 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_get_flagset_np.3 2009-05-26 17:15:43.000000000 +0200 @@ -0,0 +1,83 @@ +.\"- +.\" Copyright (c) 2008, 2009 Edward Tomasz Napierala +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR THE VOICES IN HIS HEAD BE +.\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd March 10, 2001 +.Dt ACL_GET_FLAGSET_NP 3 +.Os +.Sh NAME +.Nm acl_get_flagset_np +.Nd retrieve flagset from an NFSv4 ACL entry +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In sys/types.h +.In sys/acl.h +.Ft int +.Fn acl_get_flagset_np "acl_entry_t entry_d" "acl_flagset_t *flagset_p" +.Sh DESCRIPTION +The +.Fn acl_get_flagset_np +function +is a non-portable call that returns via +.Fa flagset_np_p +a descriptor to the flagset in the ACL entry +.Fa entry_d . +Subsequent operations using the returned flagset operate +on the flagset within the ACL entry. +.Sh RETURN VALUES +.Rv -std acl_get_flagset_np +.Sh ERRORS +The +.Fn acl_get_flagset_np +function fails if: +.Bl -tag -width Er +.It Bq Er EINVAL +Argument +.Fa entry_d +is not a valid descriptor for an ACL entry. +.El +.Sh SEE ALSO +.Xr acl 3 , +.Xr acl_add_flag_np 3 , +.Xr acl_clear_flags_np 3 , +.Xr acl_delete_flag_np 3 , +.Xr acl_set_flagset_np 3 , +.Xr posix1e 3 +.Sh STANDARDS +POSIX.1e is described in IEEE POSIX.1e draft 17. +.Sh HISTORY +POSIX.1e support was introduced in +.Fx 4.0 . +The +.Fn acl_get_flagset_np +function was added in +.Fx 8.0 . +.Sh AUTHORS +The +.Fn acl_get_flagset_np +function was written by +.An Edward Tomasz Napierala Aq trasz@FreeBSD.org . diff -urNp current/lib/libc/posix1e/acl_init.c nfs4acl/lib/libc/posix1e/acl_init.c --- current/lib/libc/posix1e/acl_init.c 2009-05-23 07:49:39.000000000 +0200 +++ nfs4acl/lib/libc/posix1e/acl_init.c 2009-06-09 21:29:12.000000000 +0200 @@ -38,10 +38,22 @@ __FBSDID("$FreeBSD: src/lib/libc/posix1e #include #include #include +#include + +#include "acl_support.h" + +#ifndef CTASSERT +#define CTASSERT(x) _CTASSERT(x, __LINE__) +#define _CTASSERT(x, y) __CTASSERT(x, y) +#define __CTASSERT(x, y) typedef char __assert_ ## y [(x) ? 1 : -1] +#endif + +CTASSERT(1 << _ACL_T_ALIGNMENT_BITS > sizeof(struct acl_t_struct)); acl_t acl_init(int count) { + int error; acl_t acl; if (count > ACL_MAX_ENTRIES) { @@ -53,11 +65,14 @@ acl_init(int count) return (NULL); } - acl = malloc(sizeof(struct acl_t_struct)); - if (acl != NULL) { - bzero(acl, sizeof(struct acl_t_struct)); - acl->ats_acl.acl_maxcnt = ACL_MAX_ENTRIES; - } + error = posix_memalign((void *)&acl, 1 << _ACL_T_ALIGNMENT_BITS, + sizeof(struct acl_t_struct)); + if (error) + return (NULL); + + bzero(acl, sizeof(struct acl_t_struct)); + acl->ats_brand = ACL_BRAND_UNKNOWN; + acl->ats_acl.acl_maxcnt = ACL_MAX_ENTRIES; return (acl); } diff -urNp current/lib/libc/posix1e/acl_is_trivial_np.3 nfs4acl/lib/libc/posix1e/acl_is_trivial_np.3 --- current/lib/libc/posix1e/acl_is_trivial_np.3 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_is_trivial_np.3 2009-05-26 17:15:43.000000000 +0200 @@ -0,0 +1,84 @@ +.\"- +.\" Copyright (c) 2008, 2009 Edward Tomasz Napierala +.\" All rights reserved. +.\" +.\" This software was developed by Robert Watson for the TrustedBSD Project. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd January 28, 2000 +.Dt ACL_STRIP_NP 3 +.Os +.Sh NAME +.Nm acl_is_trivial_np +.Nd determine whether ACL is trivial +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In sys/types.h +.In sys/acl.h +.Ft int +.Fn acl_is_trivial_np "const acl_t aclp" "int *trivialp" +.Sh DESCRIPTION +The +.Fn acl_is_trivial +function determines whether the ACL pointed to by the argument +.Va acl +is trivial. +Upon successful completion, the location referred to by the argument +.Fa trivialp +will be set to 1, if the ACL +.Fa aclp +points to is trivial, or 0 if it's not. +.Pp +ACL is trivial if it can be fully expressed as a file mode without loosing +any access rules. +For POSIX.1e ACLs, ACL is trivial if it has the three required entries, +one for owner, one for owning group, and one for other. +For NFSv4 ACLs, ACL is trivial if it has the "canonical six" entries. +Files that have non-trivial ACL have a plus sign appended after mode bits +in "ls -l" output. +.Sh RETURN VALUES +.Rv -std acl_get_tag_type +.Sh SEE ALSO +.Xr acl 3 , +.Xr posix1e 3 +.Sh STANDARDS +POSIX.1e is described in IEEE POSIX.1e draft 17. +Discussion +of the draft continues on the cross-platform POSIX.1e implementation +mailing list. +To join this list, see the +.Fx +POSIX.1e implementation +page for more information. +.Sh HISTORY +POSIX.1e support was introduced in +.Fx 4.0 . +The +.Fn acl_is_trivial_np +function was added in +.Fx 8.0 . +.Sh AUTHORS +.An Edward Tomasz Napierala Aq trasz@FreeBSD.org . diff -urNp current/lib/libc/posix1e/acl_set.3 nfs4acl/lib/libc/posix1e/acl_set.3 --- current/lib/libc/posix1e/acl_set.3 2009-05-23 17:12:25.000000000 +0200 +++ nfs4acl/lib/libc/posix1e/acl_set.3 2009-05-26 17:15:43.000000000 +0200 @@ -83,7 +83,13 @@ argument are: .Bl -column -offset 3n "ACL_TYPE_DEFAULT" .It ACL_TYPE_ACCESS POSIX.1e access ACL .It ACL_TYPE_DEFAULT POSIX.1e default ACL +.It ACL_TYPE_NFS4 NFSv4 ACL .El +.Pp +Trying to set ACL_TYPE_NFS4 with +.Va acl +branded as POSIX.1e, or ACL_TYPE_ACCESS or ACL_TYPE_DEFAULT with ACL +branded as NFSv4, will result in error. .Sh IMPLEMENTATION NOTES .Fx Ns 's support for POSIX.1e interfaces and features is still under @@ -109,7 +115,7 @@ Argument does not point to a valid ACL for this object, or the ACL type specified in .Va type -is invalid for this object, or both. +is invalid for this object, or there is branding mismatch. .It Bq Er ENAMETOOLONG A component of a pathname exceeded 255 characters, or an entire path name exceeded 1023 characters. @@ -132,6 +138,7 @@ read-only. .Xr acl 3 , .Xr acl_delete 3 , .Xr acl_get 3 , +.Xr acl_get_brand_np 3 , .Xr acl_valid 3 , .Xr posix1e 3 .Sh STANDARDS diff -urNp current/lib/libc/posix1e/acl_set.c nfs4acl/lib/libc/posix1e/acl_set.c --- current/lib/libc/posix1e/acl_set.c 2009-05-23 07:49:39.000000000 +0200 +++ nfs4acl/lib/libc/posix1e/acl_set.c 2009-05-25 19:30:06.000000000 +0200 @@ -40,6 +40,7 @@ __FBSDID("$FreeBSD: src/lib/libc/posix1e #include #include #include +#include #include "acl_support.h" @@ -59,6 +60,10 @@ acl_set_file(const char *path_p, acl_typ return (-1); } type = _acl_type_unold(type); + if (_acl_type_not_valid_for_acl(acl, type)) { + errno = EINVAL; + return (-1); + } if (_posix1e_acl(acl, type)) { error = _posix1e_acl_sort(acl); if (error) { @@ -82,6 +87,10 @@ acl_set_link_np(const char *path_p, acl_ return (-1); } type = _acl_type_unold(type); + if (_acl_type_not_valid_for_acl(acl, type)) { + errno = EINVAL; + return (-1); + } if (_posix1e_acl(acl, type)) { error = _posix1e_acl_sort(acl); if (error) { @@ -98,17 +107,11 @@ acl_set_link_np(const char *path_p, acl_ int acl_set_fd(int fd, acl_t acl) { - int error; - error = _posix1e_acl_sort(acl); - if (error) { - errno = error; - return(-1); - } - - acl->ats_cur_entry = 0; + if (fpathconf(fd, _PC_EXTENDED_SECURITY_NP)) + return (acl_set_fd_np(fd, acl, ACL_TYPE_NFS4)); - return (___acl_set_fd(fd, ACL_TYPE_ACCESS, &acl->ats_acl)); + return (acl_set_fd_np(fd, acl, ACL_TYPE_ACCESS)); } int @@ -116,7 +119,15 @@ acl_set_fd_np(int fd, acl_t acl, acl_typ { int error; + if (acl == NULL) { + errno = EINVAL; + return (-1); + } type = _acl_type_unold(type); + if (_acl_type_not_valid_for_acl(acl, type)) { + errno = EINVAL; + return (-1); + } if (_posix1e_acl(acl, type)) { error = _posix1e_acl_sort(acl); if (error) { @@ -143,6 +154,18 @@ acl_set_permset(acl_entry_t entry_d, acl return (-1); } + if ((*permset_d & ACL_POSIX1E_BITS) != *permset_d) { + if ((*permset_d & ACL_NFS4_PERM_BITS) != *permset_d) { + errno = EINVAL; + return (-1); + } + if (!_entry_brand_may_be(entry_d, ACL_BRAND_NFS4)) { + errno = EINVAL; + return (-1); + } + _entry_brand_as(entry_d, ACL_BRAND_NFS4); + } + entry_d->ae_perm = *permset_d; return (0); @@ -155,11 +178,11 @@ acl_set_permset(acl_entry_t entry_d, acl int acl_set_qualifier(acl_entry_t entry_d, const void *tag_qualifier_p) { + if (!entry_d || !tag_qualifier_p) { errno = EINVAL; return (-1); } - switch(entry_d->ae_tag) { case ACL_USER: case ACL_GROUP: @@ -187,12 +210,31 @@ acl_set_tag_type(acl_entry_t entry_d, ac } switch(tag_type) { + case ACL_OTHER: + case ACL_MASK: + if (!_entry_brand_may_be(entry_d, ACL_BRAND_POSIX)) { + errno = EINVAL; + return (-1); + } + _entry_brand_as(entry_d, ACL_BRAND_POSIX); + break; + case ACL_EVERYONE: + if (!_entry_brand_may_be(entry_d, ACL_BRAND_NFS4)) { + errno = EINVAL; + return (-1); + } + _entry_brand_as(entry_d, ACL_BRAND_NFS4); + break; + } + + switch(tag_type) { case ACL_USER_OBJ: case ACL_USER: case ACL_GROUP_OBJ: case ACL_GROUP: case ACL_MASK: case ACL_OTHER: + case ACL_EVERYONE: entry_d->ae_tag = tag_type; return (0); } @@ -200,3 +242,30 @@ acl_set_tag_type(acl_entry_t entry_d, ac errno = EINVAL; return (-1); } + +int +acl_set_entry_type_np(acl_entry_t entry_d, acl_entry_type_t entry_type) +{ + + if (entry_d == NULL) { + errno = EINVAL; + return (-1); + } + if (!_entry_brand_may_be(entry_d, ACL_BRAND_NFS4)) { + errno = EINVAL; + return (-1); + } + _entry_brand_as(entry_d, ACL_BRAND_NFS4); + + switch (entry_type) { + case ACL_ENTRY_TYPE_ALLOW: + case ACL_ENTRY_TYPE_DENY: + case ACL_ENTRY_TYPE_AUDIT: + case ACL_ENTRY_TYPE_ALARM: + entry_d->ae_entry_type = entry_type; + return (0); + } + + errno = EINVAL; + return (-1); +} diff -urNp current/lib/libc/posix1e/acl_set_entry_type_np.3 nfs4acl/lib/libc/posix1e/acl_set_entry_type_np.3 --- current/lib/libc/posix1e/acl_set_entry_type_np.3 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_set_entry_type_np.3 2009-05-26 17:15:43.000000000 +0200 @@ -0,0 +1,95 @@ +.\"- +.\" Copyright (c) 2008, 2009 Edward Tomasz Napierala +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR THE VOICES IN HIS HEAD BE +.\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd March 10, 2001 +.Dt ACL_SET_ENTRY_TYPE_NP 3 +.Os +.Sh NAME +.Nm acl_set_entry_type_np +.Nd set NFSv4 ACL entry type +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In sys/types.h +.In sys/acl.h +.Ft int +.Fn acl_set_entry_type_np "acl_entry_t entry_d" "acl_entry_type_t entry_type" +.Sh DESCRIPTION +The +.Fn acl_set_entry_type_np +function +is a non-portable call that sets the type of the ACL entry +.Fa entry_d +to the value referred to by +.Fa entry_type . +.Pp +Valid values are: +.Pp +.Bl -column -offset 3n "ACL_ENTRY_TYPE_ALLOW" +.It ACL_ENTRY_TYPE_ALLOW "allow" type entry +.It ACL_ENTRY_TYPE_DENY "deny" type entry +.El +.Pp +This call brands the ACL as NFSv4. +.Sh RETURN VALUES +.Rv -std acl_set_entry_type_np +.Sh ERRORS +The +.Fn acl_set_entry_type_np +function fails if: +.Bl -tag -width Er +.It Bq Er EINVAL +Argument +.Fa entry_d +is not a valid descriptor for an ACL entry. +The value pointed to by +.Fa entry_type +is not valid. +ACL is already branded as POSIX.1e. +.It Bq Er ENOMEM +The value to be returned requires more memory than is allowed +by the hardware or system-imposed memory management constraints. +.El +.Sh SEE ALSO +.Xr acl 3 , +.Xr acl_get_brand_np 3 , +.Xr acl_get_entry_type_np 3 , +.Xr posix1e 3 +.Sh STANDARDS +POSIX.1e is described in IEEE POSIX.1e draft 17. +.Sh HISTORY +POSIX.1e support was introduced in +.Fx 4.0 . +The +.Fn acl_get_entry_type_np +function was added in +.Fx 8.0 . +.Sh AUTHORS +The +.Fn acl_get_entry_type_np +function was written by +.An Edward Tomasz Napierala Aq trasz@FreeBSD.org . diff -urNp current/lib/libc/posix1e/acl_set_flagset_np.3 nfs4acl/lib/libc/posix1e/acl_set_flagset_np.3 --- current/lib/libc/posix1e/acl_set_flagset_np.3 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_set_flagset_np.3 2009-05-26 17:15:43.000000000 +0200 @@ -0,0 +1,85 @@ +.\"- +.\" Copyright (c) 2008, 2009 Edward Tomasz Napierala +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR THE VOICES IN HIS HEAD BE +.\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd March 10, 2001 +.Dt ACL_SET_FLAGSET_NP 3 +.Os +.Sh NAME +.Nm acl_set_flagset_np +.Nd set the flags of an NFSv4 ACL entry +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In sys/types.h +.In sys/acl.h +.Ft int +.Fn acl_set_flagset_np "acl_entry_t entry_d" "acl_flagset_t flagset_d" +.Sh DESCRIPTION +The +.Fn acl_set_flagset_np +function +is a non-portable call that sets the flags of ACL entry +.Fa entry_d +with the flags contained in +.Fa flagset_d . +.Pp +This call brands the ACL as NFSv4. +.Sh RETURN VALUES +.Rv -std acl_set_flagset_np +.Sh ERRORS +The +.Fn acl_set_flagset_np +function fails if: +.Bl -tag -width Er +.It Bq Er EINVAL +Argument +.Fa entry_d +is not a valid descriptor for an ACL entry. +ACL is already branded as POSIX.1e. +.El +.Sh SEE ALSO +.Xr acl 3 , +.Xr acl_add_flag_np 3 , +.Xr acl_get_brand_np 3 , +.Xr acl_clear_flags_np 3 , +.Xr acl_delete_flag_np 3 , +.Xr acl_get_flagset_np 3 , +.Xr posix1e 3 +.Sh STANDARDS +POSIX.1e is described in IEEE POSIX.1e draft 17. +.Sh HISTORY +POSIX.1e support was introduced in +.Fx 4.0 . +The +.Fn acl_set_flagset_np +function was added in +.Fx 8.0 . +.Sh AUTHORS +The +.Fn acl_set_flagset_np +function was written by +.An Edward Tomasz Napierala Aq trasz@FreeBSD.org . diff -urNp current/lib/libc/posix1e/acl_set_tag_type.3 nfs4acl/lib/libc/posix1e/acl_set_tag_type.3 --- current/lib/libc/posix1e/acl_set_tag_type.3 2009-05-23 17:12:25.000000000 +0200 +++ nfs4acl/lib/libc/posix1e/acl_set_tag_type.3 2009-05-26 17:15:43.000000000 +0200 @@ -49,7 +49,7 @@ to the value of .Pp Valid values are: .Pp -.Bl -column -offset 3n "ACL_EVERYONE" +.Bl -column -offset 3n "ACL_OTHER_OBJ" .It ACL_USER_OBJ Permissions apply to file owner .It ACL_USER Permissions apply to additional user specified by qualifier .It ACL_GROUP_OBJ Permissions apply to file group @@ -57,7 +57,15 @@ Valid values are: .It ACL_MASK Permissions specify mask .It ACL_OTHER Permissions apply to "other" .It ACL_OTHER_OBJ Same as ACL_OTHER +.It ACL_EVERYONE Permissions apply to "everyone@" .El +.Pp +Calling +.Fn acl_set_tag_type +with +.Fa tag_type +equal to ACL_MASK, ACL_OTHER or ACL_OTHER_OBJ brands the ACL as POSIX.1e. +Calling it with ACL_EVERYONE brands the ACL as NFSv4. .Sh RETURN VALUES .Rv -std acl_set_tag_type .Sh ERRORS @@ -72,9 +80,11 @@ is not a valid descriptor for an ACL ent Argument .Fa tag_type is not a valid ACL tag type. +ACL is already branded differently. .El .Sh SEE ALSO .Xr acl 3 , +.Xr acl_get_brand_np 3 , .Xr acl_get_tag_type 3 , .Xr posix1e 3 .Sh STANDARDS diff -urNp current/lib/libc/posix1e/acl_strip.c nfs4acl/lib/libc/posix1e/acl_strip.c --- current/lib/libc/posix1e/acl_strip.c 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_strip.c 2009-05-25 19:30:06.000000000 +0200 @@ -0,0 +1,200 @@ +/*- + * Copyright (c) 2001 Chris D. Faulhaber + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR THE VOICES IN HIS HEAD BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include "acl_support.h" + +/* + * These two routines from sys/kern/subr_acl_nfs4.c are used by both kernel + * and libc. + */ +void acl_nfs4_sync_acl_from_mode(struct acl *aclp, mode_t mode, + int file_owner_id); +void acl_nfs4_sync_mode_from_acl(mode_t *_mode, const struct acl *aclp); + +static acl_t +_nfs4_acl_strip_np(const acl_t aclp, int recalculate_mask) +{ + acl_t newacl; + mode_t mode; + + newacl = acl_init(ACL_MAX_ENTRIES); + if (newacl == NULL) { + errno = ENOMEM; + return (NULL); + } + + _acl_brand_as(newacl, ACL_BRAND_NFS4); + + acl_nfs4_sync_mode_from_acl(&mode, &(aclp->ats_acl)); + acl_nfs4_sync_acl_from_mode(&(newacl->ats_acl), mode, -1); + + return (newacl); +} + +static acl_t +_posix1e_acl_strip_np(const acl_t aclp, int recalculate_mask) +{ + acl_t acl_new, acl_old; + acl_entry_t entry, entry_new; + acl_permset_t perm; + acl_tag_t tag; + int entry_id, have_mask_entry; + + assert(_acl_brand(aclp) == ACL_BRAND_POSIX); + + acl_old = acl_dup(aclp); + if (acl_old == NULL) + return (NULL); + + assert(_acl_brand(acl_old) == ACL_BRAND_POSIX); + + have_mask_entry = 0; + acl_new = acl_init(ACL_MAX_ENTRIES); + if (acl_new == NULL) + return (NULL); + tag = ACL_UNDEFINED_TAG; + + /* only save the default user/group/other entries */ + entry_id = ACL_FIRST_ENTRY; + while (acl_get_entry(acl_old, entry_id, &entry) == 1) { + entry_id = ACL_NEXT_ENTRY; + + assert(_entry_brand(entry) == ACL_BRAND_POSIX); + + if (acl_get_tag_type(entry, &tag) == -1) + return (NULL); + + switch(tag) { + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + case ACL_OTHER: + if (acl_get_tag_type(entry, &tag) == -1) + return (NULL); + if (acl_get_permset(entry, &perm) == -1) + return (NULL); + if (acl_create_entry(&acl_new, &entry_new) == -1) + return (NULL); + if (acl_set_tag_type(entry_new, tag) == -1) + return (NULL); + if (acl_set_permset(entry_new, perm) == -1) + return (NULL); + if (acl_copy_entry(entry_new, entry) == -1) + return (NULL); + assert(_entry_brand(entry_new) == ACL_BRAND_POSIX); + break; + case ACL_MASK: + have_mask_entry = 1; + break; + default: + break; + } + } + + assert(_acl_brand(acl_new) == ACL_BRAND_POSIX); + + if (have_mask_entry && recalculate_mask) { + if (acl_calc_mask(&acl_new) == -1) + return (NULL); + } + + return (acl_new); +} + +acl_t +acl_strip_np(const acl_t aclp, int recalculate_mask) +{ + switch (_acl_brand(aclp)) { + case ACL_BRAND_NFS4: + return (_nfs4_acl_strip_np(aclp, recalculate_mask)); + + case ACL_BRAND_POSIX: + return (_posix1e_acl_strip_np(aclp, recalculate_mask)); + + default: + errno = EINVAL; + return (NULL); + } +} + +/* + * Return 1, if ACL is trivial, 0 otherwise. + * + * ACL is trivial, iff its meaning could be fully expressed using just file + * mode. In other words, ACL is trivial iff it doesn't have "+" to the right + * of the mode bits in "ls -l" output ;-) + */ +int +acl_is_trivial_np(const acl_t aclp, int *trivialp) +{ + acl_t tmpacl; + int differs; + + if (aclp == NULL || trivialp == NULL) { + errno = EINVAL; + return (-1); + } + + switch (_acl_brand(aclp)) { + case ACL_BRAND_POSIX: + if (aclp->ats_acl.acl_cnt == 3) + *trivialp = 1; + else + *trivialp = 0; + + return (0); + + case ACL_BRAND_NFS4: + /* + * Calculate trivial ACL - using acl_strip_np - and compare + * with the original. + */ + tmpacl = acl_strip_np(aclp, 0); + if (tmpacl == NULL) + return (-1); + + differs = _acl_differs(aclp, tmpacl); + acl_free(tmpacl); + + if (differs) + *trivialp = 0; + else + *trivialp = 1; + + return (0); + + default: + errno = EINVAL; + return (-1); + } +} diff -urNp current/lib/libc/posix1e/acl_strip_np.3 nfs4acl/lib/libc/posix1e/acl_strip_np.3 --- current/lib/libc/posix1e/acl_strip_np.3 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_strip_np.3 2009-05-24 21:07:38.000000000 +0200 @@ -0,0 +1,109 @@ +.\"- +.\" Copyright (c) 2008, 2009 Edward Tomasz Napierala +.\" All rights reserved. +.\" +.\" This software was developed by Robert Watson for the TrustedBSD Project. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd January 28, 2000 +.Dt ACL_STRIP_NP 3 +.Os +.Sh NAME +.Nm acl_strip_np +.Nd strip exteneded entries from an ACL +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In sys/types.h +.In sys/acl.h +.Ft acl_t +.Fn acl_strip_np "const acl_t acl" "int recalculate_mask" +.Sh DESCRIPTION +The +.Fn acl_strip_np +function returns a pointer to a trivial ACL computed from the ACL pointed +to by the argument +.Va acl . +.Pp +This function may cause memory to be allocated. +The caller should free any +releasable memory, when the new ACL is no longer required, by calling +.Xr acl_free 3 +with the +.Va (void*)acl_t +as an argument. +.Pp +Any existing ACL pointers that refer to the ACL referred to by +.Va acl +shall continue to refer to the ACL. +.Sh RETURN VALUES +Upon successful completion, this function shall return a pointer to the +newly allocated ACL. +Otherwise, a value of +.Va (acl_t)NULL +shall be returned, and +.Va errno +shall be set to indicate the error. +.Sh ERRORS +If any of the following conditions occur, the +.Fn acl_init +function shall return a value of +.Va (acl_t)NULL +and set +.Va errno +to the corresponding value: +.Bl -tag -width Er +.It Bq Er EINVAL +Argument +.Va acl +does not point to a valid ACL. +.It Bq Er ENOMEM +The +.Va acl_t +to be returned requires more memory than is allowed by the hardware or +system-imposed memory management constraints. +.El +.Sh SEE ALSO +.Xr acl 3 , +.Xr acl_is_trivial_np 3 , +.Xr posix1e 3 +.Sh STANDARDS +POSIX.1e is described in IEEE POSIX.1e draft 17. +Discussion +of the draft continues on the cross-platform POSIX.1e implementation +mailing list. +To join this list, see the +.Fx +POSIX.1e implementation +page for more information. +.Sh HISTORY +POSIX.1e support was introduced in +.Fx 4.0 . +The +.Fn acl_strip_np +function was added in +.Fx 8.0 . +.Sh AUTHORS +.An Edward Tomasz Napierala Aq trasz@FreeBSD.org . diff -urNp current/lib/libc/posix1e/acl_support.c nfs4acl/lib/libc/posix1e/acl_support.c --- current/lib/libc/posix1e/acl_support.c 2009-05-23 07:49:39.000000000 +0200 +++ nfs4acl/lib/libc/posix1e/acl_support.c 2009-05-26 17:15:43.000000000 +0200 @@ -40,6 +40,8 @@ __FBSDID("$FreeBSD: src/lib/libc/posix1e #include #include #include +#include +#include #include "acl_support.h" @@ -49,6 +51,37 @@ __FBSDID("$FreeBSD: src/lib/libc/posix1e #define ACL_STRING_PERM_NONE '-' /* + * Return 0, if both ACLs are identical. + */ +int +_acl_differs(const acl_t a, const acl_t b) +{ + int i; + struct acl_entry *entrya, *entryb; + + assert(_acl_brand(a) == _acl_brand(b)); + assert(_acl_brand(a) != ACL_BRAND_UNKNOWN); + assert(_acl_brand(b) != ACL_BRAND_UNKNOWN); + + if (a->ats_acl.acl_cnt != b->ats_acl.acl_cnt) + return (1); + + for (i = 0; i < b->ats_acl.acl_cnt; i++) { + entrya = &(a->ats_acl.acl_entry[i]); + entryb = &(b->ats_acl.acl_entry[i]); + + if (entrya->ae_tag != entryb->ae_tag || + entrya->ae_id != entryb->ae_id || + entrya->ae_perm != entryb->ae_perm || + entrya->ae_entry_type != entryb->ae_entry_type || + entrya->ae_flags != entryb->ae_flags) + return (1); + } + + return (0); +} + +/* * _posix1e_acl_entry_compare -- compare two acl_entry structures to * determine the order they should appear in. Used by _posix1e_acl_sort to * sort ACL entries into the kernel-desired order -- i.e., the order useful @@ -59,6 +92,10 @@ typedef int (*compare)(const void *, con static int _posix1e_acl_entry_compare(struct acl_entry *a, struct acl_entry *b) { + + assert(_entry_brand(a) == ACL_BRAND_POSIX); + assert(_entry_brand(b) == ACL_BRAND_POSIX); + /* * First, sort between tags -- conveniently defined in the correct * order for verification. @@ -116,6 +153,9 @@ int _posix1e_acl(acl_t acl, acl_type_t type) { + if (_acl_brand(acl) != ACL_BRAND_POSIX) + return (0); + return ((type == ACL_TYPE_ACCESS) || (type == ACL_TYPE_DEFAULT)); } @@ -243,7 +283,8 @@ _posix1e_acl_check(acl_t acl) * MAY HAVE SIDE-EFFECTS */ int -_posix1e_acl_id_to_name(acl_tag_t tag, uid_t id, ssize_t buf_len, char *buf) +_posix1e_acl_id_to_name(acl_tag_t tag, uid_t id, ssize_t buf_len, char *buf, + int flags) { struct group *g; struct passwd *p; @@ -251,7 +292,10 @@ _posix1e_acl_id_to_name(acl_tag_t tag, u switch(tag) { case ACL_USER: - p = getpwuid(id); + if (flags & ACL_TEXT_NUMERIC_IDS) + p = NULL; + else + p = getpwuid(id); if (!p) i = snprintf(buf, buf_len, "%d", id); else @@ -264,7 +308,10 @@ _posix1e_acl_id_to_name(acl_tag_t tag, u return (0); case ACL_GROUP: - g = getgrgid(id); + if (flags & ACL_TEXT_NUMERIC_IDS) + g = NULL; + else + g = getgrgid(id); if (g == NULL) i = snprintf(buf, buf_len, "%d", id); else @@ -379,21 +426,52 @@ _posix1e_acl_add_entry(acl_t acl, acl_ta /* * Convert "old" type - ACL_TYPE_{ACCESS,DEFAULT}_OLD - into its "new" - * counterpart. It's neccessary for the old (pre-NFS4 ACLs) binaries + * counterpart. It's neccessary for the old (pre-NFSv4 ACLs) binaries * to work with new libc and kernel. Fixing 'type' for old binaries with * old libc and new kernel is being done by kern/vfs_acl.c:type_unold(). */ int _acl_type_unold(acl_type_t type) { + switch (type) { case ACL_TYPE_ACCESS_OLD: return (ACL_TYPE_ACCESS); - case ACL_TYPE_DEFAULT_OLD: return (ACL_TYPE_DEFAULT); - default: return (type); } } + +char * +string_skip_whitespace(char *string) +{ + + while (*string && ((*string == ' ') || (*string == '\t'))) + string++; + + return (string); +} + +void +string_trim_trailing_whitespace(char *string) +{ + char *end; + + if (*string == '\0') + return; + + end = string + strlen(string) - 1; + + while (end != string) { + if ((*end == ' ') || (*end == '\t')) { + *end = '\0'; + end--; + } else { + return; + } + } + + return; +} diff -urNp current/lib/libc/posix1e/acl_support.h nfs4acl/lib/libc/posix1e/acl_support.h --- current/lib/libc/posix1e/acl_support.h 2009-05-23 07:49:39.000000000 +0200 +++ nfs4acl/lib/libc/posix1e/acl_support.h 2009-05-23 08:37:50.000000000 +0200 @@ -33,17 +33,33 @@ #define _ACL_SUPPORT_H #define _POSIX1E_ACL_STRING_PERM_MAXSIZE 3 /* read, write, exec */ +#define _ACL_T_ALIGNMENT_BITS 13 int _acl_type_unold(acl_type_t type); +int _acl_differs(const acl_t a, const acl_t b); +int _acl_type_not_valid_for_acl(const acl_t acl, acl_type_t type); +void _acl_brand_from_type(acl_t acl, acl_type_t type); +int _acl_brand(const acl_t acl); +int _entry_brand(const acl_entry_t entry); +int _acl_brand_may_be(const acl_t acl, int brand); +int _entry_brand_may_be(const acl_entry_t entry, int brand); +void _acl_brand_as(acl_t acl, int brand); +void _entry_brand_as(const acl_entry_t entry, int brand); +int _nfs4_format_flags(char *str, size_t size, acl_flag_t var, int verbose); +int _nfs4_format_access_mask(char *str, size_t size, acl_perm_t var, int verbose); +int _nfs4_parse_flags(const char *str, acl_flag_t *var); +int _nfs4_parse_access_mask(const char *str, acl_perm_t *var); int _posix1e_acl_check(acl_t acl); int _posix1e_acl_sort(acl_t acl); int _posix1e_acl(acl_t acl, acl_type_t type); int _posix1e_acl_id_to_name(acl_tag_t tag, uid_t id, ssize_t buf_len, - char *buf); + char *buf, int flags); int _posix1e_acl_perm_to_string(acl_perm_t perm, ssize_t buf_len, char *buf); int _posix1e_acl_string_to_perm(char *string, acl_perm_t *perm); int _posix1e_acl_add_entry(acl_t acl, acl_tag_t tag, uid_t id, acl_perm_t perm); +char *string_skip_whitespace(char *string); +void string_trim_trailing_whitespace(char *string); #endif diff -urNp current/lib/libc/posix1e/acl_support_nfs4.c nfs4acl/lib/libc/posix1e/acl_support_nfs4.c --- current/lib/libc/posix1e/acl_support_nfs4.c 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_support_nfs4.c 2009-05-26 11:21:36.000000000 +0200 @@ -0,0 +1,256 @@ +/*- + * Copyright (c) 2008, 2009 Edward Tomasz NapieraÅ‚a + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include "acl_support.h" + +struct flagnames_struct { + uint32_t flag; + const char *name; + char letter; +}; + +struct flagnames_struct a_flags[] = + {{ ACL_ENTRY_FILE_INHERIT, "file_inherit", 'f'}, + { ACL_ENTRY_DIRECTORY_INHERIT, "dir_inherit", 'd'}, + { ACL_ENTRY_INHERIT_ONLY, "inherit_only", 'i'}, + { ACL_ENTRY_NO_PROPAGATE_INHERIT, "no_propagate", 'n'}, + { ACL_ENTRY_SUCCESSFUL_ACCESS, "successfull_access", 'S'}, + { ACL_ENTRY_FAILED_ACCESS, "failed_access", 'F'}, + /* + * There is no ACE_IDENTIFIER_GROUP here - SunOS does not show it + * in the "flags" field. There is no ACE_OWNER, ACE_GROUP or + * ACE_EVERYONE either, for obvious reasons. + */ + { 0, 0, 0}}; + +struct flagnames_struct a_access_masks[] = + {{ ACL_READ_DATA, "read_data", 'r'}, + { ACL_WRITE_DATA, "write_data", 'w'}, + { ACL_EXECUTE, "execute", 'x'}, + { ACL_APPEND_DATA, "append_data", 'p'}, + { ACL_DELETE_CHILD, "delete_child", 'D'}, + { ACL_DELETE, "delete", 'd'}, + { ACL_READ_ATTRIBUTES, "read_attributes", 'a'}, + { ACL_WRITE_ATTRIBUTES, "write_attributes", 'A'}, + { ACL_READ_NAMED_ATTRS, "read_xattr", 'R'}, + { ACL_WRITE_NAMED_ATTRS, "write_xattr", 'W'}, + { ACL_READ_ACL, "read_acl", 'c'}, + { ACL_WRITE_ACL, "write_acl", 'C'}, + { ACL_WRITE_OWNER, "write_owner", 'o'}, + { ACL_SYNCHRONIZE, "synchronize", 's'}, + { 0, 0, 0}}; + +static const char * +format_flag(uint32_t *var, const struct flagnames_struct *flags) +{ + + for (; flags->name != 0; flags++) { + if ((flags->flag & *var) == 0) + continue; + + *var &= ~flags->flag; + return (flags->name); + } + + return (NULL); +} + +static int +format_flags_verbose(char *str, size_t size, uint32_t var, + const struct flagnames_struct *flags) +{ + size_t off = 0; + const char *tmp; + + while ((tmp = format_flag(&var, flags)) != NULL) { + off += snprintf(str + off, size - off, "%s/", tmp); + assert (off < size); + } + + /* If there were any flags added... */ + if (off > 0) { + off--; + /* ... then remove the last slash. */ + assert(str[off] == '/'); + } + + str[off] = '\0'; + + return (0); +} + +static int +format_flags_compact(char *str, size_t size, uint32_t var, + const struct flagnames_struct *flags) +{ + size_t i; + + for (i = 0; flags[i].name != NULL; i++) { + assert(i < size); + if ((flags[i].flag & var) == 0) + str[i] = '-'; + else + str[i] = flags[i].letter; + } + + str[i] = '\0'; + + return (0); +} + +static int +parse_flags_verbose(const char *strp, uint32_t *var, + const struct flagnames_struct *flags, const char *flags_name, + int *try_compact) +{ + int i, found, ever_found = 0; + char *str, *flag; + + str = strdup(strp); + *try_compact = 0; + *var = 0; + + while (str != NULL) { + flag = strsep(&str, "/:"); + + found = 0; + for (i = 0; flags[i].name != NULL; i++) { + if (strcmp(flags[i].name, flag) == 0) { + *var |= flags[i].flag; + found = 1; + ever_found = 1; + } + } + + if (!found) { + if (ever_found) + warnx("malformed ACL: \"%s\" field contains " + "invalid flag \"%s\"", flags_name, flag); + else + *try_compact = 1; + free(str); + return (-1); + } + } + + free(str); + return (0); +} + +static int +parse_flags_compact(const char *str, uint32_t *var, + const struct flagnames_struct *flags, const char *flags_name) +{ + int i, j, found; + + *var = 0; + + for (i = 0;; i++) { + if (str[i] == '\0') + return (0); + + /* Ignore minus signs. */ + if (str[i] == '-') + continue; + + found = 0; + + for (j = 0; flags[j].name != NULL; j++) { + if (flags[j].letter == str[i]) { + *var |= flags[j].flag; + found = 1; + break; + } + } + + if (!found) { + warnx("malformed ACL: \"%s\" field contains " + "invalid flag \"%c\"", flags_name, str[i]); + return (-1); + } + } +} + +int +_nfs4_format_flags(char *str, size_t size, acl_flag_t var, int verbose) +{ + + if (verbose) + return (format_flags_verbose(str, size, var, a_flags)); + + return (format_flags_compact(str, size, var, a_flags)); +} + +int +_nfs4_format_access_mask(char *str, size_t size, acl_perm_t var, int verbose) +{ + + if (verbose) + return (format_flags_verbose(str, size, var, a_access_masks)); + + return (format_flags_compact(str, size, var, a_access_masks)); +} + +int +_nfs4_parse_flags(const char *str, acl_flag_t *flags) +{ + int error, try_compact; + int tmpflags; + + error = parse_flags_verbose(str, &tmpflags, a_flags, "flags", &try_compact); + if (error && try_compact) + error = parse_flags_compact(str, &tmpflags, a_flags, "flags"); + + *flags = tmpflags; + + return (error); +} + +int +_nfs4_parse_access_mask(const char *str, acl_perm_t *perms) +{ + int error, try_compact; + int tmpperms; + + error = parse_flags_verbose(str, &tmpperms, a_access_masks, + "access permissions", &try_compact); + if (error && try_compact) + error = parse_flags_compact(str, &tmpperms, + a_access_masks, "access permissions"); + + *perms = tmpperms; + + return (error); +} diff -urNp current/lib/libc/posix1e/acl_to_text.3 nfs4acl/lib/libc/posix1e/acl_to_text.3 --- current/lib/libc/posix1e/acl_to_text.3 2009-04-29 09:27:01.000000000 +0200 +++ nfs4acl/lib/libc/posix1e/acl_to_text.3 2009-05-26 17:15:43.000000000 +0200 @@ -31,7 +31,8 @@ .Dt ACL_TO_TEXT 3 .Os .Sh NAME -.Nm acl_to_text +.Nm acl_to_text , +.Nm acl_to_text_np .Nd convert an ACL to text .Sh LIBRARY .Lb libc @@ -40,10 +41,14 @@ .In sys/acl.h .Ft char * .Fn acl_to_text "acl_t acl" "ssize_t *len_p" +.Ft char * +.Fn acl_to_text_np "acl_t acl" "ssize_t *len_p" "int flags" .Sh DESCRIPTION The .Fn acl_to_text -function translates the ACL pointed to by argument +and +.Fn acl_to_text_np +functions translate the ACL pointed to by argument .Va acl into a NULL terminated character string. If the pointer @@ -51,9 +56,22 @@ If the pointer is not NULL, then the function shall return the length of the string (not including the NULL terminator) in the location pointed to by .Va len_p . -The format of the text string returned by +If the ACL is POSIX.1e, the format of the text string returned by .Fn acl_to_text -shall be the POSIX.1e long ACL form. +shall be the POSIX.1e long ACL form. If the ACL is NFSv4, the format +of the text string shall be the compact form, unless the +.Va ACL_TEXT_VERBOSE +flag is given. +.Pp +The flags specified are formed by +.Em or Ns 'ing +the following values +.Pp +.Bl -column -offset 3n "ACL_TEXT_NUMERIC_IDS" +.It ACL_TEXT_VERBOSE Format ACL using verbose form +.It ACL_TEXT_NUMERIC_IDS Do not resolve IDs into user or group names +.It ACL_TEXT_APPEND_ID In addition to user and group names, append numeric IDs +.El .Pp This function allocates any memory necessary to contain the string and returns a pointer to the string. diff -urNp current/lib/libc/posix1e/acl_to_text.c nfs4acl/lib/libc/posix1e/acl_to_text.c --- current/lib/libc/posix1e/acl_to_text.c 2009-04-29 09:27:01.000000000 +0200 +++ nfs4acl/lib/libc/posix1e/acl_to_text.c 2009-05-25 19:30:06.000000000 +0200 @@ -50,8 +50,11 @@ __FBSDID("$FreeBSD: src/lib/libc/posix1e * This function will not produce nice results if it is called with * a non-POSIX.1e semantics ACL. */ -char * -acl_to_text(acl_t acl, ssize_t *len_p) + +char *_nfs4_acl_to_text_np(const acl_t acl, ssize_t *len_p, int flags); + +static char * +_posix1e_acl_to_text(acl_t acl, ssize_t *len_p, int flags) { struct acl *acl_int; char *buf, *tmpbuf; @@ -105,7 +108,7 @@ acl_to_text(acl_t acl, ssize_t *len_p) goto error_label; error = _posix1e_acl_id_to_name(ae_tag, ae_id, - UT_NAMESIZE+1, name_buf); + UT_NAMESIZE+1, name_buf, flags); if (error) goto error_label; @@ -165,7 +168,7 @@ acl_to_text(acl_t acl, ssize_t *len_p) goto error_label; error = _posix1e_acl_id_to_name(ae_tag, ae_id, - UT_NAMESIZE+1, name_buf); + UT_NAMESIZE+1, name_buf, flags); if (error) goto error_label; @@ -235,3 +238,25 @@ error_label: if (buf) free(buf); return (NULL); } + +char * +acl_to_text_np(acl_t acl, ssize_t *len_p, int flags) +{ + + switch (_acl_brand(acl)) { + case ACL_BRAND_POSIX: + return (_posix1e_acl_to_text(acl, len_p, flags)); + case ACL_BRAND_NFS4: + return (_nfs4_acl_to_text_np(acl, len_p, flags)); + default: + errno = EINVAL; + return (NULL); + } +} + +char * +acl_to_text(acl_t acl, ssize_t *len_p) +{ + + return (acl_to_text_np(acl, len_p, 0)); +} diff -urNp current/lib/libc/posix1e/acl_to_text_nfs4.c nfs4acl/lib/libc/posix1e/acl_to_text_nfs4.c --- current/lib/libc/posix1e/acl_to_text_nfs4.c 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_to_text_nfs4.c 2009-05-26 11:21:36.000000000 +0200 @@ -0,0 +1,266 @@ +/*- + * Copyright (c) 2008, 2009 Edward Tomasz NapieraÅ‚a + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "acl_support.h" + +#define MAX_ENTRY_LENGTH 512 + +static int +format_who(char *str, size_t size, const acl_entry_t entry, int numeric) +{ + int error; + acl_tag_t tag; + struct passwd *pwd; + struct group *grp; + id_t *id; + + error = acl_get_tag_type(entry, &tag); + if (error) + return (error); + + switch (tag) { + case ACL_USER_OBJ: + snprintf(str, size, "owner@"); + break; + + case ACL_USER: + id = (id_t *)acl_get_qualifier(entry); + if (id == NULL) + return (-1); + /* XXX: Thread-unsafe. */ + if (!numeric) + pwd = getpwuid(*id); + else + pwd = NULL; + if (pwd == NULL) + snprintf(str, size, "user:%d", (unsigned int)*id); + else + snprintf(str, size, "user:%s", pwd->pw_name); + break; + + case ACL_GROUP_OBJ: + snprintf(str, size, "group@"); + break; + + case ACL_GROUP: + id = (id_t *)acl_get_qualifier(entry); + if (id == NULL) + return (-1); + /* XXX: Thread-unsafe. */ + if (!numeric) + grp = getgrgid(*id); + else + grp = NULL; + if (grp == NULL) + snprintf(str, size, "group:%d", (unsigned int)*id); + else + snprintf(str, size, "group:%s", grp->gr_name); + break; + + case ACL_EVERYONE: + snprintf(str, size, "everyone@"); + break; + + default: + return (-1); + } + + return (0); +} + +static int +format_entry_type(char *str, size_t size, const acl_entry_t entry) +{ + int error; + acl_entry_type_t entry_type; + + error = acl_get_entry_type_np(entry, &entry_type); + if (error) + return (error); + + switch (entry_type) { + case ACL_ENTRY_TYPE_ALLOW: + snprintf(str, size, "allow"); + break; + case ACL_ENTRY_TYPE_DENY: + snprintf(str, size, "deny"); + break; + case ACL_ENTRY_TYPE_AUDIT: + snprintf(str, size, "audit"); + break; + case ACL_ENTRY_TYPE_ALARM: + snprintf(str, size, "alarm"); + break; + default: + return (-1); + } + + return (0); +} + +static int +format_additional_id(char *str, size_t size, const acl_entry_t entry) +{ + int error; + acl_tag_t tag; + id_t *id; + + error = acl_get_tag_type(entry, &tag); + if (error) + return (error); + + switch (tag) { + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + case ACL_EVERYONE: + str[0] = '\0'; + break; + + default: + id = (id_t *)acl_get_qualifier(entry); + if (id == NULL) + return (-1); + snprintf(str, size, ":%d", (unsigned int)*id); + } + + return (0); +} + +static int +format_entry(char *str, size_t size, const acl_entry_t entry, int flags) +{ + size_t off = 0, padding_length, maximum_who_field_length = 18; + acl_permset_t permset; + acl_flagset_t flagset; + int error, len; + char buf[MAX_ENTRY_LENGTH + 1]; + + assert(_entry_brand(entry) == ACL_BRAND_NFS4); + + error = acl_get_flagset_np(entry, &flagset); + if (error) + return (error); + + error = acl_get_permset(entry, &permset); + if (error) + return (error); + + error = format_who(buf, sizeof(buf), entry, + flags & ACL_TEXT_NUMERIC_IDS); + if (error) + return (error); + len = strlen(buf); + padding_length = maximum_who_field_length - len; + if (padding_length > 0) { + memset(str, ' ', padding_length); + off += padding_length; + } + off += snprintf(str + off, size - off, "%s:", buf); + + error = _nfs4_format_access_mask(buf, sizeof(buf), *permset, + flags & ACL_TEXT_VERBOSE); + if (error) + return (error); + off += snprintf(str + off, size - off, "%s:", buf); + + error = _nfs4_format_flags(buf, sizeof(buf), *flagset, + flags & ACL_TEXT_VERBOSE); + if (error) + return (error); + off += snprintf(str + off, size - off, "%s:", buf); + + error = format_entry_type(buf, sizeof(buf), entry); + if (error) + return (error); + off += snprintf(str + off, size - off, "%s", buf); + + if (flags & ACL_TEXT_APPEND_ID) { + error = format_additional_id(buf, sizeof(buf), entry); + if (error) + return (error); + off += snprintf(str + off, size - off, "%s", buf); + } + + off += snprintf(str + off, size - off, "\n"); + + /* Make sure we didn't truncate anything. */ + assert (off < size); + + return (0); +} + +char * +_nfs4_acl_to_text_np(const acl_t aclp, ssize_t *len_p, int flags) +{ + int error, off = 0, size, entry_id = ACL_FIRST_ENTRY; + char *str; + acl_entry_t entry; + + if (aclp->ats_acl.acl_cnt == 0) + return strdup(""); + + size = aclp->ats_acl.acl_cnt * MAX_ENTRY_LENGTH; + str = malloc(size); + if (str == NULL) + return (NULL); + + while (acl_get_entry(aclp, entry_id, &entry) == 1) { + entry_id = ACL_NEXT_ENTRY; + + assert(off < size); + + error = format_entry(str + off, size - off, entry, flags); + if (error) { + errno = EINVAL; + return (NULL); + } + + off = strlen(str); + } + + assert(off < size); + str[off] = '\0'; + + if (len_p != NULL) + *len_p = off; + + return (str); +} diff -urNp current/lib/libc/posix1e/acl_valid.c nfs4acl/lib/libc/posix1e/acl_valid.c --- current/lib/libc/posix1e/acl_valid.c 2009-05-23 07:49:39.000000000 +0200 +++ nfs4acl/lib/libc/posix1e/acl_valid.c 2009-05-23 12:26:06.000000000 +0200 @@ -62,6 +62,10 @@ acl_valid(acl_t acl) errno = EINVAL; return (-1); } + if (!_acl_brand_may_be(acl, ACL_BRAND_POSIX)) { + errno = EINVAL; + return (-1); + } _posix1e_acl_sort(acl); error = _posix1e_acl_check(acl); if (error) { diff -urNp current/sbin/mount/mntopts.h nfs4acl/sbin/mount/mntopts.h --- current/sbin/mount/mntopts.h 2009-04-29 09:27:28.000000000 +0200 +++ nfs4acl/sbin/mount/mntopts.h 2009-04-29 09:35:43.000000000 +0200 @@ -54,6 +54,7 @@ struct mntopt { #define MOPT_SNAPSHOT { "snapshot", 0, MNT_SNAPSHOT, 0 } #define MOPT_MULTILABEL { "multilabel", 0, MNT_MULTILABEL, 0 } #define MOPT_ACLS { "acls", 0, MNT_ACLS, 0 } +#define MOPT_NFS4ACLS { "nfs4acls", 0, MNT_NFS4ACLS, 0 } /* Control flags. */ #define MOPT_FORCE { "force", 0, MNT_FORCE, 0 } @@ -87,7 +88,8 @@ struct mntopt { MOPT_NOCLUSTERR, \ MOPT_NOCLUSTERW, \ MOPT_MULTILABEL, \ - MOPT_ACLS + MOPT_ACLS, \ + MOPT_NFS4ACLS void getmntopts(const char *, const struct mntopt *, int *, int *); void rmslashes(char *, char *); diff -urNp current/sbin/mount/mount.8 nfs4acl/sbin/mount/mount.8 --- current/sbin/mount/mount.8 2009-04-29 09:27:28.000000000 +0200 +++ nfs4acl/sbin/mount/mount.8 2009-05-26 17:15:44.000000000 +0200 @@ -120,11 +120,14 @@ takes effect. The following options are available: .Bl -tag -width indent .It Cm acls -Enable Access Control Lists, or ACLS, which can be customized via the +Enable POSIX.1e Access Control Lists, or ACLS, which can be customized via the .Xr setfacl 1 and .Xr getfacl 1 commands. +This flag is mutually exclusive with +.Cm nfs4acls +flag. .It Cm async All .Tn I/O @@ -186,6 +189,15 @@ See .Xr mac 4 for more information, which cause the multilabel mount flag to be set automatically at mount-time. +.It Cm nfs4acls +Enable NFSv4 ACLS, which can be customized via the +.Xr setfacl 1 +and +.Xr getfacl 1 +commands. +This flag is mutually exclusive with +.Cm acls +flag. .It Cm noasync Metadata I/O should be done synchronously, while data I/O should be done asynchronously. diff -urNp current/sbin/mount/mount.c nfs4acl/sbin/mount/mount.c --- current/sbin/mount/mount.c 2009-05-29 19:54:13.000000000 +0200 +++ nfs4acl/sbin/mount/mount.c 2009-05-29 20:25:33.000000000 +0200 @@ -111,6 +111,7 @@ static struct opt { { MNT_SOFTDEP, "soft-updates" }, { MNT_MULTILABEL, "multilabel" }, { MNT_ACLS, "acls" }, + { MNT_NFS4ACLS, "nfs4acls" }, { MNT_GJOURNAL, "gjournal" }, { 0, NULL } }; @@ -915,6 +916,7 @@ flags2opts(int flags) if (flags & MNT_SUIDDIR) res = catopt(res, "suiddir"); if (flags & MNT_MULTILABEL) res = catopt(res, "multilabel"); if (flags & MNT_ACLS) res = catopt(res, "acls"); + if (flags & MNT_NFS4ACLS) res = catopt(res, "nfs4acls"); return (res); } diff -urNp current/share/man/man9/Makefile nfs4acl/share/man/man9/Makefile --- current/share/man/man9/Makefile 2009-06-04 22:03:14.000000000 +0200 +++ nfs4acl/share/man/man9/Makefile 2009-06-04 22:31:02.000000000 +0200 @@ -244,6 +244,7 @@ MAN= accept_filter.9 \ usbdi.9 \ utopia.9 \ vaccess.9 \ + vaccess_acl_nfs4.9 \ vaccess_acl_posix1e.9 \ vcount.9 \ vflush.9 \ diff -urNp current/share/man/man9/VOP_ACCESS.9 nfs4acl/share/man/man9/VOP_ACCESS.9 --- current/share/man/man9/VOP_ACCESS.9 2009-06-04 22:03:14.000000000 +0200 +++ nfs4acl/share/man/man9/VOP_ACCESS.9 2009-06-04 22:31:02.000000000 +0200 @@ -95,6 +95,7 @@ requested access. .El .Sh SEE ALSO .Xr vaccess 9 , +.Xr vaccess_acl_nfs4 9 , .Xr vaccess_acl_posix1e 9 , .Xr vnode 9 .Sh AUTHORS diff -urNp current/share/man/man9/acl.9 nfs4acl/share/man/man9/acl.9 --- current/share/man/man9/acl.9 2009-05-25 16:31:55.000000000 +0200 +++ nfs4acl/share/man/man9/acl.9 2009-05-25 17:32:06.000000000 +0200 @@ -208,6 +208,7 @@ The following values are valid: .Sh SEE ALSO .Xr acl 3 , .Xr vaccess_acl_posix1e 9 , +.Xr vaccess_acl_nfs4 9 , .Xr VFS 9 , .Xr vnaccess 9 , .Xr VOP_ACLCHECK 9 , diff -urNp current/share/man/man9/vaccess_acl_nfs4.9 nfs4acl/share/man/man9/vaccess_acl_nfs4.9 --- current/share/man/man9/vaccess_acl_nfs4.9 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/share/man/man9/vaccess_acl_nfs4.9 2009-05-26 17:15:44.000000000 +0200 @@ -0,0 +1,129 @@ +.\"- +.\" Copyright (c) 2001 Robert N. M. Watson +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd August 22, 2001 +.Os +.Dt VACCESS_ACL_NFS4 9 +.Sh NAME +.Nm vaccess_acl_nfs4 +.Nd generate a NFSv4 ACL access control decision using vnode parameters +.Sh SYNOPSIS +.In sys/param.h +.In sys/vnode.h +.In sys/acl.h +.Ft int +.Fo vaccess_acl_nfs4 +.Fa "enum vtype type" +.Fa "uid_t file_uid" +.Fa "gid_t file_gid" +.Fa "struct acl *acl" +.Fa "accmode_t accmode" +.Fa "struct ucred *cred" +.Fa "int *privused" +.Fc +.Sh DESCRIPTION +This call implements the logic for the +.Ux +discretionary file security model +with NFSv4 ACL extensions. +It accepts the vnodes type +.Fa type , +owning UID +.Fa file_uid , +owning GID +.Fa file_gid , +access ACL for the file +.Fa acl , +desired access mode +.Fa accmode , +requesting credential +.Fa cred , +and an optional call-by-reference +.Vt int +pointer returning whether or not +privilege was required for successful evaluation of the call; the +.Fa privused +pointer may be set to +.Dv NULL +by the caller in order not to be informed of +privilege information, or it may point to an integer that will be set to +1 if privilege is used, and 0 otherwise. +.Pp +This call is intended to support implementations of +.Xr VOP_ACCESS 9 , +which will use their own access methods to retrieve the vnode properties, +and then invoke +.Fn vaccess_acl_nfs4 +in order to perform the actual check. +Implementations of +.Xr VOP_ACCESS 9 +may choose to implement additional security mechanisms whose results will +be composed with the return value. +.Pp +The algorithm used by +.Fn vaccess_acl_nfs4 +is based on the NFSv4 ACL evaluation algorithm, as described in +NFSv4 Minor Version 1, draft-ietf-nfsv4-minorversion1-21.txt. +The algorithm selects a +.Em matching +entry from the access ACL, which may +then be composed with an available ACL mask entry, providing +.Ux +security compatibility. +.Pp +Once appropriate protections are selected for the current credential, +the requested access mode, in combination with the vnode type, will be +compared with the discretionary rights available for the credential. +If the rights granted by discretionary protections are insufficient, +then super-user privilege, if available for the credential, will also be +considered. +.Sh RETURN VALUES +.Fn vaccess_acl_nfs4 +will return 0 on success, or a non-zero error value on failure. +.Sh ERRORS +.Bl -tag -width Er +.It Bq Er EACCES +Permission denied. +An attempt was made to access a file in a way forbidden by its file access +permissions. +.It Bq Er EPERM +Operation not permitted. +An attempt was made to perform an operation limited to processes with +appropriate privileges or to the owner of a file or other resource. +.El +.Sh SEE ALSO +.Xr vaccess 9 , +.Xr vnode 9 , +.Xr VOP_ACCESS 9 +.Sh AUTHORS +Current implementation of +.Fn vaccess_acl_nfs4 +was written by +.An Edward Tomasz Napierala Aq trasz@FreeBSD.org . +.Sh BUGS +This manual page should include a full description of the NFSv4 ACL +evaluation algorithm, or cross reference another page that does. diff -urNp current/sys/cddl/compat/opensolaris/kern/opensolaris_acl.c nfs4acl/sys/cddl/compat/opensolaris/kern/opensolaris_acl.c --- current/sys/cddl/compat/opensolaris/kern/opensolaris_acl.c 2009-05-26 14:25:40.000000000 +0200 +++ nfs4acl/sys/cddl/compat/opensolaris/kern/opensolaris_acl.c 2009-05-26 14:41:48.000000000 +0200 @@ -25,7 +25,7 @@ */ #include -__FBSDID("$FreeBSD: src/sys/cddl/compat/opensolaris/kern/opensolaris_acl.c,v 1.2 2009/05/26 11:42:06 trasz Exp $"); +__FBSDID("$FreeBSD: head/sys/cddl/compat/opensolaris/kern/opensolaris_acl.c 192804 2009-05-26 11:42:06Z trasz $"); #include #include diff -urNp current/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c nfs4acl/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c --- current/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c 2009-05-26 14:25:40.000000000 +0200 +++ nfs4acl/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c 2009-05-30 20:34:42.000000000 +0200 @@ -3841,6 +3841,10 @@ zfs_pathconf(vnode_t *vp, int cmd, ulong *valp = (int)SPA_MINBLOCKSIZE; return (0); + case _PC_EXTENDED_SECURITY_NP: + *valp = 1; + return (0); + default: return (EOPNOTSUPP); } @@ -3964,7 +3968,6 @@ zfs_freebsd_access(ap) struct thread *a_td; } */ *ap; { - /* * ZFS itself only knowns about VREAD, VWRITE and VEXEC, the rest * we have to handle by calling vaccess(). @@ -4822,7 +4825,7 @@ zfs_freebsd_setacl(ap) return (EINVAL); /* - * With NFS4 ACLs, chmod(2) may need to add additional entries, + * With NFSv4 ACLs, chmod(2) may need to add additional entries, * splitting every entry into two and appending "canonical six" * entries at the end. Don't allow for setting an ACL that would * cause chmod(2) to run out of ACL entries. @@ -4896,11 +4899,9 @@ struct vop_vector zfs_vnodeops = { .vop_deleteextattr = zfs_deleteextattr, .vop_setextattr = zfs_setextattr, .vop_listextattr = zfs_listextattr, -#ifdef notyet .vop_getacl = zfs_freebsd_getacl, .vop_setacl = zfs_freebsd_setacl, .vop_aclcheck = zfs_freebsd_aclcheck, -#endif }; struct vop_vector zfs_fifoops = { @@ -4914,9 +4915,7 @@ struct vop_vector zfs_fifoops = { .vop_setattr = zfs_freebsd_setattr, .vop_write = VOP_PANIC, .vop_fid = zfs_freebsd_fid, -#ifdef notyet .vop_getacl = zfs_freebsd_getacl, .vop_setacl = zfs_freebsd_setacl, .vop_aclcheck = zfs_freebsd_aclcheck, -#endif }; diff -urNp current/sys/conf/files nfs4acl/sys/conf/files --- current/sys/conf/files 2009-06-04 22:03:15.000000000 +0200 +++ nfs4acl/sys/conf/files 2009-06-04 22:31:04.000000000 +0200 @@ -1965,6 +1965,7 @@ kern/serdev_if.m standard kern/stack_protector.c standard \ compile-with "${NORMAL_C:N-fstack-protector*}" kern/subr_acl_posix1e.c standard +kern/subr_acl_nfs4.c standard kern/subr_autoconf.c standard kern/subr_blist.c standard kern/subr_bus.c standard diff -urNp current/sys/fs/nfs/nfs_commonacl.c nfs4acl/sys/fs/nfs/nfs_commonacl.c --- current/sys/fs/nfs/nfs_commonacl.c 2009-05-29 19:54:21.000000000 +0200 +++ nfs4acl/sys/fs/nfs/nfs_commonacl.c 2009-05-29 20:25:41.000000000 +0200 @@ -695,7 +695,7 @@ nfsrv_setacl(vnode_t vp, NFSACL_T *aclp, if (nfsrv_useacl == 0 || !NFSHASNFS4ACL(vnode_mount(vp))) return (NFSERR_ATTRNOTSUPP); /* - * With NFS4 ACLs, chmod(2) may need to add additional entries. + * With NFSv4 ACLs, chmod(2) may need to add additional entries. * Make sure it has enough room for that - splitting every entry * into two and appending "canonical six" entries at the end. * Cribbed out of kern/vfs_acl.c - Rick M. diff -urNp current/sys/i386/conf/GENERIC nfs4acl/sys/i386/conf/GENERIC --- current/sys/i386/conf/GENERIC 2009-06-04 22:03:26.000000000 +0200 +++ nfs4acl/sys/i386/conf/GENERIC 2009-06-04 22:31:15.000000000 +0200 @@ -83,6 +83,10 @@ options INVARIANT_SUPPORT # Extra sanit options WITNESS # Enable checks to detect deadlocks and cycles options WITNESS_SKIPSPIN # Don't run witness on spinlocks for speed +options IPSEC +device crypto +device carp + # To make an SMP kernel, the next two lines are needed options SMP # Symmetric MultiProcessor Kernel device apic # I/O APIC diff -urNp current/sys/kern/subr_acl_nfs4.c nfs4acl/sys/kern/subr_acl_nfs4.c --- current/sys/kern/subr_acl_nfs4.c 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/sys/kern/subr_acl_nfs4.c 2009-06-09 18:00:39.000000000 +0200 @@ -0,0 +1,1070 @@ +/*- + * Copyright (c) 2008 Edward Tomasz NapieraÅ‚a + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * ACL support routines specific to NFSv4 access control lists. These are + * utility routines for code common across file systems implementing NFSv4 + * ACLs. + */ + +#ifdef _KERNEL +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#define KASSERT(a, b) assert(a) +#define CTASSERT(a) +#endif + +#ifdef _KERNEL + +static struct { + accmode_t accmode; + int mask; +} accmode2mask[] = {{VREAD, ACL_READ_DATA}, + {VWRITE, ACL_WRITE_DATA}, + {VAPPEND, ACL_APPEND_DATA}, + {VEXEC, ACL_EXECUTE}, + {VREAD_NAMED_ATTRS, ACL_READ_NAMED_ATTRS}, + {VWRITE_NAMED_ATTRS, ACL_WRITE_NAMED_ATTRS}, + {VDELETE_CHILD, ACL_DELETE_CHILD}, + {VREAD_ATTRIBUTES, ACL_READ_ATTRIBUTES}, + {VWRITE_ATTRIBUTES, ACL_WRITE_ATTRIBUTES}, + {VDELETE, ACL_DELETE}, + {VREAD_ACL, ACL_READ_ACL}, + {VWRITE_ACL, ACL_WRITE_ACL}, + {VWRITE_OWNER, ACL_WRITE_OWNER}, + {VSYNCHRONIZE, ACL_SYNCHRONIZE}, + {0, 0}}; + +static int +_access_mask_from_accmode(accmode_t accmode) +{ + int access_mask = 0, i; + + for (i = 0; accmode2mask[i].accmode != 0; i++) { + if (accmode & accmode2mask[i].accmode) + access_mask |= accmode2mask[i].mask; + } + + return (access_mask); +} + +/* + * Return 0, iff access is allowed, 1 otherwise. + */ +static int +_acl_denies(const struct acl *aclp, int access_mask, struct ucred *cred, + int file_uid, int file_gid, int *denied_explicitly) +{ + int i; + const struct acl_entry *entry; + + if (denied_explicitly != NULL) + *denied_explicitly = 0; + + KASSERT(aclp->acl_cnt > 0, ("aclp->acl_cnt > 0")); + KASSERT(aclp->acl_cnt <= ACL_MAX_ENTRIES, + ("aclp->acl_cnt <= ACL_MAX_ENTRIES")); + + for (i = 0; i < aclp->acl_cnt; i++) { + entry = &(aclp->acl_entry[i]); + + if (entry->ae_entry_type != ACL_ENTRY_TYPE_ALLOW && + entry->ae_entry_type != ACL_ENTRY_TYPE_DENY) + continue; + if (entry->ae_flags & ACL_ENTRY_INHERIT_ONLY) + continue; + switch (entry->ae_tag) { + case ACL_USER_OBJ: + if (file_uid != cred->cr_uid) + continue; + break; + case ACL_USER: + if (entry->ae_id != cred->cr_uid) + continue; + break; + case ACL_GROUP_OBJ: + if (!groupmember(file_gid, cred)) + continue; + break; + case ACL_GROUP: + if (!groupmember(entry->ae_id, cred)) + continue; + break; + default: + KASSERT(entry->ae_tag == ACL_EVERYONE, + ("entry->ae_tag == ACL_EVERYONE")); + } + + if (entry->ae_entry_type == ACL_ENTRY_TYPE_DENY) { + if (entry->ae_perm & access_mask) { + if (denied_explicitly != NULL) + *denied_explicitly = 1; + return (1); + } + } + + access_mask &= ~(entry->ae_perm); + if (access_mask == 0) + return (0); + } + + return (1); +} + +int +vaccess_acl_nfs4(enum vtype type, uid_t file_uid, gid_t file_gid, + struct acl *aclp, accmode_t accmode, struct ucred *cred, int *privused) +{ + accmode_t priv_granted = 0; + int denied, explicitly_denied, access_mask, is_directory, + must_be_owner = 0; + + if (privused != NULL) + *privused = 0; + + if (accmode & VADMIN) + must_be_owner = 1; + + access_mask = _access_mask_from_accmode(accmode); + + if (type == VDIR) + is_directory = 1; + else + is_directory = 0; + + /* + * File owner is always allowed to read and write the ACL + * and basic attributes. This is to prevent a situation + * where user would change ACL in a way that prevents him + * from undoing the change. + */ + if (file_uid == cred->cr_uid) + access_mask &= ~(ACL_READ_ACL | ACL_WRITE_ACL | + ACL_READ_ATTRIBUTES | ACL_WRITE_ATTRIBUTES); + + /* + * Ignore append permission for regular files; use write + * permission instead. + */ + if (!is_directory && (access_mask & ACL_APPEND_DATA)) { + access_mask &= ~ACL_APPEND_DATA; + access_mask |= ACL_WRITE_DATA; + } + + denied = _acl_denies(aclp, access_mask, cred, file_uid, file_gid, + &explicitly_denied); + + if (must_be_owner) { + if (file_uid != cred->cr_uid) + denied = EPERM; + } + + if (!denied) + return (0); + + /* + * Access failed. Iff it was not denied explicitly and + * VEXPLICIT_DENY flag was specified, allow access. + */ + if ((accmode & VEXPLICIT_DENY) && explicitly_denied == 0) + return (0); + + accmode &= ~VEXPLICIT_DENY; + + /* + * No match. Try to use privileges, if there are any. + */ + if (is_directory) { + if ((accmode & VEXEC) && !priv_check_cred(cred, + PRIV_VFS_LOOKUP, 0)) + priv_granted |= VEXEC; + } else { + if ((accmode & VEXEC) && !priv_check_cred(cred, + PRIV_VFS_EXEC, 0)) + priv_granted |= VEXEC; + } + + if ((accmode & VREAD) && !priv_check_cred(cred, PRIV_VFS_READ, 0)) + priv_granted |= VREAD; + + if ((accmode & (VWRITE | VAPPEND | VDELETE_CHILD)) && + !priv_check_cred(cred, PRIV_VFS_WRITE, 0)) + priv_granted |= (VWRITE | VAPPEND | VDELETE_CHILD); + + if ((accmode & VADMIN_PERMS) && + !priv_check_cred(cred, PRIV_VFS_ADMIN, 0)) + priv_granted |= VADMIN_PERMS; + + if ((accmode & (VSTAT_PERMS | VSYNCHRONIZE)) && + !priv_check_cred(cred, PRIV_VFS_STAT, 0)) + priv_granted |= (VSTAT_PERMS | VSYNCHRONIZE); + + if ((accmode & priv_granted) == accmode) { + if (privused != NULL) + *privused = 1; + + return (0); + } + + if (accmode & (VADMIN_PERMS | VDELETE_CHILD | VDELETE)) + denied = EPERM; + else + denied = EACCES; + + /* + * Nie wydostaniecie siÄ™. Drzwi zamkniÄ™te. + */ + return (denied); +} +#endif + +static int +_acl_entry_matches(struct acl_entry *entry, acl_tag_t tag, acl_perm_t perm, + acl_entry_type_t entry_type) +{ + if (entry->ae_tag != tag) + return (0); + + if (entry->ae_id != ACL_UNDEFINED_ID) + return (0); + + if (entry->ae_perm != perm) + return (0); + + if (entry->ae_entry_type != entry_type) + return (0); + + if (entry->ae_flags != 0) + return (0); + + return (1); +} + +static struct acl_entry * +_acl_append(struct acl *aclp, acl_tag_t tag, acl_perm_t perm, + acl_entry_type_t entry_type) +{ + struct acl_entry *entry; + + KASSERT(aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES, + ("aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES")); + + entry = &(aclp->acl_entry[aclp->acl_cnt]); + aclp->acl_cnt++; + + entry->ae_tag = tag; + entry->ae_id = ACL_UNDEFINED_ID; + entry->ae_perm = perm; + entry->ae_entry_type = entry_type; + entry->ae_flags = 0; + + return (entry); +} + +static struct acl_entry * +_acl_duplicate_entry(struct acl *aclp, int entry_index) +{ + int i; + + KASSERT(aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES, + ("aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES")); + + for (i = aclp->acl_cnt; i > entry_index; i--) + aclp->acl_entry[i] = aclp->acl_entry[i - 1]; + + aclp->acl_cnt++; + + return (&(aclp->acl_entry[entry_index + 1])); +} + +void +acl_nfs4_sync_acl_from_mode(struct acl *aclp, mode_t mode, int file_owner_id) +{ + int i, meets, must_append; + struct acl_entry *entry, *copy, *previous, + *a1, *a2, *a3, *a4, *a5, *a6; + mode_t amode; + const int READ = 04; + const int WRITE = 02; + const int EXEC = 01; + + KASSERT(aclp->acl_cnt >= 0, ("aclp->acl_cnt >= 0")); + KASSERT(aclp->acl_cnt <= ACL_MAX_ENTRIES, + ("aclp->acl_cnt <= ACL_MAX_ENTRIES")); + + /* + * NFSv4 Minor Version 1, draft-ietf-nfsv4-minorversion1-03.txt + * + * 3.16.6.3. Applying a Mode to an Existing ACL + */ + + /* + * 1. For each ACE: + */ + for (i = 0; i < aclp->acl_cnt; i++) { + entry = &(aclp->acl_entry[i]); + + /* + * 1.1. If the type is neither ALLOW or DENY - skip. + */ + if (entry->ae_entry_type != ACL_ENTRY_TYPE_ALLOW && + entry->ae_entry_type != ACL_ENTRY_TYPE_DENY) + continue; + + /* + * 1.2. If ACL_ENTRY_INHERIT_ONLY is set - skip. + */ + if (entry->ae_flags & ACL_ENTRY_INHERIT_ONLY) + continue; + + /* + * 1.3. If ACL_ENTRY_FILE_INHERIT or ACL_ENTRY_DIRECTORY_INHERIT + * are set: + */ + if (entry->ae_flags & + (ACL_ENTRY_FILE_INHERIT | ACL_ENTRY_DIRECTORY_INHERIT)) { + /* + * 1.3.1. A copy of the current ACE is made, and placed + * in the ACL immediately following the current + * ACE. + */ + copy = _acl_duplicate_entry(aclp, i); + + /* + * 1.3.2. In the first ACE, the flag + * ACL_ENTRY_INHERIT_ONLY is set. + */ + entry->ae_flags |= ACL_ENTRY_INHERIT_ONLY; + + /* + * 1.3.3. In the second ACE, the following flags + * are cleared: + * ACL_ENTRY_FILE_INHERIT, + * ACL_ENTRY_DIRECTORY_INHERIT, + * ACL_ENTRY_NO_PROPAGATE_INHERIT. + */ + copy->ae_flags &= ~(ACL_ENTRY_FILE_INHERIT | + ACL_ENTRY_DIRECTORY_INHERIT | + ACL_ENTRY_NO_PROPAGATE_INHERIT); + + /* + * The algorithm continues on with the second ACE. + */ + i++; + entry = copy; + } + + /* + * 1.4. If it's owner@, group@ or everyone@ entry, clear + * ACL_READ_DATA, ACL_WRITE_DATA, ACL_APPEND_DATA + * and ACL_EXECUTE. Continue to the next entry. + */ + if (entry->ae_tag == ACL_USER_OBJ || + entry->ae_tag == ACL_GROUP_OBJ || + entry->ae_tag == ACL_EVERYONE) { + entry->ae_perm &= ~(ACL_READ_DATA | ACL_WRITE_DATA | + ACL_APPEND_DATA | ACL_EXECUTE); + continue; + } + + /* + * 1.5. Otherwise, if the "who" field did not match one + * of OWNER@, GROUP@, EVERYONE@: + * + * 1.5.1. If the type is ALLOW, check the preceding ACE. + * If it does not meet all of the following criteria: + */ + if (entry->ae_entry_type != ACL_ENTRY_TYPE_ALLOW) + continue; + + meets = 0; + if (i > 0) { + meets = 1; + previous = &(aclp->acl_entry[i - 1]); + + /* + * 1.5.1.1. The type field is DENY, + */ + if (previous->ae_entry_type != ACL_ENTRY_TYPE_DENY) + meets = 0; + + /* + * 1.5.1.2. The "who" field is the same as the current + * ACE, + * + * 1.5.1.3. The flag bit ACE4_IDENTIFIER_GROUP + * is the same as it is in the current ACE, + * and no other flag bits are set, + */ + if (previous->ae_id != entry->ae_id || + previous->ae_tag != entry->ae_tag) + meets = 0; + + if (previous->ae_flags) + meets = 0; + + /* + * 1.5.1.4. The mask bits are a subset of the mask bits + * of the current ACE, and are also subset of + * the following: ACL_READ_DATA, + * ACL_WRITE_DATA, ACL_APPEND_DATA, ACL_EXECUTE + */ + if (previous->ae_perm & ~(entry->ae_perm)) + meets = 0; + + if (previous->ae_perm & ~(ACL_READ_DATA | + ACL_WRITE_DATA | ACL_APPEND_DATA | ACL_EXECUTE)) + meets = 0; + } + + if (!meets) { + /* + * Then the ACE of type DENY, with a who equal + * to the current ACE, flag bits equal to + * ( & ) + * and no mask bits, is prepended. + */ + previous = entry; + entry = _acl_duplicate_entry(aclp, i); + + /* Adjust counter, as we've just added an entry. */ + i++; + + previous->ae_tag = entry->ae_tag; + previous->ae_id = entry->ae_id; + previous->ae_flags = entry->ae_flags; + previous->ae_perm = 0; + previous->ae_entry_type = ACL_ENTRY_TYPE_DENY; + } + + /* + * 1.5.2. The following modifications are made to the prepended + * ACE. The intent is to mask the following ACE + * to disallow ACL_READ_DATA, ACL_WRITE_DATA, + * ACL_APPEND_DATA, or ACL_EXECUTE, based upon the group + * permissions of the new mode. As a special case, + * if the ACE matches the current owner of the file, + * the owner bits are used, rather than the group bits. + * This is reflected in the algorithm below. + */ + amode = mode >> 3; + + /* + * If ACE4_IDENTIFIER_GROUP is not set, and the "who" field + * in ACE matches the owner of the file, we shift amode three + * more bits, in order to have the owner permission bits + * placed in the three low order bits of amode. + */ + if (entry->ae_tag == ACL_USER && entry->ae_id == file_owner_id) + amode = amode >> 3; + + if (entry->ae_perm & ACL_READ_DATA) { + if (amode & READ) + previous->ae_perm &= ~ACL_READ_DATA; + else + previous->ae_perm |= ACL_READ_DATA; + } + + if (entry->ae_perm & ACL_WRITE_DATA) { + if (amode & WRITE) + previous->ae_perm &= ~ACL_WRITE_DATA; + else + previous->ae_perm |= ACL_WRITE_DATA; + } + + if (entry->ae_perm & ACL_APPEND_DATA) { + if (amode & WRITE) + previous->ae_perm &= ~ACL_APPEND_DATA; + else + previous->ae_perm |= ACL_APPEND_DATA; + } + + if (entry->ae_perm & ACL_EXECUTE) { + if (amode & EXEC) + previous->ae_perm &= ~ACL_EXECUTE; + else + previous->ae_perm |= ACL_EXECUTE; + } + + /* + * 1.5.3. If ACE4_IDENTIFIER_GROUP is set in the flags + * of the ALLOW ace: + * + * XXX: This point is not there in the Falkner's draft. + */ + if (entry->ae_tag == ACL_GROUP && + entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) { + mode_t extramode, ownermode; + extramode = (mode >> 3) & 07; + ownermode = mode >> 6; + extramode &= ~ownermode; + + if (extramode) { + if (extramode & READ) { + entry->ae_perm &= ~ACL_READ_DATA; + previous->ae_perm &= ~ACL_READ_DATA; + } + + if (extramode & WRITE) { + entry->ae_perm &= + ~(ACL_WRITE_DATA | ACL_APPEND_DATA); + previous->ae_perm &= + ~(ACL_WRITE_DATA | ACL_APPEND_DATA); + } + + if (extramode & EXEC) { + entry->ae_perm &= ~ACL_EXECUTE; + previous->ae_perm &= ~ACL_EXECUTE; + } + } + } + } + + /* + * 2. If there at least six ACEs, the final six ACEs are examined. + * If they are not equal to what we want, append six ACEs. + */ + must_append = 0; + if (aclp->acl_cnt < 6) { + must_append = 1; + } else { + a6 = &(aclp->acl_entry[aclp->acl_cnt - 1]); + a5 = &(aclp->acl_entry[aclp->acl_cnt - 2]); + a4 = &(aclp->acl_entry[aclp->acl_cnt - 3]); + a3 = &(aclp->acl_entry[aclp->acl_cnt - 4]); + a2 = &(aclp->acl_entry[aclp->acl_cnt - 5]); + a1 = &(aclp->acl_entry[aclp->acl_cnt - 6]); + + if (!_acl_entry_matches(a1, ACL_USER_OBJ, 0, + ACL_ENTRY_TYPE_DENY)) + must_append = 1; + if (!_acl_entry_matches(a2, ACL_USER_OBJ, ACL_WRITE_ACL | + ACL_WRITE_OWNER | ACL_WRITE_ATTRIBUTES | + ACL_WRITE_NAMED_ATTRS, ACL_ENTRY_TYPE_ALLOW)) + must_append = 1; + if (!_acl_entry_matches(a3, ACL_GROUP_OBJ, 0, + ACL_ENTRY_TYPE_DENY)) + must_append = 1; + if (!_acl_entry_matches(a4, ACL_GROUP_OBJ, 0, + ACL_ENTRY_TYPE_ALLOW)) + must_append = 1; + if (!_acl_entry_matches(a5, ACL_EVERYONE, ACL_WRITE_ACL | + ACL_WRITE_OWNER | ACL_WRITE_ATTRIBUTES | + ACL_WRITE_NAMED_ATTRS, ACL_ENTRY_TYPE_DENY)) + must_append = 1; + if (!_acl_entry_matches(a6, ACL_EVERYONE, ACL_READ_ACL | + ACL_READ_ATTRIBUTES | ACL_READ_NAMED_ATTRS | + ACL_SYNCHRONIZE, ACL_ENTRY_TYPE_ALLOW)) + must_append = 1; + } + + if (must_append) { + KASSERT(aclp->acl_cnt + 6 <= ACL_MAX_ENTRIES, + ("aclp->acl_cnt <= ACL_MAX_ENTRIES")); + + a1 = _acl_append(aclp, ACL_USER_OBJ, 0, ACL_ENTRY_TYPE_DENY); + a2 = _acl_append(aclp, ACL_USER_OBJ, ACL_WRITE_ACL | + ACL_WRITE_OWNER | ACL_WRITE_ATTRIBUTES | + ACL_WRITE_NAMED_ATTRS, ACL_ENTRY_TYPE_ALLOW); + a3 = _acl_append(aclp, ACL_GROUP_OBJ, 0, ACL_ENTRY_TYPE_DENY); + a4 = _acl_append(aclp, ACL_GROUP_OBJ, 0, ACL_ENTRY_TYPE_ALLOW); + a5 = _acl_append(aclp, ACL_EVERYONE, ACL_WRITE_ACL | + ACL_WRITE_OWNER | ACL_WRITE_ATTRIBUTES | + ACL_WRITE_NAMED_ATTRS, ACL_ENTRY_TYPE_DENY); + a6 = _acl_append(aclp, ACL_EVERYONE, ACL_READ_ACL | + ACL_READ_ATTRIBUTES | ACL_READ_NAMED_ATTRS | + ACL_SYNCHRONIZE, ACL_ENTRY_TYPE_ALLOW); + + KASSERT(a1 != NULL && a2 != NULL && a3 != NULL && a4 != NULL && + a5 != NULL && a6 != NULL, ("couldn't append to ACL.")); + } + + /* + * 3. The final six ACEs are adjusted according to the incoming mode. + */ + if (mode & S_IRUSR) + a2->ae_perm |= ACL_READ_DATA; + else + a1->ae_perm |= ACL_READ_DATA; + if (mode & S_IWUSR) + a2->ae_perm |= (ACL_WRITE_DATA | ACL_APPEND_DATA); + else + a1->ae_perm |= (ACL_WRITE_DATA | ACL_APPEND_DATA); + if (mode & S_IXUSR) + a2->ae_perm |= ACL_EXECUTE; + else + a1->ae_perm |= ACL_EXECUTE; + + if (mode & S_IRGRP) + a4->ae_perm |= ACL_READ_DATA; + else + a3->ae_perm |= ACL_READ_DATA; + if (mode & S_IWGRP) + a4->ae_perm |= (ACL_WRITE_DATA | ACL_APPEND_DATA); + else + a3->ae_perm |= (ACL_WRITE_DATA | ACL_APPEND_DATA); + if (mode & S_IXGRP) + a4->ae_perm |= ACL_EXECUTE; + else + a3->ae_perm |= ACL_EXECUTE; + + if (mode & S_IROTH) + a6->ae_perm |= ACL_READ_DATA; + else + a5->ae_perm |= ACL_READ_DATA; + if (mode & S_IWOTH) + a6->ae_perm |= (ACL_WRITE_DATA | ACL_APPEND_DATA); + else + a5->ae_perm |= (ACL_WRITE_DATA | ACL_APPEND_DATA); + if (mode & S_IXOTH) + a6->ae_perm |= ACL_EXECUTE; + else + a5->ae_perm |= ACL_EXECUTE; +} + +void +acl_nfs4_sync_mode_from_acl(mode_t *_mode, const struct acl *aclp) +{ + int i; + mode_t old_mode = *_mode, mode = 0, seen = 0; + const struct acl_entry *entry; + + KASSERT(aclp->acl_cnt > 0, ("aclp->acl_cnt > 0")); + KASSERT(aclp->acl_cnt <= ACL_MAX_ENTRIES, + ("aclp->acl_cnt <= ACL_MAX_ENTRIES")); + + /* + * NFSv4 Minor Version 1, draft-ietf-nfsv4-minorversion1-03.txt + * + * 3.16.6.1. Recomputing mode upon SETATTR of ACL + */ + + for (i = 0; i < aclp->acl_cnt; i++) { + entry = &(aclp->acl_entry[i]); + + if (entry->ae_entry_type != ACL_ENTRY_TYPE_ALLOW && + entry->ae_entry_type != ACL_ENTRY_TYPE_DENY) + continue; + + if (entry->ae_flags & ACL_ENTRY_INHERIT_ONLY) + continue; + + if (entry->ae_tag == ACL_USER_OBJ) { + if ((entry->ae_perm & ACL_READ_DATA) && + ((seen & S_IRUSR) == 0)) { + seen |= S_IRUSR; + if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) + mode |= S_IRUSR; + } + if ((entry->ae_perm & ACL_WRITE_DATA) && + ((seen & S_IWUSR) == 0)) { + seen |= S_IWUSR; + if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) + mode |= S_IWUSR; + } + if ((entry->ae_perm & ACL_EXECUTE) && + ((seen & S_IXUSR) == 0)) { + seen |= S_IXUSR; + if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) + mode |= S_IXUSR; + } + } else if (entry->ae_tag == ACL_GROUP_OBJ) { + if ((entry->ae_perm & ACL_READ_DATA) && + ((seen & S_IRGRP) == 0)) { + seen |= S_IRGRP; + if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) + mode |= S_IRGRP; + } + if ((entry->ae_perm & ACL_WRITE_DATA) && + ((seen & S_IWGRP) == 0)) { + seen |= S_IWGRP; + if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) + mode |= S_IWGRP; + } + if ((entry->ae_perm & ACL_EXECUTE) && + ((seen & S_IXGRP) == 0)) { + seen |= S_IXGRP; + if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) + mode |= S_IXGRP; + } + } else if (entry->ae_tag == ACL_EVERYONE) { + if (entry->ae_perm & ACL_READ_DATA) { + if ((seen & S_IRUSR) == 0) { + seen |= S_IRUSR; + if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) + mode |= S_IRUSR; + } + if ((seen & S_IRGRP) == 0) { + seen |= S_IRGRP; + if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) + mode |= S_IRGRP; + } + if ((seen & S_IROTH) == 0) { + seen |= S_IROTH; + if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) + mode |= S_IROTH; + } + } + if (entry->ae_perm & ACL_WRITE_DATA) { + if ((seen & S_IWUSR) == 0) { + seen |= S_IWUSR; + if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) + mode |= S_IWUSR; + } + if ((seen & S_IWGRP) == 0) { + seen |= S_IWGRP; + if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) + mode |= S_IWGRP; + } + if ((seen & S_IWOTH) == 0) { + seen |= S_IWOTH; + if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) + mode |= S_IWOTH; + } + } + if (entry->ae_perm & ACL_EXECUTE) { + if ((seen & S_IXUSR) == 0) { + seen |= S_IXUSR; + if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) + mode |= S_IXUSR; + } + if ((seen & S_IXGRP) == 0) { + seen |= S_IXGRP; + if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) + mode |= S_IXGRP; + } + if ((seen & S_IXOTH) == 0) { + seen |= S_IXOTH; + if (entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) + mode |= S_IXOTH; + } + } + } + } + + *_mode = mode | (old_mode & ACL_PRESERVE_MASK); +} + +void +acl_nfs4_compute_inherited_acl(const struct acl *parent_aclp, + struct acl *child_aclp, mode_t mode, int file_owner_id, + int is_directory) +{ + int i, flags; + const struct acl_entry *parent_entry; + struct acl_entry *entry, *copy; + + KASSERT(child_aclp->acl_cnt == 0, ("child_aclp->acl_cnt == 0")); + KASSERT(parent_aclp->acl_cnt > 0, ("parent_aclp->acl_cnt > 0")); + KASSERT(parent_aclp->acl_cnt <= ACL_MAX_ENTRIES, + ("parent_aclp->acl_cnt <= ACL_MAX_ENTRIES")); + + /* + * NFSv4 Minor Version 1, draft-ietf-nfsv4-minorversion1-03.txt + * + * 3.16.6.2. Applying the mode given to CREATE or OPEN + * to an inherited ACL + */ + + /* + * 1. Form an ACL that is the concatenation of all inheritable ACEs. + */ + for (i = 0; i < parent_aclp->acl_cnt; i++) { + parent_entry = &(parent_aclp->acl_entry[i]); + flags = parent_entry->ae_flags; + + /* + * Entry is not inheritable at all. + */ + if ((flags & (ACL_ENTRY_DIRECTORY_INHERIT | + ACL_ENTRY_FILE_INHERIT)) == 0) + continue; + + /* + * We're creating a file, but entry is not inheritable + * by files. + */ + if (!is_directory && (flags & ACL_ENTRY_FILE_INHERIT) == 0) + continue; + + /* + * Entry is inheritable only by files, but has NO_PROPAGATE + * flag set, and we're creating a directory, so it wouldn't + * propagate to any file in that directory anyway. + */ + if (is_directory && + (flags & ACL_ENTRY_DIRECTORY_INHERIT) == 0 && + (flags & ACL_ENTRY_NO_PROPAGATE_INHERIT)) + continue; + + KASSERT(child_aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES, + ("child_aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES")); + child_aclp->acl_entry[child_aclp->acl_cnt] = *parent_entry; + child_aclp->acl_cnt++; + } + + /* + * 2. For each entry in the new ACL, adjust its flags, possibly + * creating two entries in place of one. + */ + for (i = 0; i < child_aclp->acl_cnt; i++) { + entry = &(child_aclp->acl_entry[i]); + + /* + * This is not in the specification, but SunOS + * apparently does that. + */ + if (((entry->ae_flags & ACL_ENTRY_NO_PROPAGATE_INHERIT) || + !is_directory) && + entry->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) + entry->ae_perm &= ~(ACL_WRITE_ACL | ACL_WRITE_OWNER); + + /* + * 2.A. If the ACL_ENTRY_NO_PROPAGATE_INHERIT is set, or if the object + * being created is not a directory, then clear the + * following flags: ACL_ENTRY_NO_PROPAGATE_INHERIT, + * ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT, + * ACL_ENTRY_INHERIT_ONLY. + */ + if (entry->ae_flags & ACL_ENTRY_NO_PROPAGATE_INHERIT || + !is_directory) { + entry->ae_flags &= ~(ACL_ENTRY_NO_PROPAGATE_INHERIT | + ACL_ENTRY_FILE_INHERIT | ACL_ENTRY_DIRECTORY_INHERIT | + ACL_ENTRY_INHERIT_ONLY); + + /* + * Continue on to the next ACE. + */ + continue; + } + + /* + * 2.B. If the object is a directory and ACL_ENTRY_FILE_INHERIT + * is set, but ACL_ENTRY_NO_PROPAGATE_INHERIT is not set, ensure + * that ACL_ENTRY_INHERIT_ONLY is set. Continue to the + * next ACE. Otherwise... + */ + /* + * XXX: Read it again and make sure what does the "otherwise" + * apply to. + */ + if (is_directory && + (entry->ae_flags & ACL_ENTRY_FILE_INHERIT) && + ((entry->ae_flags & ACL_ENTRY_DIRECTORY_INHERIT) == 0)) { + entry->ae_flags |= ACL_ENTRY_INHERIT_ONLY; + continue; + } + + /* + * 2.C. If the type of the ACE is neither ALLOW nor deny, + * then continue. + */ + if (entry->ae_entry_type != ACL_ENTRY_TYPE_ALLOW && + entry->ae_entry_type != ACL_ENTRY_TYPE_DENY) + continue; + + /* + * 2.D. Copy the original ACE into a second, adjacent ACE. + */ + copy = _acl_duplicate_entry(child_aclp, i); + + /* + * 2.E. On the first ACE, ensure that ACL_ENTRY_INHERIT_ONLY + * is set. + */ + entry->ae_flags |= ACL_ENTRY_INHERIT_ONLY; + + /* + * 2.F. On the second ACE, clear the following flags: + * ACL_ENTRY_NO_PROPAGATE_INHERIT, ACL_ENTRY_FILE_INHERIT, + * ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_INHERIT_ONLY. + */ + copy->ae_flags &= ~(ACL_ENTRY_NO_PROPAGATE_INHERIT | + ACL_ENTRY_FILE_INHERIT | ACL_ENTRY_DIRECTORY_INHERIT | + ACL_ENTRY_INHERIT_ONLY); + + /* + * 2.G. On the second ACE, if the type is ALLOW, + * an implementation MAY clear the following + * mask bits: ACL_WRITE_ACL, ACL_WRITE_OWNER. + */ + if (copy->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) + copy->ae_perm &= ~(ACL_WRITE_ACL | ACL_WRITE_OWNER); + + /* + * Increment the counter to skip the copied entry. + */ + i++; + } + + /* + * 3. To ensure that the mode is honored, apply the algorithm describe + * in Section 2.16.6.3, using the mode that is to be used for file + * creation. + */ + acl_nfs4_sync_acl_from_mode(child_aclp, mode, file_owner_id); +} + +#ifdef _KERNEL +static int +_acls_are_equal(const struct acl *a, const struct acl *b) +{ + int i; + const struct acl_entry *entrya, *entryb; + + if (a->acl_cnt != b->acl_cnt) + return (0); + + for (i = 0; i < b->acl_cnt; i++) { + entrya = &(a->acl_entry[i]); + entryb = &(b->acl_entry[i]); + + if (entrya->ae_tag != entryb->ae_tag || + entrya->ae_id != entryb->ae_id || + entrya->ae_perm != entryb->ae_perm || + entrya->ae_entry_type != entryb->ae_entry_type || + entrya->ae_flags != entryb->ae_flags) + return (0); + } + + return (1); +} + +/* + * This routine is used to determine whether to remove entry_type attribute + * that stores ACL contents. + */ +int +acl_nfs4_is_trivial(const struct acl *aclp, int file_owner_id) +{ + int trivial; + mode_t tmpmode = 0; + struct acl *tmpaclp; + + if (aclp->acl_cnt != 6) + return (0); + + /* + * Compute the mode from the ACL, then compute new ACL from that mode. + * If the ACLs are identical, then the ACL is trivial. + * + * XXX: I guess there is a faster way to do this. However, even + * this slow implementation significantly speeds things up + * for files that don't have any entry_type ACL entries - it's + * critical for performance to not use EA when they are not + * needed. + */ + tmpaclp = acl_alloc(M_WAITOK | M_ZERO); + acl_nfs4_sync_mode_from_acl(&tmpmode, aclp); + acl_nfs4_sync_acl_from_mode(tmpaclp, tmpmode, file_owner_id); + trivial = _acls_are_equal(aclp, tmpaclp); + acl_free(tmpaclp); + + return (trivial); +} +#endif + +int +acl_nfs4_check(const struct acl *aclp, int is_directory) +{ + int i; + const struct acl_entry *entry; + + /* + * The spec doesn't seem to say anything about ACL validity. + * It seems there is not much to do here. There is even no need + * to count "owner@" or "everyone@" (ACL_USER_OBJ and ACL_EVERYONE) + * entries, as there can be several of them and that's perfectly + * valid. There can be none of them too. Really. + */ + + if (aclp->acl_cnt > ACL_MAX_ENTRIES || aclp->acl_cnt <= 0) + return (EINVAL); + + for (i = 0; i < aclp->acl_cnt; i++) { + entry = &(aclp->acl_entry[i]); + + switch (entry->ae_tag) { + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + case ACL_EVERYONE: + if (entry->ae_id != ACL_UNDEFINED_ID) + return (EINVAL); + break; + + case ACL_USER: + case ACL_GROUP: + if (entry->ae_id == ACL_UNDEFINED_ID) + return (EINVAL); + break; + + default: + return (EINVAL); + } + + if ((entry->ae_perm | ACL_NFS4_PERM_BITS) != ACL_NFS4_PERM_BITS) + return (EINVAL); + + /* + * Disallow ACL_ENTRY_TYPE_AUDIT and ACL_ENTRY_TYPE_ALARM for now. + */ + if (entry->ae_entry_type != ACL_ENTRY_TYPE_ALLOW && + entry->ae_entry_type != ACL_ENTRY_TYPE_DENY) + return (EINVAL); + + if ((entry->ae_flags | ACL_FLAGS_BITS) != ACL_FLAGS_BITS) + return (EINVAL); + + /* Disallow unimplemented flags. */ + if (entry->ae_flags & (ACL_ENTRY_SUCCESSFUL_ACCESS | + ACL_ENTRY_FAILED_ACCESS)) + return (EINVAL); + + /* Disallow flags not allowed for ordinary files. */ + if (!is_directory) { + if (entry->ae_flags & (ACL_ENTRY_FILE_INHERIT | + ACL_ENTRY_DIRECTORY_INHERIT | + ACL_ENTRY_NO_PROPAGATE_INHERIT | ACL_ENTRY_INHERIT_ONLY)) + return (EINVAL); + } + } + + return (0); +} diff -urNp current/sys/kern/vfs_acl.c nfs4acl/sys/kern/vfs_acl.c --- current/sys/kern/vfs_acl.c 2009-05-23 07:50:02.000000000 +0200 +++ nfs4acl/sys/kern/vfs_acl.c 2009-05-30 20:40:35.000000000 +0200 @@ -175,7 +175,7 @@ acl_copyout(struct acl *kernel_acl, void /* * Convert "old" type - ACL_TYPE_{ACCESS,DEFAULT}_OLD - into its "new" - * counterpart. It's required for old (pre-NFS4 ACLs) libc to work + * counterpart. It's required for old (pre-NFSv4 ACLs) libc to work * with new kernel. Fixing 'type' for old binaries with new libc * is being done in lib/libc/posix1e/acl_support.c:_acl_type_unold(). */ @@ -215,8 +215,20 @@ vacl_set_acl(struct thread *td, struct v inkernelacl = acl_alloc(M_WAITOK); error = acl_copyin(aclp, inkernelacl, type); - if (error) + if (error != 0) + goto out; + + /* + * With NFSv4 ACLs, chmod(2) may need to add additional entries. + * Make sure it has enough room for that - splitting every entry + * into two and appending "canonical six" entries at the end. + */ + if (type == ACL_TYPE_NFS4 && + inkernelacl->acl_cnt > (ACL_MAX_ENTRIES - 6) / 2) { + error = ENOSPC; goto out; + } + error = vn_start_write(vp, &mp, V_WAIT | PCATCH); if (error != 0) goto out; @@ -235,7 +247,7 @@ out_unlock: vn_finished_write(mp); out: acl_free(inkernelacl); - return(error); + return (error); } /* @@ -255,12 +267,14 @@ vacl_get_acl(struct thread *td, struct v if (error != 0) goto out; #endif + error = VOP_ACCESSX(vp, VREAD_ACL, td->td_ucred, td); + if (error != 0) + goto out; + error = VOP_GETACL(vp, acl_type_unold(type), inkernelacl, td->td_ucred, td); -#ifdef MAC out: -#endif VOP_UNLOCK(vp, 0); if (error == 0) error = acl_copyout(inkernelacl, aclp, type); @@ -278,12 +292,12 @@ vacl_delete(struct thread *td, struct vn int error; error = vn_start_write(vp, &mp, V_WAIT | PCATCH); - if (error) + if (error != 0) return (error); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); #ifdef MAC error = mac_vnode_check_deleteacl(td->td_ucred, vp, type); - if (error) + if (error != 0) goto out; #endif error = VOP_SETACL(vp, acl_type_unold(type), 0, td->td_ucred, td); @@ -307,9 +321,22 @@ vacl_aclcheck(struct thread *td, struct inkernelacl = acl_alloc(M_WAITOK); error = acl_copyin(aclp, inkernelacl, type); - if (error) + if (error != 0) goto out; - error = VOP_ACLCHECK(vp, type, inkernelacl, td->td_ucred, td); + + /* + * With NFSv4 ACLs, chmod(2) may need to add additional entries. + * Make sure it has enough room for that - splitting every entry + * into two and appending "canonical six" entries at the end. + */ + if (type == ACL_TYPE_NFS4 && + inkernelacl->acl_cnt > (ACL_MAX_ENTRIES - 6) / 2) { + error = ENOSPC; + goto out; + } + + error = VOP_ACLCHECK(vp, acl_type_unold(type), inkernelacl, + td->td_ucred, td); out: acl_free(inkernelacl); return (error); diff -urNp current/sys/kern/vfs_subr.c nfs4acl/sys/kern/vfs_subr.c --- current/sys/kern/vfs_subr.c 2009-06-04 22:03:27.000000000 +0200 +++ nfs4acl/sys/kern/vfs_subr.c 2009-06-04 22:31:18.000000000 +0200 @@ -3641,6 +3641,13 @@ extattr_check_cred(struct vnode *vp, int /* Potentially should be: return (EPERM); */ return (priv_check_cred(cred, PRIV_VFS_EXTATTR_SYSTEM, 0)); case EXTATTR_NAMESPACE_USER: +#ifdef SunOS_doesnt_do_that + if (accmode == VREAD) + return (VOP_ACCESSX(vp, VREAD_NAMED_ATTRS, cred, td)); + if (accmode == VWRITE) + return (VOP_ACCESSX(vp, VWRITE_NAMED_ATTRS, cred, td)); +#endif + /* XXX: Is this possible for "accmode" to not be any of the two above? */ return (VOP_ACCESS(vp, accmode, cred, td)); default: return (EPERM); diff -urNp current/sys/kern/vfs_vnops.c nfs4acl/sys/kern/vfs_vnops.c --- current/sys/kern/vfs_vnops.c 2009-06-04 22:03:27.000000000 +0200 +++ nfs4acl/sys/kern/vfs_vnops.c 2009-06-04 22:31:18.000000000 +0200 @@ -711,6 +711,9 @@ vn_stat(vp, sb, active_cred, file_cred, if (error) return (error); #endif + error = VOP_ACCESSX(vp, VREAD_ATTRIBUTES, active_cred, td); + if (error) + return (error); vap = &vattr; diff -urNp current/sys/netgraph/bluetooth/drivers/ubt/ng_ubt_var.h nfs4acl/sys/netgraph/bluetooth/drivers/ubt/ng_ubt_var.h --- current/sys/netgraph/bluetooth/drivers/ubt/ng_ubt_var.h 2009-05-29 19:54:24.000000000 +0200 +++ nfs4acl/sys/netgraph/bluetooth/drivers/ubt/ng_ubt_var.h 2009-05-29 20:25:43.000000000 +0200 @@ -28,7 +28,7 @@ * SUCH DAMAGE. * * $Id: ng_ubt_var.h,v 1.2 2003/03/22 23:44:36 max Exp $ - * $FreeBSD: src/sys/netgraph/bluetooth/drivers/ubt/ng_ubt_var.h,v 1.11 2009/05/28 17:36:36 thompsa Exp $ + * $FreeBSD: head/sys/netgraph/bluetooth/drivers/ubt/ng_ubt_var.h 192984 2009-05-28 17:36:36Z thompsa $ */ #ifndef _NG_UBT_VAR_H_ diff -urNp current/sys/security/mac_lomac/mac_lomac.c nfs4acl/sys/security/mac_lomac/mac_lomac.c --- current/sys/security/mac_lomac/mac_lomac.c 2009-06-04 22:03:36.000000000 +0200 +++ nfs4acl/sys/security/mac_lomac/mac_lomac.c 2009-06-04 22:31:27.000000000 +0200 @@ -2469,7 +2469,7 @@ lomac_vnode_check_open(struct ucred *cre obj = SLOT(vplabel); /* XXX privilege override for admin? */ - if (accmode & (VWRITE | VAPPEND | VADMIN)) { + if (accmode & VMODIFY_PERMS) { if (!lomac_subject_dominate(subj, obj)) return (EACCES); } diff -urNp current/sys/sys/acl.h nfs4acl/sys/sys/acl.h --- current/sys/sys/acl.h 2009-05-23 19:03:08.000000000 +0200 +++ nfs4acl/sys/sys/acl.h 2009-05-26 17:15:44.000000000 +0200 @@ -128,8 +128,15 @@ struct acl { struct acl_t_struct { struct acl ats_acl; int ats_cur_entry; - /* Will be used for ACL branding. */ - int ats_spare; + /* + * ats_brand is for libc internal bookkeeping only. + * Applications should use acl_get_brand_np(3). + * Kernel code should use the "type" argument passed + * to VOP_SETACL, VOP_GETACL or VOP_ACLCHECK calls; + * ACL_TYPE_ACCESS or ACL_TYPE_DEFAULT mean POSIX.1e + * ACL, ACL_TYPE_NFS4 means NFSv4 ACL. + */ + int ats_brand; }; typedef struct acl_t_struct *acl_t; @@ -141,6 +148,13 @@ typedef void *acl_t; #endif /* !_KERNEL && !_ACL_PRIVATE */ /* + * Possible valid values for ats_brand field. + */ +#define ACL_BRAND_UNKNOWN 0 +#define ACL_BRAND_POSIX 1 +#define ACL_BRAND_NFS4 2 + +/* * Possible valid values for ae_tag field. For explanation, see acl(9). */ #define ACL_UNDEFINED_TAG 0x00000000 @@ -236,7 +250,12 @@ typedef void *acl_t; */ #define ACL_UNDEFINED_ID ((uid_t)-1) -#ifdef _KERNEL +/* + * Possible values for _flags parameter in acl_to_text_np(3). + */ +#define ACL_TEXT_VERBOSE 0x01 +#define ACL_TEXT_NUMERIC_IDS 0x02 +#define ACL_TEXT_APPEND_ID 0x04 /* * POSIX.1e ACLs are capable of expressing the read, write, and execute bits @@ -247,6 +266,8 @@ typedef void *acl_t; #define ACL_OVERRIDE_MASK (S_IRWXU | S_IRWXG | S_IRWXO) #define ACL_PRESERVE_MASK (~ACL_OVERRIDE_MASK) +#ifdef _KERNEL + /* * Filesystem-independent code to move back and forth between POSIX mode and * POSIX.1e ACL representations. @@ -263,6 +284,17 @@ mode_t acl_posix1e_newfilemode(mode_t struct acl *dacl); struct acl *acl_alloc(int flags); void acl_free(struct acl *aclp); + +void acl_nfs4_sync_acl_from_mode(struct acl *aclp, + mode_t mode, int file_owner_id); +void acl_nfs4_sync_mode_from_acl(mode_t *mode, + const struct acl *aclp); +int acl_nfs4_is_trivial(const struct acl *aclp, + int file_owner_id); +void acl_nfs4_compute_inherited_acl( + const struct acl *parent_aclp, + struct acl *child_aclp, mode_t mode, + int file_owner_id, int is_directory); int acl_copy_oldacl_into_acl(const struct oldacl *source, struct acl *dest); int acl_copy_acl_into_oldacl(const struct acl *source, @@ -272,11 +304,11 @@ int acl_copy_acl_into_oldacl(const str * To allocate 'struct acl', use acl_alloc()/acl_free() instead of this. */ MALLOC_DECLARE(M_ACL); - /* * Filesystem-independent syntax check for a POSIX.1e ACL. */ int acl_posix1e_check(struct acl *acl); +int acl_nfs4_check(const struct acl *aclp, int is_directory); #else /* !_KERNEL */ @@ -312,46 +344,60 @@ __END_DECLS * filesystems (i.e., AFS). */ __BEGIN_DECLS +int acl_add_flag_np(acl_flagset_t _flagset_d, acl_flag_t _flag); int acl_add_perm(acl_permset_t _permset_d, acl_perm_t _perm); int acl_calc_mask(acl_t *_acl_p); +int acl_clear_flags_np(acl_flagset_t _flagset_d); int acl_clear_perms(acl_permset_t _permset_d); int acl_copy_entry(acl_entry_t _dest_d, acl_entry_t _src_d); ssize_t acl_copy_ext(void *_buf_p, acl_t _acl, ssize_t _size); acl_t acl_copy_int(const void *_buf_p); int acl_create_entry(acl_t *_acl_p, acl_entry_t *_entry_p); +int acl_create_entry_np(acl_t *_acl_p, acl_entry_t *_entry_p, int _index); int acl_delete_entry(acl_t _acl, acl_entry_t _entry_d); +int acl_delete_entry_np(acl_t _acl, int _index); int acl_delete_fd_np(int _filedes, acl_type_t _type); int acl_delete_file_np(const char *_path_p, acl_type_t _type); int acl_delete_link_np(const char *_path_p, acl_type_t _type); int acl_delete_def_file(const char *_path_p); int acl_delete_def_link_np(const char *_path_p); +int acl_delete_flag_np(acl_flagset_t _flagset_d, acl_flag_t _flag); int acl_delete_perm(acl_permset_t _permset_d, acl_perm_t _perm); acl_t acl_dup(acl_t _acl); int acl_free(void *_obj_p); acl_t acl_from_text(const char *_buf_p); +int acl_get_brand_np(acl_t _acl, int *_brand_p); int acl_get_entry(acl_t _acl, int _entry_id, acl_entry_t *_entry_p); acl_t acl_get_fd(int _fd); acl_t acl_get_fd_np(int fd, acl_type_t _type); acl_t acl_get_file(const char *_path_p, acl_type_t _type); +int acl_get_entry_type_np(acl_entry_t _entry_d, acl_entry_type_t *_entry_type_p); acl_t acl_get_link_np(const char *_path_p, acl_type_t _type); void *acl_get_qualifier(acl_entry_t _entry_d); +int acl_get_flag_np(acl_flagset_t _flagset_d, acl_flag_t _flag); int acl_get_perm_np(acl_permset_t _permset_d, acl_perm_t _perm); +int acl_get_flagset_np(acl_entry_t _entry_d, acl_flagset_t *_flagset_p); int acl_get_permset(acl_entry_t _entry_d, acl_permset_t *_permset_p); int acl_get_tag_type(acl_entry_t _entry_d, acl_tag_t *_tag_type_p); acl_t acl_init(int _count); int acl_set_fd(int _fd, acl_t _acl); int acl_set_fd_np(int _fd, acl_t _acl, acl_type_t _type); int acl_set_file(const char *_path_p, acl_type_t _type, acl_t _acl); +int acl_set_entry_type_np(acl_entry_t _entry_d, acl_entry_type_t _entry_type); int acl_set_link_np(const char *_path_p, acl_type_t _type, acl_t _acl); +int acl_set_flagset_np(acl_entry_t _entry_d, acl_flagset_t _flagset_d); int acl_set_permset(acl_entry_t _entry_d, acl_permset_t _permset_d); int acl_set_qualifier(acl_entry_t _entry_d, const void *_tag_qualifier_p); int acl_set_tag_type(acl_entry_t _entry_d, acl_tag_t _tag_type); ssize_t acl_size(acl_t _acl); char *acl_to_text(acl_t _acl, ssize_t *_len_p); +char *acl_to_text_np(acl_t _acl, ssize_t *_len_p, int _flags); int acl_valid(acl_t _acl); int acl_valid_fd_np(int _fd, acl_type_t _type, acl_t _acl); int acl_valid_file_np(const char *_path_p, acl_type_t _type, acl_t _acl); int acl_valid_link_np(const char *_path_p, acl_type_t _type, acl_t _acl); +int acl_is_trivial_np(const acl_t _acl, int *_trivialp); +acl_t acl_strip_np(const acl_t _acl, int recalculate_mask); __END_DECLS #endif /* !_KERNEL */ diff -urNp current/sys/sys/mount.h nfs4acl/sys/sys/mount.h --- current/sys/sys/mount.h 2009-06-04 22:03:36.000000000 +0200 +++ nfs4acl/sys/sys/mount.h 2009-06-04 22:31:28.000000000 +0200 @@ -239,6 +239,7 @@ void __mnt_vnode_markerfree(str #define MNT_NOATIME 0x10000000 /* disable update of file access time */ #define MNT_NOCLUSTERR 0x40000000 /* disable cluster read */ #define MNT_NOCLUSTERW 0x80000000 /* disable cluster write */ +#define MNT_NFS4ACLS 0x00000010 /* * NFS export related mount flags. @@ -274,7 +275,7 @@ void __mnt_vnode_markerfree(str MNT_ROOTFS | MNT_NOATIME | MNT_NOCLUSTERR| \ MNT_NOCLUSTERW | MNT_SUIDDIR | MNT_SOFTDEP | \ MNT_IGNORE | MNT_EXPUBLIC | MNT_NOSYMFOLLOW | \ - MNT_GJOURNAL | MNT_MULTILABEL | MNT_ACLS) + MNT_GJOURNAL | MNT_MULTILABEL | MNT_ACLS | MNT_NFS4ACLS) /* Mask of flags that can be updated. */ #define MNT_UPDATEMASK (MNT_NOSUID | MNT_NOEXEC | \ @@ -282,7 +283,7 @@ void __mnt_vnode_markerfree(str MNT_NOATIME | \ MNT_NOSYMFOLLOW | MNT_IGNORE | \ MNT_NOCLUSTERR | MNT_NOCLUSTERW | MNT_SUIDDIR | \ - MNT_ACLS | MNT_USER) + MNT_ACLS | MNT_USER | MNT_NFS4ACLS) /* * External filesystem command modifier flags. @@ -300,10 +301,6 @@ void __mnt_vnode_markerfree(str #define MNT_CMDFLAGS (MNT_UPDATE | MNT_DELEXPORT | MNT_RELOAD | \ MNT_FORCE | MNT_SNAPSHOT | MNT_BYFSID) /* - * Still available. - */ -#define MNT_SPARE_0x00000010 0x00000010 -/* * Internal filesystem control flags stored in mnt_kern_flag. * * MNTK_UNMOUNT locks the mount entry so that name lookup cannot proceed diff -urNp current/sys/sys/unistd.h nfs4acl/sys/sys/unistd.h --- current/sys/sys/unistd.h 2009-04-29 09:29:27.000000000 +0200 +++ nfs4acl/sys/sys/unistd.h 2009-04-29 09:37:25.000000000 +0200 @@ -153,6 +153,7 @@ #define _PC_CAP_PRESENT 61 #define _PC_INF_PRESENT 62 #define _PC_MAC_PRESENT 63 +#define _PC_EXTENDED_SECURITY_NP 64 #endif /* From OpenSolaris, used by SEEK_DATA/SEEK_HOLE. */ diff -urNp current/sys/sys/vnode.h nfs4acl/sys/sys/vnode.h --- current/sys/sys/vnode.h 2009-06-04 22:03:36.000000000 +0200 +++ nfs4acl/sys/sys/vnode.h 2009-06-09 20:15:09.000000000 +0200 @@ -317,7 +317,7 @@ struct vattr { #define VAPPEND 000000040000 /* permission to write/append */ /* * VEXPLICIT_DENY makes VOP_ACCESS(9) return EPERM or EACCES only - * if permission was denied explicitly, by a "deny" rule in NFS4 ACL, + * if permission was denied explicitly, by a "deny" rule in NFSv4 ACL, * and 0 otherwise. This never happens with ordinary unix access rights * or POSIX.1e ACLs. Obviously, VEXPLICIT_DENY must be OR-ed with * some other V* constant. @@ -612,6 +612,9 @@ int vaccess(enum vtype type, mode_t file int vaccess_acl_posix1e(enum vtype type, uid_t file_uid, gid_t file_gid, struct acl *acl, accmode_t accmode, struct ucred *cred, int *privused); +int vaccess_acl_nfs4(enum vtype type, uid_t file_uid, + gid_t file_gid, struct acl *acl, accmode_t accmode, + struct ucred *cred, int *privused); void vattr_null(struct vattr *vap); int vcount(struct vnode *vp); void vdrop(struct vnode *); diff -urNp current/sys/ufs/ffs/ffs_vfsops.c nfs4acl/sys/ufs/ffs/ffs_vfsops.c --- current/sys/ufs/ffs/ffs_vfsops.c 2009-05-17 13:05:56.000000000 +0200 +++ nfs4acl/sys/ufs/ffs/ffs_vfsops.c 2009-05-22 18:33:33.000000000 +0200 @@ -129,7 +129,7 @@ static struct buf_ops ffs_ops = { static const char *ffs_opts[] = { "acls", "async", "noatime", "noclusterr", "noclusterw", "noexec", "export", "force", "from", "multilabel", "snapshot", "nosuid", "suiddir", "nosymfollow", "sync", - "union", NULL }; + "union", "nfs4acls", NULL }; static int ffs_mount(struct mount *mp) @@ -178,6 +178,14 @@ ffs_mount(struct mount *mp) vfs_deleteopt(mp->mnt_opt, "snapshot"); } + if (vfs_getopt(mp->mnt_optnew, "nfs4acls", NULL, NULL) == 0) { + if (mntorflags & MNT_ACLS) { + printf("WARNING: both acls and nfs4acls specified\n"); + return (EINVAL); + } + mntorflags |= MNT_NFS4ACLS; + } + MNT_ILOCK(mp); mp->mnt_flag = (mp->mnt_flag | mntorflags) & ~mntandnotflags; MNT_IUNLOCK(mp); @@ -835,7 +843,12 @@ ffs_mountfs(devvp, mp, td) if ((fs->fs_flags & FS_ACLS) != 0) { #ifdef UFS_ACL MNT_ILOCK(mp); - mp->mnt_flag |= MNT_ACLS; + + if (mp->mnt_flag | MNT_NFS4ACLS) + printf("WARNING: ACLs flag on fs conflicts with nfs4acls mount option; flag ignored\n"); + else + mp->mnt_flag |= MNT_ACLS; + MNT_IUNLOCK(mp); #else printf( diff -urNp current/sys/ufs/ufs/ufs_acl.c nfs4acl/sys/ufs/ufs/ufs_acl.c --- current/sys/ufs/ufs/ufs_acl.c 2009-05-23 07:50:07.000000000 +0200 +++ nfs4acl/sys/ufs/ufs/ufs_acl.c 2009-05-30 20:40:35.000000000 +0200 @@ -140,6 +140,62 @@ ufs_sync_inode_from_acl(struct acl *acl, DIP_SET(ip, i_mode, ip->i_mode); } +static int +ufs_getacl_nfs4(struct vop_getacl_args *ap) +{ + int error, len; + struct inode *ip = VTOI(ap->a_vp); + + if ((ap->a_vp->v_mount->mnt_flag & MNT_NFS4ACLS) == 0) + return (EOPNOTSUPP); + + len = sizeof(*ap->a_aclp); + bzero(ap->a_aclp, len); + + error = vn_extattr_get(ap->a_vp, IO_NODELOCKED, + NFS4_ACL_EXTATTR_NAMESPACE, + NFS4_ACL_EXTATTR_NAME, &len, (char *) ap->a_aclp, + ap->a_td); + ap->a_aclp->acl_maxcnt = ACL_MAX_ENTRIES; + if (error == ENOATTR) { + /* + * Legitimately no ACL set on object, purely + * emulate it through the inode. + */ + acl_nfs4_sync_acl_from_mode(ap->a_aclp, ip->i_mode, ip->i_uid); + + return (0); + } + + if (error) + return (error); + + if (len != sizeof(*ap->a_aclp)) { + /* + * A short (or long) read, meaning that for + * some reason the ACL is corrupted. Return + * EPERM since the object DAC protections + * are unsafe. + */ + printf("ufs_getacl_nfs4(): Loaded invalid ACL (" + "%d bytes), inumber %d on %s\n", len, + ip->i_number, ip->i_fs->fs_fsmnt); + + return (EPERM); + } + + error = acl_nfs4_check(ap->a_aclp, ap->a_vp->v_type == VDIR); + if (error) { + printf("ufs_getacl_nfs4(): Loaded invalid ACL " + "(failed acl_nfs4_check), inumber %d on %s\n", + ip->i_number, ip->i_fs->fs_fsmnt); + + return (EPERM); + } + + return (0); +} + /* * Read POSIX.1e ACL from an EA. Return error if its not found * or if any other error has occured. @@ -285,9 +341,89 @@ ufs_getacl(ap) } */ *ap; { + if (ap->a_type == ACL_TYPE_NFS4) + return (ufs_getacl_nfs4(ap)); + return (ufs_getacl_posix1e(ap)); } +static int +ufs_setacl_nfs4(struct vop_setacl_args *ap) +{ + int error; + mode_t mode; + struct inode *ip = VTOI(ap->a_vp); + + if ((ap->a_vp->v_mount->mnt_flag & MNT_NFS4ACLS) == 0) + return (EOPNOTSUPP); + + if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) + return (EROFS); + + if (ap->a_aclp == NULL) + return (EINVAL); + + error = VOP_ACLCHECK(ap->a_vp, ap->a_type, ap->a_aclp, ap->a_cred, + ap->a_td); + if (error) + return (error); + + /* + * Authorize the ACL operation. + */ + if (ip->i_flags & (IMMUTABLE | APPEND)) + return (EPERM); + + /* + * Must hold VWRITE_ACL or have appropriate privilege. + */ + if ((error = VOP_ACCESSX(ap->a_vp, VWRITE_ACL, ap->a_cred, ap->a_td))) + return (error); + + if (acl_nfs4_is_trivial(ap->a_aclp, ip->i_uid)) { + error = vn_extattr_rm(ap->a_vp, IO_NODELOCKED, + NFS4_ACL_EXTATTR_NAMESPACE, + NFS4_ACL_EXTATTR_NAME, ap->a_td); + + /* + * An attempt to remove ACL from a file that didn't have + * any extended entries is not an error. + */ + if (error == ENOATTR) + error = 0; + + } else { + error = vn_extattr_set(ap->a_vp, IO_NODELOCKED, + NFS4_ACL_EXTATTR_NAMESPACE, + NFS4_ACL_EXTATTR_NAME, + sizeof(*ap->a_aclp), + (char *) ap->a_aclp, ap->a_td); + } + + /* + * Map lack of attribute definition in UFS_EXTATTR into lack of + * support for ACLs on the filesystem. + */ + if (error == ENOATTR) + return (EOPNOTSUPP); + + if (error) + return (error); + + mode = ip->i_mode; + + acl_nfs4_sync_mode_from_acl(&mode, ap->a_aclp); + + ip->i_mode &= ACL_PRESERVE_MASK; + ip->i_mode |= mode; + DIP_SET(ip, i_mode, ip->i_mode); + ip->i_flag |= IN_CHANGE; + + VN_KNOTE_UNLOCKED(ap->a_vp, NOTE_ATTRIB); + + return (0); +} + /* * Set the ACL on a file. * @@ -426,10 +562,27 @@ ufs_setacl(ap) } */ *ap; { + if (ap->a_type == ACL_TYPE_NFS4) + return (ufs_setacl_nfs4(ap)); + return (ufs_setacl_posix1e(ap)); } static int +ufs_aclcheck_nfs4(struct vop_aclcheck_args *ap) +{ + int is_directory = 0; + + if ((ap->a_vp->v_mount->mnt_flag & MNT_NFS4ACLS) == 0) + return (EOPNOTSUPP); + + if (ap->a_vp->v_type == VDIR) + is_directory = 1; + + return (acl_nfs4_check(ap->a_aclp, is_directory)); +} + +static int ufs_aclcheck_posix1e(struct vop_aclcheck_args *ap) { @@ -474,6 +627,9 @@ ufs_aclcheck(ap) } */ *ap; { + if (ap->a_type == ACL_TYPE_NFS4) + return (ufs_aclcheck_nfs4(ap)); + return (ufs_aclcheck_posix1e(ap)); } diff -urNp current/sys/ufs/ufs/ufs_lookup.c nfs4acl/sys/ufs/ufs/ufs_lookup.c --- current/sys/ufs/ufs/ufs_lookup.c 2009-04-29 09:29:29.000000000 +0200 +++ nfs4acl/sys/ufs/ufs/ufs_lookup.c 2009-05-30 20:40:36.000000000 +0200 @@ -80,6 +80,61 @@ SYSCTL_INT(_debug, OID_AUTO, dircheck, C static int ufs_lookup_(struct vnode *, struct vnode **, struct componentname *, ino_t *); +static int +ufs_delete_denied(struct vnode *vdp, struct vnode *tdp, struct ucred *cred, + struct thread *td) +{ + int error; + +#ifdef UFS_ACL + /* + * NFSv4 Minor Version 1, draft-ietf-nfsv4-minorversion1-03.txt + * + * 3.16.2.1. ACE4_DELETE vs. ACE4_DELETE_CHILD + */ + + /* + * XXX: Is this check required? + */ + error = VOP_ACCESS(vdp, VEXEC, cred, td); + if (error) + return (error); + + error = VOP_ACCESSX(tdp, VDELETE, cred, td); + if (error == 0) + return (0); + + error = VOP_ACCESSX(vdp, VDELETE_CHILD, cred, td); + if (error == 0) + return (0); + + error = VOP_ACCESSX(vdp, VEXPLICIT_DENY | VDELETE_CHILD, cred, td); + if (error) + return (error); + +#endif /* !UFS_ACL */ + + /* + * Standard Unix access control - delete access requires VWRITE. + */ + error = VOP_ACCESS(vdp, VWRITE, cred, td); + if (error) + return (error); + + /* + * If directory is "sticky", then user must own + * the directory, or the file in it, else she + * may not delete it (unless she's root). This + * implements append-only directories. + */ + if ((VTOI(vdp)->i_mode & ISVTX) && + VOP_ACCESS(vdp, VADMIN, cred, td) && + VOP_ACCESS(tdp, VADMIN, cred, td)) + return (EPERM); + + return (0); +} + /* * Convert a component of a pathname into a pointer to a locked inode. * This is a very central and rather complicated routine. @@ -410,8 +465,13 @@ notfound: /* * Access for write is interpreted as allowing * creation of files in the directory. + * + * XXX: Fix the comment above. */ - error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread); + if (flags & WILLBEDIR) + error = VOP_ACCESSX(vdp, VAPPEND, cred, cnp->cn_thread); + else + error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread); if (error) return (error); /* @@ -498,12 +558,17 @@ found: if (nameiop == DELETE && (flags & ISLASTCN)) { if (flags & LOCKPARENT) ASSERT_VOP_ELOCKED(vdp, __FUNCTION__); - /* - * Write access to directory required to delete files. - */ - error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread); - if (error) + if ((error = VFS_VGET(vdp->v_mount, ino, + LK_EXCLUSIVE, &tdp)) != 0) return (error); + + error = ufs_delete_denied(vdp, tdp, cred, cnp->cn_thread); + if (error) { + vput(tdp); + return (error); + } + + /* * Return pointer to current entry in dp->i_offset, * and distance past previous entry (if there @@ -523,23 +588,10 @@ found: if (dp->i_number == ino) { VREF(vdp); *vpp = vdp; - return (0); - } - if ((error = VFS_VGET(vdp->v_mount, ino, - LK_EXCLUSIVE, &tdp)) != 0) - return (error); - /* - * If directory is "sticky", then user must own - * the directory, or the file in it, else she - * may not delete it (unless she's root). This - * implements append-only directories. - */ - if ((dp->i_mode & ISVTX) && - VOP_ACCESS(vdp, VADMIN, cred, cnp->cn_thread) && - VOP_ACCESS(tdp, VADMIN, cred, cnp->cn_thread)) { vput(tdp); - return (EPERM); + return (0); } + *vpp = tdp; return (0); } @@ -551,7 +603,11 @@ found: * regular file, or empty directory. */ if (nameiop == RENAME && (flags & ISLASTCN)) { - if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread))) + if (flags & WILLBEDIR) + error = VOP_ACCESSX(vdp, VAPPEND, cred, cnp->cn_thread); + else + error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread); + if (error) return (error); /* * Careful about locking second inode. @@ -563,6 +619,33 @@ found: if ((error = VFS_VGET(vdp->v_mount, ino, LK_EXCLUSIVE, &tdp)) != 0) return (error); + + error = ufs_delete_denied(vdp, tdp, cred, cnp->cn_thread); + if (error) { + vput(tdp); + return (error); + } + +#ifdef SunOS_doesnt_do_that + /* + * The only purpose of this check is to return the correct + * error. Assume that we want to rename directory "a" + * to a file "b", and that we have no ACL_WRITE_DATA on + * a containing directory, but we _do_ have ACL_APPEND_DATA. + * In that case, the VOP_ACCESS check above will return 0, + * and the operation will fail with ENOTDIR instead + * of EACCESS. + */ + if (tdp->v_type == VDIR) + error = VOP_ACCESSX(vdp, VAPPEND, cred, cnp->cn_thread); + else + error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread); + if (error) { + vput(tdp); + return (error); + } +#endif + *vpp = tdp; cnp->cn_flags |= SAVENAME; return (0); diff -urNp current/sys/ufs/ufs/ufs_vnops.c nfs4acl/sys/ufs/ufs/ufs_vnops.c --- current/sys/ufs/ufs/ufs_vnops.c 2009-05-29 19:54:25.000000000 +0200 +++ nfs4acl/sys/ufs/ufs/ufs_vnops.c 2009-05-30 20:40:36.000000000 +0200 @@ -89,7 +89,7 @@ __FBSDID("$FreeBSD: src/sys/ufs/ufs/ufs_ #include -static vop_access_t ufs_access; +static vop_accessx_t ufs_accessx; static int ufs_chmod(struct vnode *, int, struct ucred *, struct thread *); static int ufs_chown(struct vnode *, uid_t, gid_t, struct ucred *, struct thread *); static vop_close_t ufs_close; @@ -298,8 +298,8 @@ ufs_close(ap) } static int -ufs_access(ap) - struct vop_access_args /* { +ufs_accessx(ap) + struct vop_accessx_args /* { struct vnode *a_vp; accmode_t a_accmode; struct ucred *a_cred; @@ -315,6 +315,7 @@ ufs_access(ap) #endif #ifdef UFS_ACL struct acl *acl; + acl_type_t type; #endif /* @@ -322,7 +323,7 @@ ufs_access(ap) * unless the file is a socket, fifo, or a block or * character device resident on the filesystem. */ - if (accmode & VWRITE) { + if (accmode & VMODIFY_PERMS) { switch (vp->v_type) { case VDIR: case VLNK: @@ -367,14 +368,24 @@ relock: } } - /* If immutable bit set, nobody gets to write it. */ - if ((accmode & VWRITE) && (ip->i_flags & (IMMUTABLE | SF_SNAPSHOT))) + /* + * If immutable bit set, nobody gets to write it. + * "& ~VADMIN_PERMS" is here, because without it, + * it would be impossible to remove the IMMUTABLE flag. + */ + if ((accmode & (VMODIFY_PERMS & ~VADMIN_PERMS)) && + (ip->i_flags & (IMMUTABLE | SF_SNAPSHOT))) return (EPERM); #ifdef UFS_ACL - if ((vp->v_mount->mnt_flag & MNT_ACLS) != 0) { + if ((vp->v_mount->mnt_flag & (MNT_ACLS | MNT_NFS4ACLS)) != 0) { + if (vp->v_mount->mnt_flag & MNT_NFS4ACLS) + type = ACL_TYPE_NFS4; + else + type = ACL_TYPE_ACCESS; + acl = acl_alloc(M_WAITOK); - error = VOP_GETACL(vp, ACL_TYPE_ACCESS, acl, ap->a_cred, + error = VOP_GETACL(vp, type, acl, ap->a_cred, ap->a_td); switch (error) { case EOPNOTSUPP: @@ -382,12 +393,17 @@ relock: ip->i_gid, ap->a_accmode, ap->a_cred, NULL); break; case 0: - error = vaccess_acl_posix1e(vp->v_type, ip->i_uid, - ip->i_gid, acl, ap->a_accmode, ap->a_cred, NULL); + if (type == ACL_TYPE_NFS4) { + error = vaccess_acl_nfs4(vp->v_type, ip->i_uid, + ip->i_gid, acl, ap->a_accmode, ap->a_cred, NULL); + } else { + error = vaccess_acl_posix1e(vp->v_type, ip->i_uid, + ip->i_gid, acl, ap->a_accmode, ap->a_cred, NULL); + } break; default: printf( -"ufs_access(): Error retrieving ACL on object (%d).\n", +"ufs_accessx(): Error retrieving ACL on object (%d).\n", error); /* * XXX: Fall back until debugged. Should @@ -608,11 +624,20 @@ ufs_setattr(ap) * check succeeds. */ if (vap->va_vaflags & VA_UTIMES_NULL) { - error = VOP_ACCESS(vp, VADMIN, cred, td); + /* + * NFSv4.1, draft 21, 6.2.1.3.1, Discussion of Mask Attributes + * + * "A user having ACL_WRITE_DATA or ACL_WRITE_ATTRIBUTES + * will be allowed to set the times [..] to the current + * server time." + * + * XXX: Calling it four times seems a little excessive. + */ + error = VOP_ACCESSX(vp, VWRITE_ATTRIBUTES, cred, td); if (error) error = VOP_ACCESS(vp, VWRITE, cred, td); } else - error = VOP_ACCESS(vp, VADMIN, cred, td); + error = VOP_ACCESSX(vp, VWRITE_ATTRIBUTES, cred, td); if (error) return (error); if (vap->va_atime.tv_sec != VNOVAL) @@ -652,6 +677,35 @@ ufs_setattr(ap) return (error); } +#ifdef UFS_ACL +static int +ufs_update_nfs4_acl_after_mode_change(struct vnode *vp, int mode, + int file_owner_id, struct ucred *cred, struct thread *td) +{ + int error; + struct acl *aclp; + + aclp = acl_alloc(M_WAITOK); + + error = VOP_GETACL(vp, ACL_TYPE_NFS4, aclp, cred, td); + /* + * We cannot get EOPNOTSUPP here, as the filesystem claims + * to support ACLs. + */ + if (error) + goto out; + + acl_nfs4_sync_acl_from_mode(aclp, mode, file_owner_id); + + error = VOP_SETACL(vp, ACL_TYPE_NFS4, aclp, cred, td); + +out: + acl_free(aclp); + + return (error); +} +#endif /* UFS_ACL */ + /* * Mark this file's access time for update for vfs_mark_atime(). This * is called from execve() and mmap(). @@ -689,7 +743,7 @@ ufs_chmod(vp, mode, cred, td) * To modify the permissions on a file, must possess VADMIN * for that file. */ - if ((error = VOP_ACCESS(vp, VADMIN, cred, td))) + if ((error = VOP_ACCESSX(vp, VWRITE_ACL, cred, td))) return (error); /* * Privileged processes may set the sticky bit on non-directories, @@ -706,11 +760,25 @@ ufs_chmod(vp, mode, cred, td) if (error) return (error); } + + /* + * Deny setting setuid if we are not the file owner. + */ + if ((mode & ISUID) && ip->i_uid != cred->cr_uid) { + error = priv_check_cred(cred, PRIV_VFS_ADMIN, 0); + if (error) + return (error); + } + ip->i_mode &= ~ALLPERMS; ip->i_mode |= (mode & ALLPERMS); DIP_SET(ip, i_mode, ip->i_mode); ip->i_flag |= IN_CHANGE; - return (0); +#ifdef UFS_ACL + if ((vp->v_mount->mnt_flag & MNT_NFS4ACLS) != 0) + error = ufs_update_nfs4_acl_after_mode_change(vp, mode, ip->i_uid, cred, td); +#endif + return (error); } /* @@ -742,14 +810,14 @@ ufs_chown(vp, uid, gid, cred, td) * To modify the ownership of a file, must possess VADMIN for that * file. */ - if ((error = VOP_ACCESS(vp, VADMIN, cred, td))) + if ((error = VOP_ACCESSX(vp, VWRITE_OWNER, cred, td))) return (error); /* * To change the owner of a file, or change the group of a file to a * group of which we are not a member, the caller must have * privilege. */ - if ((uid != ip->i_uid || + if (((uid != ip->i_uid && uid != cred->cr_uid) || (gid != ip->i_gid && !groupmember(gid, cred))) && (error = priv_check_cred(cred, PRIV_VFS_CHOWN, 0))) return (error); @@ -1397,6 +1465,36 @@ out: return (error); } +#ifdef UFS_ACL +static int +ufs_do_nfs4_acl_inheritance(struct vnode *dvp, struct vnode *tvp, + mode_t child_mode, struct ucred *cred, struct thread *td) +{ + int error; + struct acl *parent_aclp, *child_aclp; + + parent_aclp = acl_alloc(M_WAITOK); + child_aclp = acl_alloc(M_WAITOK | M_ZERO); + + error = VOP_GETACL(dvp, ACL_TYPE_NFS4, parent_aclp, cred, td); + if (error) + goto out; + + acl_nfs4_compute_inherited_acl(parent_aclp, child_aclp, + child_mode, VTOI(tvp)->i_uid, tvp->v_type == VDIR); + + error = VOP_SETACL(tvp, ACL_TYPE_NFS4, child_aclp, cred, td); + if (error) + goto out; + +out: + acl_free(parent_aclp); + acl_free(child_aclp); + + return (error); +} +#endif + /* * Mkdir system call */ @@ -1628,6 +1726,13 @@ ufs_mkdir(ap) acl_free(dacl); dacl = acl = NULL; } + + if (dvp->v_mount->mnt_flag & MNT_NFS4ACLS) { + error = ufs_do_nfs4_acl_inheritance(dvp, tvp, dmode, + cnp->cn_cred, cnp->cn_thread); + if (error) + goto bad; + } #endif /* !UFS_ACL */ /* @@ -2144,6 +2249,18 @@ ufs_pathconf(ap) *ap->a_retval = 0; #endif break; + + case _PC_EXTENDED_SECURITY_NP: +#ifdef UFS_ACL + if (ap->a_vp->v_mount->mnt_flag & MNT_NFS4ACLS) + *ap->a_retval = 1; + else + *ap->a_retval = 0; +#else + *ap->a_retval = 0; +#endif + break; + case _PC_ACL_PATH_MAX: #ifdef UFS_ACL if (ap->a_vp->v_mount->mnt_flag & MNT_ACLS) @@ -2439,6 +2556,13 @@ ufs_makeinode(mode, dvp, vpp, cnp) } acl_free(acl); } + + if (dvp->v_mount->mnt_flag & MNT_NFS4ACLS) { + error = ufs_do_nfs4_acl_inheritance(dvp, tvp, mode, + cnp->cn_cred, cnp->cn_thread); + if (error) + goto bad; + } #endif /* !UFS_ACL */ ufs_makedirentry(ip, cnp, &newdir); error = ufs_direnter(dvp, tvp, &newdir, cnp, NULL); @@ -2469,7 +2593,7 @@ struct vop_vector ufs_vnodeops = { .vop_read = VOP_PANIC, .vop_reallocblks = VOP_PANIC, .vop_write = VOP_PANIC, - .vop_access = ufs_access, + .vop_accessx = ufs_accessx, .vop_bmap = ufs_bmap, .vop_cachedlookup = ufs_lookup, .vop_close = ufs_close, @@ -2513,7 +2637,7 @@ struct vop_vector ufs_vnodeops = { struct vop_vector ufs_fifoops = { .vop_default = &fifo_specops, .vop_fsync = VOP_PANIC, - .vop_access = ufs_access, + .vop_accessx = ufs_accessx, .vop_close = ufsfifo_close, .vop_getattr = ufs_getattr, .vop_inactive = ufs_inactive, diff -urNp current/tools/regression/acltools/00.t nfs4acl/tools/regression/acltools/00.t --- current/tools/regression/acltools/00.t 2009-04-29 09:29:31.000000000 +0200 +++ nfs4acl/tools/regression/acltools/00.t 2009-04-29 15:07:14.000000000 +0200 @@ -45,7 +45,7 @@ chmod 600 xxx rm xxx echo "ok 2" -perl $TESTDIR/run $TESTDIR/tools-posix.test > /dev/null +perl $TESTDIR/run $TESTDIR/tools-posix.test if [ $? -eq 0 ]; then echo "ok 3" diff -urNp current/tools/regression/acltools/01.t nfs4acl/tools/regression/acltools/01.t --- current/tools/regression/acltools/01.t 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/tools/regression/acltools/01.t 2009-04-29 15:07:25.000000000 +0200 @@ -0,0 +1,60 @@ +#!/bin/sh +# +# This is a wrapper script to run tools-nfs4.test on ZFS filesystem. +# +# WARNING: It uses hardcoded ZFS pool name "acltools" +# +# If any of the tests fails, here is how to debug it: go to +# the directory with problematic filesystem mounted on it, +# and do /path/to/test run /path/to/test tools-nfs4.test, e.g. +# +# /usr/src/tools/regression/acltools/run /usr/src/tools/regression/acltools/tools-nfs4.test +# +# Output should be obvious. + +echo "1..4" + +if [ `whoami` != "root" ]; then + echo "not ok 1 - you need to be root to run this test." + exit 1 +fi + +TESTDIR=`dirname $0` + +# Set up the test filesystem. +MD=`mdconfig -at swap -s 64m` +MNT=`mktemp -dt acltools` +zpool create -R $MNT acltools /dev/$MD +if [ $? -ne 0 ]; then + echo "not ok 1 - 'zpool create' failed." + exit 1 +fi + +echo "ok 1" + +cd $MNT + +# First, check whether we can crash the kernel by creating too many +# entries. For some reason this won't work in the test file. +touch xxx +setfacl -x5 xxx +while :; do setfacl -a0 u:42:rwx:allow xxx 2> /dev/null; if [ $? -ne 0 ]; then break; fi; done +chmod 600 xxx +rm xxx +echo "ok 2" + +perl $TESTDIR/run $TESTDIR/tools-nfs4.test + +if [ $? -eq 0 ]; then + echo "ok 3" +else + echo "not ok 3" +fi + +cd / +zpool destroy -f acltools +rmdir $MNT +mdconfig -du $MD + +echo "ok 4" + diff -urNp current/tools/regression/acltools/02.t nfs4acl/tools/regression/acltools/02.t --- current/tools/regression/acltools/02.t 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/tools/regression/acltools/02.t 2009-04-29 15:07:37.000000000 +0200 @@ -0,0 +1,59 @@ +#!/bin/sh +# +# This is a wrapper script to run tools-nfs4.test. +# +# If any of the tests fails, here is how to debug it: go to +# the directory with problematic filesystem mounted on it, +# and do /path/to/test run /path/to/test tools-nfs4.test, e.g. +# +# /usr/src/tools/regression/acltools/run /usr/src/tools/regression/acltools/tools-nfs4.test +# +# Output should be obvious. + +echo "1..4" + +if [ `whoami` != "root" ]; then + echo "not ok 1 - you need to be root to run this test." + exit 1 +fi + +TESTDIR=`dirname $0` + +# Set up the test filesystem. +MD=`mdconfig -at swap -s 10m` +MNT=`mktemp -dt acltools` +newfs /dev/$MD > /dev/null +mount -o nfs4acls /dev/$MD $MNT +if [ $? -ne 0 ]; then + echo "not ok 1 - mount failed." + exit 1 +fi + +echo "ok 1" + +cd $MNT + +# First, check whether we can crash the kernel by creating too many +# entries. For some reason this won't work in the test file. +touch xxx +setfacl -x5 xxx +while :; do setfacl -a0 u:42:rwx:allow xxx 2> /dev/null; if [ $? -ne 0 ]; then break; fi; done +chmod 600 xxx +rm xxx +echo "ok 2" + +perl $TESTDIR/run $TESTDIR/tools-nfs4.test + +if [ $? -eq 0 ]; then + echo "ok 3" +else + echo "not ok 3" +fi + +cd / +umount -f $MNT +rmdir $MNT +mdconfig -du $MD + +echo "ok 4" + diff -urNp current/tools/regression/acltools/03.t nfs4acl/tools/regression/acltools/03.t --- current/tools/regression/acltools/03.t 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/tools/regression/acltools/03.t 2009-04-29 15:07:48.000000000 +0200 @@ -0,0 +1,82 @@ +#!/bin/sh +# +# This is a wrapper script to run tools-crossfs.test, intended to verify +# whether cp(1) and mv(1) do the correct thing wrt ACLs. +# +# Output should be obvious. + +echo "1..5" + +if [ `whoami` != "root" ]; then + echo "not ok 1 - you need to be root to run this test." + exit 1 +fi + +TESTDIR=`dirname $0` +MNTROOT=`mktemp -dt acltools` + +# Set up the test filesystems. +MD1=`mdconfig -at swap -s 10m` +MNT1=$MNTROOT/nfs4 +mkdir $MNT1 +newfs /dev/$MD1 > /dev/null +mount -o nfs4acls /dev/$MD1 $MNT1 +if [ $? -ne 0 ]; then + echo "not ok 1 - mount failed." + exit 1 +fi + +echo "ok 1" + +MD2=`mdconfig -at swap -s 10m` +MNT2=$MNTROOT/posix +mkdir $MNT2 +newfs /dev/$MD2 > /dev/null +mount -o acls /dev/$MD2 $MNT2 +if [ $? -ne 0 ]; then + echo "not ok 2 - mount failed." + exit 1 +fi + +echo "ok 2" + +MD3=`mdconfig -at swap -s 10m` +MNT3=$MNTROOT/none +mkdir $MNT3 +newfs /dev/$MD3 > /dev/null +mount /dev/$MD3 $MNT3 +if [ $? -ne 0 ]; then + echo "not ok 3 - mount failed." + exit 1 +fi + +echo "ok 3" + +cd $MNTROOT + +perl $TESTDIR/run $TESTDIR/tools-crossfs.test + +if [ $? -eq 0 ]; then + echo "ok 4" +else + echo "not ok 4" +fi + +cd / + +umount -f $MNT3 +rmdir $MNT3 +mdconfig -du $MD3 + +umount -f $MNT2 +rmdir $MNT2 +mdconfig -du $MD2 + +umount -f $MNT1 +rmdir $MNT1 +mdconfig -du $MD1 + +rmdir $MNTROOT + +echo "ok 5" + diff -urNp current/tools/regression/acltools/fuzzer.sh nfs4acl/tools/regression/acltools/fuzzer.sh --- current/tools/regression/acltools/fuzzer.sh 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/tools/regression/acltools/fuzzer.sh 2009-05-26 17:15:44.000000000 +0200 @@ -0,0 +1,198 @@ +#!/bin/sh +# This is an NFSv4 ACL fuzzer. It expects to be run by non-root +# in a scratch directory on a filesystem with NFSv4 ACLs support. +# It generates the output that is expected to be fed to +# /usr/src/tools/regression/acltools/run script. + +NUMBER_OF_COMMANDS=300 + +run_command() +{ + echo "\$ $1" + eval $1 2>&1 | sed 's/^/> /' +} + +rnd_from_0_to() +{ + max=`expr $1 + 1` + rnd=`jot -r 1` + rnd=`expr $rnd % $max` + + echo $rnd +} + +rnd_path() +{ + rnd=`rnd_from_0_to 3` + case $rnd in + 0) echo "$TMP/aaa" ;; + 1) echo "$TMP/bbb" ;; + 2) echo "$TMP/aaa/ccc" ;; + 3) echo "$TMP/bbb/ddd" ;; + esac +} + +f_prepend_random_acl_on() +{ + rnd=`rnd_from_0_to 4` + case $rnd in + 0) u="owner@" ;; + 1) u="group@" ;; + 2) u="everyone@" ;; + 3) u="u:1138" ;; + 4) u="g:1138" ;; + esac + + p="" + while :; do + rnd=`rnd_from_0_to 30` + if [ -n "$p" -a $rnd -ge 14 ]; then + break; + fi + + case $rnd in + 0) p="${p}r" ;; + 1) p="${p}w" ;; + 2) p="${p}x" ;; + 3) p="${p}p" ;; + 4) p="${p}d" ;; + 5) p="${p}D" ;; + 6) p="${p}a" ;; + 7) p="${p}A" ;; + 8) p="${p}R" ;; + 9) p="${p}W" ;; + 10) p="${p}R" ;; + 11) p="${p}c" ;; + 12) p="${p}C" ;; + 13) p="${p}o" ;; + 14) p="${p}s" ;; + esac + done + + f="" + while :; do + rnd=`rnd_from_0_to 10` + if [ $rnd -ge 6 ]; then + break; + fi + + case $rnd in + 0) f="${f}f" ;; + 1) f="${f}d" ;; + 2) f="${f}n" ;; + 3) f="${f}i" ;; + esac + done + + rnd=`rnd_from_0_to 1` + case $rnd in + 0) x="allow" ;; + 1) x="deny" ;; + esac + + acl="$u:$p:$f:$x" + + file=`rnd_path` + run_command "setfacl -a0 $acl $file" +} + +f_getfacl() +{ + file=`rnd_path` + run_command "getfacl -qn $file" +} + +f_ls_mode() +{ + file=`rnd_path` + run_command "ls -al $file | sed -n '2p' | cut -d' ' -f1" +} + +f_chmod() +{ + b1=`rnd_from_0_to 7` + b2=`rnd_from_0_to 7` + b3=`rnd_from_0_to 7` + b4=`rnd_from_0_to 7` + file=`rnd_path` + + run_command "chmod $b1$b2$b3$b4 $file $2" +} + +f_touch() +{ + file=`rnd_path` + run_command "touch $file" +} + +f_rm() +{ + file=`rnd_path` + run_command "rm -f $file" +} + +f_mkdir() +{ + file=`rnd_path` + run_command "mkdir $file" +} + +f_rmdir() +{ + file=`rnd_path` + run_command "rmdir $file" +} + +f_mv() +{ + from=`rnd_path` + to=`rnd_path` + run_command "mv -f $from $to" +} + +# XXX: chown(8), setting times with touch(1). + +switch_to_random_user() +{ +# echo "switch" +} + +execute_random_command() +{ + rnd=`rnd_from_0_to 20` + + case $rnd in + 0|10|11|12|13|15) cmd=f_prepend_random_acl_on ;; + 1) cmd=f_getfacl ;; + 2) cmd=f_ls_mode ;; + 3) cmd=f_chmod ;; + 4|18|19) cmd=f_touch ;; + 5) cmd=f_rm ;; + 6|16|17) cmd=f_mkdir ;; + 7) cmd=f_rmdir ;; + 8) cmd=f_mv ;; + esac + + $cmd "XXX" +} + +echo "# Fuzzing; will stop after $NUMBER_OF_COMMANDS commands." +TMP="aclfuzzer_`dd if=/dev/random bs=1k count=1 2>/dev/null | openssl md5`" + +run_command "whoami" +umask 022 +run_command "umask 022" +run_command "mkdir $TMP" + +i=0; +while [ "$i" -lt "$NUMBER_OF_COMMANDS" ]; do + switch_to_random_user + execute_random_command + i=`expr $i + 1` +done + +run_command "find $TMP -exec setfacl -a0 everyone@:rxd:allow {} \;" +run_command "rm -rfv $TMP" + +echo "# Fuzzed, thank you." + diff -urNp current/tools/regression/acltools/tools-crossfs.test nfs4acl/tools/regression/acltools/tools-crossfs.test --- current/tools/regression/acltools/tools-crossfs.test 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/tools/regression/acltools/tools-crossfs.test 2009-05-26 17:15:44.000000000 +0200 @@ -0,0 +1,148 @@ +# This is a tools-level test whether cp(1) and mv(1) do the correct +# thing wrt ACLs. Run it as root using ACL-enabled kernel: +# +# /usr/src/tools/regression/acltools/run /usr/src/tools/regression/acltools/tools-nfs4.test +# +# You need to have three subdirectories, named nfs4, posix and none, +# with filesystems with NFSv4 ACLs, POSIX.1e ACLs and no ACLs enabled, +# respectively, mounted on them, in your current directory. +# +# WARNING: Creates files in unsafe way. + +$ whoami +> root +$ umask 022 + +$ touch nfs4/xxx +$ getfacl -nq nfs4/xxx +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> group@:-wxp----------:------:deny +> group@:r-------------:------:allow +> everyone@:-wxp---A-W-Co-:------:deny +> everyone@:r-----a-R-c--s:------:allow + +$ touch posix/xxx +$ getfacl -nq posix/xxx +> user::rw- +> group::r-- +> other::r-- + +$ rm posix/xxx + +# mv without any ACLs. +$ chmod 456 nfs4/xxx +$ mv nfs4/xxx posix/ +$ ls -l posix/xxx | cut -d' ' -f1 +> -r--r-xrw- + +# mv with POSIX.1e ACLs. +$ setfacl -m u:42:x,g:43:w posix/xxx +$ rm -f posix/yyy +$ mv posix/xxx posix/yyy +$ getfacl -nq posix/yyy +> user::r-- +> user:42:--x +> group::r-x +> group:43:-w- +> mask::rwx +> other::rw- + +# mv from POSIX.1e to NFSv4. +$ rm -f nfs4/xxx +$ mv posix/yyy nfs4/xxx +> mv: ACL brand of source is POSIX.1e, but destination is NFSv4; ACL not copied +$ getfacl -nq nfs4/xxx +> owner@:-wxp----------:------:deny +> owner@:r------A-W-Co-:------:allow +> group@:--------------:------:deny +> group@:rwxp----------:------:allow +> everyone@:--x----A-W-Co-:------:deny +> everyone@:rw-p--a-R-c--s:------:allow + +# mv with NFSv4 ACLs. +$ setfacl -a0 u:42:x:allow,g:43:w:allow nfs4/xxx +$ rm -f nfs4/yyy +$ mv nfs4/xxx nfs4/yyy +$ getfacl -nq nfs4/yyy +> user:42:--x-----------:------:allow +> group:43:-w------------:------:allow +> owner@:-wxp----------:------:deny +> owner@:r------A-W-Co-:------:allow +> group@:--------------:------:deny +> group@:rwxp----------:------:allow +> everyone@:--x----A-W-Co-:------:deny +> everyone@:rw-p--a-R-c--s:------:allow + +# mv from NFSv4 to POSIX.1e. +$ rm -f posix/xxx +$ mv nfs4/yyy posix/xxx +> mv: ACL brand of source is NFSv4, but destination is POSIX.1e; ACL not copied +$ ls -l posix/xxx | cut -d' ' -f1 +> -r--rwxrw- + +# mv from POSIX.1e to none. +$ setfacl -m u:42:x,g:43:w posix/xxx +$ mv posix/xxx none/xxx +$ ls -l none/xxx | cut -d' ' -f1 +> -r--rwxrw- + +# cp with POSIX.1e ACLs. +$ rm -f posix/xxx +$ touch posix/xxx +$ setfacl -m u:42:x,g:43:w posix/xxx +$ getfacl -nq posix/xxx +> user::rw- +> user:42:--x +> group::r-- +> group:43:-w- +> mask::rwx +> other::r-- + +$ rm -f posix/yyy +$ cp posix/xxx posix/yyy +$ getfacl -nq posix/yyy +> user::rw- +> group::r-x +> other::r-- + +$ rm -f posix/yyy +$ cp -p posix/xxx posix/yyy +$ getfacl -nq posix/yyy +> user::rw- +> user:42:--x +> group::r-- +> group:43:-w- +> mask::rwx +> other::r-- + +# mv from POSIX.1e to NFSv4. +$ rm -f nfs4/xxx +$ cp -p posix/xxx nfs4/xxx +> cp: ACL brand of source is POSIX.1e, but destination is NFSv4; ACL not copied +$ ls -l nfs4/xxx | cut -d' ' -f1 +> -rw-rwxr-- + +# cp with NFSv4 ACLs. +$ setfacl -a0 u:42:x:allow,g:43:w:allow nfs4/xxx +$ rm -f nfs4/yyy +$ cp -p nfs4/xxx nfs4/yyy +$ getfacl -nq nfs4/yyy +> user:42:--x-----------:------:allow +> group:43:-w------------:------:allow +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> group@:--------------:------:deny +> group@:rwxp----------:------:allow +> everyone@:-wxp---A-W-Co-:------:deny +> everyone@:r-----a-R-c--s:------:allow + +# mv from NFSv4 to POSIX.1e. +$ rm -f posix/xxx +$ cp -p nfs4/xxx posix/xxx +> cp: ACL brand of source is NFSv4, but destination is POSIX.1e; ACL not copied +$ ls -l posix/xxx | cut -d' ' -f1 +> -rw-rwxr-- + +$ cp -p nfs4/yyy none/xxx + diff -urNp current/tools/regression/acltools/tools-nfs4.test nfs4acl/tools/regression/acltools/tools-nfs4.test --- current/tools/regression/acltools/tools-nfs4.test 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/tools/regression/acltools/tools-nfs4.test 2009-05-26 17:15:44.000000000 +0200 @@ -0,0 +1,802 @@ +# This is a tools-level test for NFSv4 ACL functionality. Run it as root +# using ACL-enabled kernel: +# +# /usr/src/tools/regression/acltools/run /usr/src/tools/regression/acltools/tools-nfs4.test +# +# WARNING: Creates files in unsafe way. + +$ whoami +> root +$ umask 022 + +# Smoke test for getfacl(1). +$ touch xxx +$ getfacl xxx +> # file: xxx +> # owner: root +> # group: wheel +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> group@:-wxp----------:------:deny +> group@:r-------------:------:allow +> everyone@:-wxp---A-W-Co-:------:deny +> everyone@:r-----a-R-c--s:------:allow + +$ getfacl -q xxx +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> group@:-wxp----------:------:deny +> group@:r-------------:------:allow +> everyone@:-wxp---A-W-Co-:------:deny +> everyone@:r-----a-R-c--s:------:allow + +# Check verbose mode formatting. +$ getfacl -v xxx +> # file: xxx +> # owner: root +> # group: wheel +> owner@:execute::deny +> owner@:read_data/write_data/append_data/write_attributes/write_xattr/write_acl/write_owner::allow +> group@:write_data/execute/append_data::deny +> group@:read_data::allow +> everyone@:write_data/execute/append_data/write_attributes/write_xattr/write_acl/write_owner::deny +> everyone@:read_data/read_attributes/read_xattr/read_acl/synchronize::allow + +# Test setfacl -a. +$ setfacl -a2 u:0:write_acl:allow,g:1:read_acl:deny xxx +$ getfacl -n xxx +> # file: xxx +> # owner: root +> # group: wheel +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> user:0:-----------C--:------:allow +> group:1:----------c---:------:deny +> group@:-wxp----------:------:deny +> group@:r-------------:------:allow +> everyone@:-wxp---A-W-Co-:------:deny +> everyone@:r-----a-R-c--s:------:allow + +# Test user and group name resolving. +$ rm xxx +$ touch xxx +$ setfacl -a2 u:root:write_acl:allow,g:daemon:read_acl:deny xxx +$ getfacl xxx +> # file: xxx +> # owner: root +> # group: wheel +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> user:root:-----------C--:------:allow +> group:daemon:----------c---:------:deny +> group@:-wxp----------:------:deny +> group@:r-------------:------:allow +> everyone@:-wxp---A-W-Co-:------:deny +> everyone@:r-----a-R-c--s:------:allow + +# Check whether ls correctly marks files with "+". +$ ls -l xxx | cut -d' ' -f1 +> -rw-r--r--+ + +# Test removing entries by number. +$ setfacl -x 4 xxx +$ setfacl -x 4 xxx +$ getfacl -n xxx +> # file: xxx +> # owner: root +> # group: wheel +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> user:0:-----------C--:------:allow +> group:1:----------c---:------:deny +> everyone@:-wxp---A-W-Co-:------:deny +> everyone@:r-----a-R-c--s:------:allow + +# Test setfacl -m. +$ setfacl -a0 everyone@:rwx:deny xxx +$ setfacl -a0 everyone@:rwx:deny xxx +$ setfacl -a0 everyone@:rwx:deny xxx +$ setfacl -m everyone@::deny xxx +$ getfacl -n xxx +> # file: xxx +> # owner: root +> # group: wheel +> everyone@:--------------:------:deny +> everyone@:--------------:------:deny +> everyone@:--------------:------:deny +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> user:0:-----------C--:------:allow +> group:1:----------c---:------:deny +> everyone@:--------------:------:deny +> everyone@:r-----a-R-c--s:------:allow + +# Test getfacl -i. +$ getfacl -i xxx +> # file: xxx +> # owner: root +> # group: wheel +> everyone@:--------------:------:deny +> everyone@:--------------:------:deny +> everyone@:--------------:------:deny +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> user:root:-----------C--:------:allow:0 +> group:daemon:----------c---:------:deny:1 +> everyone@:--------------:------:deny +> everyone@:r-----a-R-c--s:------:allow + +# Make sure cp without any flags does not copy copy the ACL. +$ cp xxx yyy +$ ls -l yyy | cut -d' ' -f1 +> -rw-r--r-- + +# Make sure it does with the "-p" flag. +$ rm yyy +$ cp -p xxx yyy +$ getfacl -n yyy +> # file: yyy +> # owner: root +> # group: wheel +> everyone@:--------------:------:deny +> everyone@:--------------:------:deny +> everyone@:--------------:------:deny +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> user:0:-----------C--:------:allow +> group:1:----------c---:------:deny +> everyone@:--------------:------:deny +> everyone@:r-----a-R-c--s:------:allow + +$ rm yyy + +# Test removing entries by... by example? +$ setfacl -x everyone@::deny xxx +$ getfacl -n xxx +> # file: xxx +> # owner: root +> # group: wheel +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> user:0:-----------C--:------:allow +> group:1:----------c---:------:deny +> everyone@:r-----a-R-c--s:------:allow + +# Test setfacl -b. +$ setfacl -b xxx +$ getfacl -n xxx +> # file: xxx +> # owner: root +> # group: wheel +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> group@:-wxp----------:------:deny +> group@:r-------------:------:allow +> everyone@:-wxp---A-W-Co-:------:deny +> everyone@:r-----a-R-c--s:------:allow + +$ ls -l xxx | cut -d' ' -f1 +> -rw-r--r-- + +# Check setfacl(1) and getfacl(1) with multiple files. +$ touch xxx yyy zzz + +$ ls -l xxx yyy zzz | cut -d' ' -f1 +> -rw-r--r-- +> -rw-r--r-- +> -rw-r--r-- + +$ setfacl -m u:42:x:allow,g:43:w:allow nnn xxx yyy zzz +> setfacl: nnn: acl_get_file() failed: No such file or directory + +$ ls -l nnn xxx yyy zzz | cut -d' ' -f1 +> ls: nnn: No such file or directory +> -rw-r--r--+ +> -rw-r--r--+ +> -rw-r--r--+ + +$ getfacl -nq nnn xxx yyy zzz +> getfacl: nnn: stat() failed: No such file or directory +> user:42:--x-----------:------:allow +> group:43:-w------------:------:allow +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> group@:-wxp----------:------:deny +> group@:r-------------:------:allow +> everyone@:-wxp---A-W-Co-:------:deny +> everyone@:r-----a-R-c--s:------:allow +> +> user:42:--x-----------:------:allow +> group:43:-w------------:------:allow +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> group@:-wxp----------:------:deny +> group@:r-------------:------:allow +> everyone@:-wxp---A-W-Co-:------:deny +> everyone@:r-----a-R-c--s:------:allow +> +> user:42:--x-----------:------:allow +> group:43:-w------------:------:allow +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> group@:-wxp----------:------:deny +> group@:r-------------:------:allow +> everyone@:-wxp---A-W-Co-:------:deny +> everyone@:r-----a-R-c--s:------:allow + +$ setfacl -b nnn xxx yyy zzz +> setfacl: nnn: acl_get_file() failed: No such file or directory + +$ ls -l nnn xxx yyy zzz | cut -d' ' -f1 +> ls: nnn: No such file or directory +> -rw-r--r-- +> -rw-r--r-- +> -rw-r--r-- + +$ rm xxx yyy zzz + +# Test applying mode to an ACL. +$ touch xxx +$ setfacl -a0 user:42:r:allow,user:43:w:deny,user:43:w:allow,user:44:x:allow -x everyone@::allow xxx +$ chmod 600 xxx +$ getfacl -n xxx +> # file: xxx +> # owner: root +> # group: wheel +> user:42:r-------------:------:deny +> user:42:r-------------:------:allow +> user:43:-w------------:------:deny +> user:43:-w------------:------:allow +> user:44:--x-----------:------:deny +> user:44:--x-----------:------:allow +> owner@:--------------:------:deny +> owner@:-------A-W-Co-:------:allow +> group@:--------------:------:deny +> group@:--------------:------:allow +> everyone@:-------A-W-Co-:------:deny +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> group@:rwxp----------:------:deny +> group@:--------------:------:allow +> everyone@:rwxp---A-W-Co-:------:deny +> everyone@:------a-R-c--s:------:allow +$ ls -l xxx | cut -d' ' -f1 +> -rw-------+ + +$ rm xxx +$ touch xxx +$ chown 42 xxx +$ setfacl -a0 user:42:r:allow,user:43:w:deny,user:43:w:allow,user:44:x:allow xxx +$ chmod 600 xxx +$ getfacl -n xxx +> # file: xxx +> # owner: 42 +> # group: wheel +> user:42:--------------:------:deny +> user:42:r-------------:------:allow +> user:43:-w------------:------:deny +> user:43:-w------------:------:allow +> user:44:--x-----------:------:deny +> user:44:--x-----------:------:allow +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> group@:rwxp----------:------:deny +> group@:--------------:------:allow +> everyone@:rwxp---A-W-Co-:------:deny +> everyone@:------a-R-c--s:------:allow +$ ls -l xxx | cut -d' ' -f1 +> -rw-------+ + +$ rm xxx +$ touch xxx +$ chown 43 xxx +$ setfacl -a0 user:42:r:allow,user:43:w:deny,user:43:w:allow,user:44:x:allow xxx +$ chmod 124 xxx +$ getfacl -n xxx +> # file: xxx +> # owner: 43 +> # group: wheel +> user:42:r-------------:------:deny +> user:42:r-------------:------:allow +> user:43:-w------------:------:deny +> user:43:-w------------:------:allow +> user:44:--x-----------:------:deny +> user:44:--x-----------:------:allow +> owner@:rw-p----------:------:deny +> owner@:--x----A-W-Co-:------:allow +> group@:r-x-----------:------:deny +> group@:-w-p----------:------:allow +> everyone@:-wxp---A-W-Co-:------:deny +> everyone@:r-----a-R-c--s:------:allow +$ ls -l xxx | cut -d' ' -f1 +> ---x-w-r--+ + +$ rm xxx +$ touch xxx +$ chown 43 xxx +$ setfacl -a0 user:42:r:allow,user:43:w:deny,user:43:w:allow,user:44:x:allow xxx +$ chmod 412 xxx +$ getfacl -n xxx +> # file: xxx +> # owner: 43 +> # group: wheel +> user:42:r-------------:------:deny +> user:42:r-------------:------:allow +> user:43:-w------------:------:deny +> user:43:-w------------:------:allow +> user:44:--------------:------:deny +> user:44:--x-----------:------:allow +> owner@:-wxp----------:------:deny +> owner@:r------A-W-Co-:------:allow +> group@:rw-p----------:------:deny +> group@:--x-----------:------:allow +> everyone@:r-x----A-W-Co-:------:deny +> everyone@:-w-p--a-R-c--s:------:allow +$ ls -l xxx | cut -d' ' -f1 +> -r----x-w-+ + +$ mkdir ddd +$ setfacl -a0 group:44:rwapd:allow ddd +$ setfacl -a0 group:43:write_data/delete_child:d:deny,group@:ad:allow ddd +$ setfacl -a0 user:42:rx:fi:allow,group:42:write_data/delete_child:d:allow ddd +$ setfacl -m everyone@:-w-p--a-R-c--s:fi:allow ddd +$ getfacl -n ddd +> # file: ddd +> # owner: root +> # group: wheel +> user:42:r-x-----------:f-i---:allow +> group:42:-w--D---------:-d----:allow +> group:43:-w--D---------:-d----:deny +> group@:-----da-------:------:allow +> group:44:rw-p-da-------:------:allow +> owner@:--------------:------:deny +> owner@:rwxp---A-W-Co-:------:allow +> group@:-w-p----------:------:deny +> group@:r-x-----------:------:allow +> everyone@:-w-p---A-W-Co-:------:deny +> everyone@:-w-p--a-R-c--s:f-i---:allow +$ chmod 777 ddd +$ getfacl -n ddd +> # file: ddd +> # owner: root +> # group: wheel +> user:42:r-x-----------:f-i---:allow +> group:42:-w--D---------:-di---:allow +> group:42:--------------:------:deny +> group:42:-w--D---------:------:allow +> group:43:-w--D---------:-di---:deny +> group:43:-w--D---------:------:deny +> group@:-----da-------:------:allow +> group:44:--------------:------:deny +> group:44:rw-p-da-------:------:allow +> owner@:--------------:------:deny +> owner@:-------A-W-Co-:------:allow +> group@:--------------:------:deny +> group@:--------------:------:allow +> everyone@:-------A-W-Co-:------:deny +> everyone@:-w-p--a-R-c--s:f-i---:allow +> owner@:--------------:------:deny +> owner@:rwxp---A-W-Co-:------:allow +> group@:--------------:------:deny +> group@:rwxp----------:------:allow +> everyone@:-------A-W-Co-:------:deny +> everyone@:rwxp--a-R-c--s:------:allow + +$ rmdir ddd +$ mkdir ddd +$ setfacl -a0 group:44:rwapd:allow ddd +$ setfacl -a0 group:43:write_data/delete_child:d:deny,group@:ad:allow ddd +$ setfacl -a0 user:42:rx:fi:allow,group:42:write_data/delete_child:d:allow ddd +$ setfacl -m everyone@:-w-p--a-R-c--s:fi:allow ddd +$ chmod 124 ddd +$ getfacl -n ddd +> # file: ddd +> # owner: root +> # group: wheel +> user:42:r-x-----------:f-i---:allow +> group:42:-w--D---------:-di---:allow +> group:42:--------------:------:deny +> group:42:----D---------:------:allow +> group:43:-w--D---------:-di---:deny +> group:43:-w--D---------:------:deny +> group@:-----da-------:------:allow +> group:44:r-------------:------:deny +> group:44:r----da-------:------:allow +> owner@:--------------:------:deny +> owner@:-------A-W-Co-:------:allow +> group@:--------------:------:deny +> group@:--------------:------:allow +> everyone@:-------A-W-Co-:------:deny +> everyone@:-w-p--a-R-c--s:f-i---:allow +> owner@:rw-p----------:------:deny +> owner@:--x----A-W-Co-:------:allow +> group@:r-x-----------:------:deny +> group@:-w-p----------:------:allow +> everyone@:-wxp---A-W-Co-:------:deny +> everyone@:r-----a-R-c--s:------:allow + +$ rmdir ddd +$ mkdir ddd +$ setfacl -a0 group:44:rwapd:allow ddd +$ setfacl -a0 group:43:write_data/delete_child:d:deny,group@:ad:allow ddd +$ setfacl -a0 user:42:rx:allow,user:42:rx:fi:allow,group:42:write_data/delete_child:d:allow ddd +$ setfacl -m everyone@:-w-p--a-R-c--s:fi:allow ddd +$ chmod 412 ddd +$ getfacl -n ddd +> # file: ddd +> # owner: root +> # group: wheel +> user:42:r-------------:------:deny +> user:42:r-x-----------:------:allow +> user:42:r-x-----------:f-i---:allow +> group:42:-w--D---------:-di---:allow +> group:42:-w------------:------:deny +> group:42:-w--D---------:------:allow +> group:43:-w--D---------:-di---:deny +> group:43:-w--D---------:------:deny +> group@:-----da-------:------:allow +> group:44:rw-p----------:------:deny +> group:44:rw-p-da-------:------:allow +> owner@:--------------:------:deny +> owner@:-------A-W-Co-:------:allow +> group@:--------------:------:deny +> group@:--------------:------:allow +> everyone@:-------A-W-Co-:------:deny +> everyone@:-w-p--a-R-c--s:f-i---:allow +> owner@:-wxp----------:------:deny +> owner@:r------A-W-Co-:------:allow +> group@:rw-p----------:------:deny +> group@:--x-----------:------:allow +> everyone@:r-x----A-W-Co-:------:deny +> everyone@:-w-p--a-R-c--s:------:allow + +$ rmdir ddd +$ mkdir ddd +$ setfacl -a0 group:44:rwapd:allow ddd +$ setfacl -a0 group:43:write_data/delete_child:d:deny,group@:ad:allow ddd +$ setfacl -a0 user:42:rx:allow,user:42:rx:fi:allow,group:42:write_data/delete_child:d:allow ddd +$ setfacl -m everyone@:-w-p--a-R-c--s:fi:allow ddd +$ chown 42 ddd +$ chmod 412 ddd +$ getfacl -n ddd +> # file: ddd +> # owner: 42 +> # group: wheel +> user:42:--x-----------:------:deny +> user:42:r-x-----------:------:allow +> user:42:r-x-----------:f-i---:allow +> group:42:-w--D---------:-di---:allow +> group:42:-w------------:------:deny +> group:42:-w--D---------:------:allow +> group:43:-w--D---------:-di---:deny +> group:43:-w--D---------:------:deny +> group@:-----da-------:------:allow +> group:44:rw-p----------:------:deny +> group:44:rw-p-da-------:------:allow +> owner@:--------------:------:deny +> owner@:-------A-W-Co-:------:allow +> group@:--------------:------:deny +> group@:--------------:------:allow +> everyone@:-------A-W-Co-:------:deny +> everyone@:-w-p--a-R-c--s:f-i---:allow +> owner@:-wxp----------:------:deny +> owner@:r------A-W-Co-:------:allow +> group@:rw-p----------:------:deny +> group@:--x-----------:------:allow +> everyone@:r-x----A-W-Co-:------:deny +> everyone@:-w-p--a-R-c--s:------:allow + +# Test applying ACL to mode. +$ rmdir ddd +$ mkdir ddd +$ setfacl -a0 u:42:rwx:fi:allow ddd +$ ls -ld ddd | cut -d' ' -f1 +> drwxr-xr-x+ + +$ rmdir ddd +$ mkdir ddd +$ chmod 0 ddd +$ setfacl -a0 owner@:r:allow,group@:w:deny,group@:wx:allow ddd +$ ls -ld ddd | cut -d' ' -f1 +> dr----x---+ + +# XXX: This one is fishy. Shouldn't it be "dr---wx---+"? +$ rmdir ddd +$ mkdir ddd +$ chmod 0 ddd +$ setfacl -a0 owner@:r:allow,group@:w:fi:deny,group@:wx:allow ddd +$ ls -ld ddd | cut -d' ' -f1 +> dr---wx---+ + +$ rmdir ddd +$ mkdir ddd +$ chmod 0 ddd +$ setfacl -a0 owner@:r:allow,group:43:w:deny,group:43:wx:allow ddd +$ ls -ld ddd | cut -d' ' -f1 +> dr--------+ + +$ rmdir ddd +$ mkdir ddd +$ chmod 0 ddd +$ setfacl -a0 owner@:r:allow,user:43:w:deny,user:43:wx:allow ddd +$ ls -ld ddd | cut -d' ' -f1 +> dr--------+ + +# Test inheritance. +$ rmdir ddd +$ mkdir ddd +$ setfacl -a0 group:43:write_data/write_acl:fin:deny,u:43:rwxp:allow ddd +$ setfacl -a0 user:42:rx:fi:allow,group:42:write_data/delete_child:dn:deny ddd +$ setfacl -a0 user:42:write_acl/write_owner:fi:allow ddd +$ setfacl -a0 group:41:read_data/read_attributes:dni:allow ddd +$ setfacl -a0 user:41:write_data/write_attributes:fn:allow ddd +$ getfacl -qn ddd +> user:41:-w-----A------:f--n--:allow +> group:41:r-----a-------:-din--:allow +> user:42:-----------Co-:f-i---:allow +> user:42:r-x-----------:f-i---:allow +> group:42:-w--D---------:-d-n--:deny +> group:43:-w---------C--:f-in--:deny +> user:43:rwxp----------:------:allow +> owner@:--------------:------:deny +> owner@:rwxp---A-W-Co-:------:allow +> group@:-w-p----------:------:deny +> group@:r-x-----------:------:allow +> everyone@:-w-p---A-W-Co-:------:deny +> everyone@:r-x---a-R-c--s:------:allow + +$ cd ddd +$ touch xxx +$ getfacl -qn xxx +> user:41:-w------------:------:deny +> user:41:-w-----A------:------:allow +> user:42:--------------:------:deny +> user:42:--------------:------:allow +> user:42:--x-----------:------:deny +> user:42:r-x-----------:------:allow +> group:43:-w---------C--:------:deny +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> group@:-wxp----------:------:deny +> group@:r-------------:------:allow +> everyone@:-wxp---A-W-Co-:------:deny +> everyone@:r-----a-R-c--s:------:allow + +$ rm xxx +$ umask 077 +$ touch xxx +$ getfacl -qn xxx +> user:41:-w------------:------:deny +> user:41:-w-----A------:------:allow +> user:42:--------------:------:deny +> user:42:--------------:------:allow +> user:42:r-x-----------:------:deny +> user:42:r-x-----------:------:allow +> group:43:-w---------C--:------:deny +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> group@:rwxp----------:------:deny +> group@:--------------:------:allow +> everyone@:rwxp---A-W-Co-:------:deny +> everyone@:------a-R-c--s:------:allow + +$ rm xxx +$ umask 770 +$ touch xxx +$ getfacl -qn xxx +> user:41:-w------------:------:deny +> user:41:-w-----A------:------:allow +> user:42:--------------:------:deny +> user:42:--------------:------:allow +> user:42:r-x-----------:------:deny +> user:42:r-x-----------:------:allow +> group:43:-w---------C--:------:deny +> owner@:rwxp----------:------:deny +> owner@:-------A-W-Co-:------:allow +> group@:rwxp----------:------:deny +> group@:--------------:------:allow +> everyone@:--x----A-W-Co-:------:deny +> everyone@:rw-p--a-R-c--s:------:allow + +$ rm xxx +$ umask 707 +$ touch xxx +$ getfacl -qn xxx +> user:41:--------------:------:deny +> user:41:-w-----A------:------:allow +> user:42:--------------:------:deny +> user:42:--------------:------:allow +> user:42:--x-----------:------:deny +> user:42:r-x-----------:------:allow +> group:43:-w---------C--:------:deny +> owner@:rwxp----------:------:deny +> owner@:-------A-W-Co-:------:allow +> group@:--x-----------:------:deny +> group@:rw-p----------:------:allow +> everyone@:rwxp---A-W-Co-:------:deny +> everyone@:------a-R-c--s:------:allow + +$ umask 077 +$ mkdir yyy +$ getfacl -qn yyy +> group:41:r-------------:------:deny +> group:41:r-----a-------:------:allow +> user:42:-----------Co-:f-i---:allow +> user:42:r-x-----------:f-i---:allow +> group:42:-w--D---------:------:deny +> owner@:--------------:------:deny +> owner@:rwxp---A-W-Co-:------:allow +> group@:rwxp----------:------:deny +> group@:--------------:------:allow +> everyone@:rwxp---A-W-Co-:------:deny +> everyone@:------a-R-c--s:------:allow + +$ rmdir yyy +$ umask 770 +$ mkdir yyy +$ getfacl -qn yyy +> group:41:r-------------:------:deny +> group:41:r-----a-------:------:allow +> user:42:-----------Co-:f-i---:allow +> user:42:r-x-----------:f-i---:allow +> group:42:-w--D---------:------:deny +> owner@:rwxp----------:------:deny +> owner@:-------A-W-Co-:------:allow +> group@:rwxp----------:------:deny +> group@:--------------:------:allow +> everyone@:-------A-W-Co-:------:deny +> everyone@:rwxp--a-R-c--s:------:allow + +$ rmdir yyy +$ umask 707 +$ mkdir yyy +$ getfacl -qn yyy +> group:41:--------------:------:deny +> group:41:------a-------:------:allow +> user:42:-----------Co-:f-i---:allow +> user:42:r-x-----------:f-i---:allow +> group:42:-w--D---------:------:deny +> owner@:rwxp----------:------:deny +> owner@:-------A-W-Co-:------:allow +> group@:--------------:------:deny +> group@:rwxp----------:------:allow +> everyone@:rwxp---A-W-Co-:------:deny +> everyone@:------a-R-c--s:------:allow + +# There is some complication regarding how write_acl and write_owner flags +# get inherited. Make sure we got it right. +$ setfacl -b . +$ setfacl -a0 u:42:Co:f:allow . +$ setfacl -a0 u:43:Co:d:allow . +$ setfacl -a0 u:44:Co:fd:allow . +$ setfacl -a0 u:45:Co:fi:allow . +$ setfacl -a0 u:46:Co:di:allow . +$ setfacl -a0 u:47:Co:fdi:allow . +$ setfacl -a0 u:48:Co:fn:allow . +$ setfacl -a0 u:49:Co:dn:allow . +$ setfacl -a0 u:50:Co:fdn:allow . +$ setfacl -a0 u:51:Co:fni:allow . +$ setfacl -a0 u:52:Co:dni:allow . +$ setfacl -a0 u:53:Co:fdni:allow . +$ umask 022 +$ rm xxx +$ touch xxx +$ getfacl -nq xxx +> user:53:--------------:------:deny +> user:53:--------------:------:allow +> user:51:--------------:------:deny +> user:51:--------------:------:allow +> user:50:--------------:------:deny +> user:50:--------------:------:allow +> user:48:--------------:------:deny +> user:48:--------------:------:allow +> user:47:--------------:------:deny +> user:47:--------------:------:allow +> user:45:--------------:------:deny +> user:45:--------------:------:allow +> user:44:--------------:------:deny +> user:44:--------------:------:allow +> user:42:--------------:------:deny +> user:42:--------------:------:allow +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> group@:-wxp----------:------:deny +> group@:r-------------:------:allow +> everyone@:-wxp---A-W-Co-:------:deny +> everyone@:r-----a-R-c--s:------:allow + +$ rmdir yyy +$ mkdir yyy +$ getfacl -nq yyy +> user:53:--------------:------:deny +> user:53:--------------:------:allow +> user:52:--------------:------:deny +> user:52:--------------:------:allow +> user:50:--------------:------:deny +> user:50:--------------:------:allow +> user:49:--------------:------:deny +> user:49:--------------:------:allow +> user:47:-----------Co-:fdi---:allow +> user:47:--------------:------:deny +> user:47:--------------:------:allow +> user:46:-----------Co-:-di---:allow +> user:46:--------------:------:deny +> user:46:--------------:------:allow +> user:45:-----------Co-:f-i---:allow +> user:44:-----------Co-:fdi---:allow +> user:44:--------------:------:deny +> user:44:--------------:------:allow +> user:43:-----------Co-:-di---:allow +> user:43:--------------:------:deny +> user:43:--------------:------:allow +> user:42:-----------Co-:f-i---:allow +> owner@:--------------:------:deny +> owner@:rwxp---A-W-Co-:------:allow +> group@:-w-p----------:------:deny +> group@:r-x-----------:------:allow +> everyone@:-w-p---A-W-Co-:------:deny +> everyone@:r-x---a-R-c--s:------:allow + +$ setfacl -b . +$ setfacl -a0 u:42:Co:f:deny . +$ setfacl -a0 u:43:Co:d:deny . +$ setfacl -a0 u:44:Co:fd:deny . +$ setfacl -a0 u:45:Co:fi:deny . +$ setfacl -a0 u:46:Co:di:deny . +$ setfacl -a0 u:47:Co:fdi:deny . +$ setfacl -a0 u:48:Co:fn:deny . +$ setfacl -a0 u:49:Co:dn:deny . +$ setfacl -a0 u:50:Co:fdn:deny . +$ setfacl -a0 u:51:Co:fni:deny . +$ setfacl -a0 u:52:Co:dni:deny . +$ setfacl -a0 u:53:Co:fdni:deny . +$ umask 022 +$ rm xxx +$ touch xxx +$ getfacl -nq xxx +> user:53:-----------Co-:------:deny +> user:51:-----------Co-:------:deny +> user:50:-----------Co-:------:deny +> user:48:-----------Co-:------:deny +> user:47:-----------Co-:------:deny +> user:45:-----------Co-:------:deny +> user:44:-----------Co-:------:deny +> user:42:-----------Co-:------:deny +> owner@:--x-----------:------:deny +> owner@:rw-p---A-W-Co-:------:allow +> group@:-wxp----------:------:deny +> group@:r-------------:------:allow +> everyone@:-wxp---A-W-Co-:------:deny +> everyone@:r-----a-R-c--s:------:allow + +$ rmdir yyy +$ mkdir yyy +$ getfacl -nq yyy +> user:53:-----------Co-:------:deny +> user:52:-----------Co-:------:deny +> user:50:-----------Co-:------:deny +> user:49:-----------Co-:------:deny +> user:47:-----------Co-:fdi---:deny +> user:47:-----------Co-:------:deny +> user:46:-----------Co-:-di---:deny +> user:46:-----------Co-:------:deny +> user:45:-----------Co-:f-i---:deny +> user:44:-----------Co-:fdi---:deny +> user:44:-----------Co-:------:deny +> user:43:-----------Co-:-di---:deny +> user:43:-----------Co-:------:deny +> user:42:-----------Co-:f-i---:deny +> owner@:--------------:------:deny +> owner@:rwxp---A-W-Co-:------:allow +> group@:-w-p----------:------:deny +> group@:r-x-----------:------:allow +> everyone@:-w-p---A-W-Co-:------:deny +> everyone@:r-x---a-R-c--s:------:allow + +$ rmdir yyy +$ rm xxx +$ cd .. +$ rmdir ddd + +$ rm xxx + diff -urNp current/tools/regression/acltools/tools-posix.test nfs4acl/tools/regression/acltools/tools-posix.test --- current/tools/regression/acltools/tools-posix.test 2009-04-29 09:29:31.000000000 +0200 +++ nfs4acl/tools/regression/acltools/tools-posix.test 2009-04-29 16:29:29.000000000 +0200 @@ -13,7 +13,7 @@ $ umask 022 # Smoke test for getfacl(1). $ touch xxx -$ getfacl xxx +$ getfacl -n xxx > # file: xxx > # owner: root > # group: wheel @@ -27,7 +27,7 @@ $ getfacl -q xxx > other::r-- $ setfacl -m u:42:r,g:43:w xxx -$ getfacl xxx +$ getfacl -n xxx > # file: xxx > # owner: root > # group: wheel @@ -99,8 +99,32 @@ $ getfacl xxx > mask::rw- > other::r-- +$ setfacl -m u:42:r xxx +$ getfacl -n xxx +> # file: xxx +> # owner: root +> # group: wheel +> user::rw- +> user:42:r-- +> group::r-- +> group:43:-w- +> mask::rw- +> other::r-- + +# Test removing entries by number. +$ setfacl -x 1 xxx +$ getfacl -n xxx +> # file: xxx +> # owner: root +> # group: wheel +> user::rw- +> group::r-- +> group:43:-w- +> mask::rw- +> other::r-- + $ setfacl -m g:43:r xxx -$ getfacl xxx +$ getfacl -n xxx > # file: xxx > # owner: root > # group: wheel @@ -118,7 +142,7 @@ $ ls -l yyy | cut -d' ' -f1 # Make sure it does with the "-p" flag. $ rm yyy $ cp -p xxx yyy -$ getfacl yyy +$ getfacl -n yyy > # file: yyy > # owner: root > # group: wheel @@ -133,7 +157,7 @@ $ rm yyy # Test removing entries by... by example? $ setfacl -m u:42:r,g:43:w xxx $ setfacl -x u:42: xxx -$ getfacl xxx +$ getfacl -n xxx > # file: xxx > # owner: root > # group: wheel @@ -145,7 +169,7 @@ $ getfacl xxx # Test setfacl -b. $ setfacl -b xxx -$ getfacl xxx +$ getfacl -n xxx > # file: xxx > # owner: root > # group: wheel @@ -158,7 +182,7 @@ $ ls -l xxx | cut -d' ' -f1 > -rw-r--r--+ $ setfacl -nb xxx -$ getfacl xxx +$ getfacl -n xxx > # file: xxx > # owner: root > # group: wheel @@ -178,7 +202,7 @@ $ ls -l xxx yyy zzz | cut -d' ' -f1 > -rw-r--r-- $ setfacl -m u:42:x,g:43:w nnn xxx yyy zzz -> setfacl: stat() of nnn failed: No such file or directory +> setfacl: nnn: acl_get_file() failed: No such file or directory $ ls -l nnn xxx yyy zzz | cut -d' ' -f1 > ls: nnn: No such file or directory @@ -186,8 +210,8 @@ $ ls -l nnn xxx yyy zzz | cut -d' ' -f1 > -rw-rwxr--+ > -rw-rwxr--+ -$ getfacl -q nnn xxx yyy zzz -> getfacl: nnn: No such file or directory +$ getfacl -nq nnn xxx yyy zzz +> getfacl: nnn: stat() failed: No such file or directory > user::rw- > user:42:--x > group::r-- @@ -210,7 +234,7 @@ $ getfacl -q nnn xxx yyy zzz > other::r-- $ setfacl -b nnn xxx yyy zzz -> setfacl: stat() of nnn failed: No such file or directory +> setfacl: nnn: acl_get_file() failed: No such file or directory $ ls -l nnn xxx yyy zzz | cut -d' ' -f1 > ls: nnn: No such file or directory @@ -219,7 +243,7 @@ $ ls -l nnn xxx yyy zzz | cut -d' ' -f1 > -rw-r--r--+ $ setfacl -bn nnn xxx yyy zzz -> setfacl: stat() of nnn failed: No such file or directory +> setfacl: nnn: acl_get_file() failed: No such file or directory $ ls -l nnn xxx yyy zzz | cut -d' ' -f1 > ls: nnn: No such file or directory @@ -233,7 +257,7 @@ $ rm xxx yyy zzz $ touch xxx $ setfacl -m u:42:rwx,g:43:rwx xxx $ chmod 600 xxx -$ getfacl xxx +$ getfacl -n xxx > # file: xxx > # owner: root > # group: wheel @@ -245,7 +269,7 @@ $ getfacl xxx > other::--- $ chmod 060 xxx -$ getfacl xxx +$ getfacl -n xxx > # file: xxx > # owner: root > # group: wheel @@ -259,7 +283,7 @@ $ getfacl xxx # Test default ACLs. $ umask 022 $ mkdir ddd -$ getfacl -q ddd +$ getfacl -qn ddd > user::rwx > group::r-x > other::r-x @@ -269,7 +293,7 @@ $ ls -l | grep ddd | cut -d' ' -f1 $ getfacl -dq ddd $ setfacl -dm u::rwx,g::rx,o::rx,mask::rwx ddd -$ getfacl -dq ddd +$ getfacl -dqn ddd > user::rwx > group::r-x > mask::rwx @@ -281,7 +305,7 @@ $ ls -l | grep ddd | cut -d' ' -f1 $ setfacl -dm g:42:rwx,u:42:r ddd $ setfacl -dm g::w ddd -$ getfacl -dq ddd +$ getfacl -dqn ddd > user::rwx > user:42:r-- > group::-w- @@ -290,7 +314,7 @@ $ getfacl -dq ddd > other::r-x $ setfacl -dx group:42: ddd -$ getfacl -dq ddd +$ getfacl -dqn ddd > user::rwx > user:42:r-- > group::-w- diff -urNp current/tools/regression/fstest/Makefile nfs4acl/tools/regression/fstest/Makefile --- current/tools/regression/fstest/Makefile 2009-04-29 09:29:33.000000000 +0200 +++ nfs4acl/tools/regression/fstest/Makefile 2009-04-29 09:37:31.000000000 +0200 @@ -4,7 +4,7 @@ OSTYPE=$(shell uname) ifeq "${OSTYPE}" "FreeBSD" CFLAGS += -D__OS_FreeBSD__ -CFLAGS += -DHAS_LCHMOD -DHAS_CHFLAGS -DHAS_LCHFLAGS +CFLAGS += -DHAS_LCHMOD -DHAS_CHFLAGS -DHAS_LCHFLAGS -DHAS_FREEBSD_ACL endif ifeq "${OSTYPE}" "SunOS" Files current/tools/regression/fstest/fstest and nfs4acl/tools/regression/fstest/fstest differ diff -urNp current/tools/regression/fstest/fstest.c nfs4acl/tools/regression/fstest/fstest.c --- current/tools/regression/fstest/fstest.c 2009-06-04 22:03:37.000000000 +0200 +++ nfs4acl/tools/regression/fstest/fstest.c 2009-06-04 22:31:30.000000000 +0200 @@ -45,6 +45,9 @@ #define stat64 stat #define lstat64 lstat #endif +#ifdef HAS_FREEBSD_ACL +#include +#endif #ifndef ALLPERMS #define ALLPERMS (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO) @@ -75,7 +78,12 @@ enum action { ACTION_TRUNCATE, ACTION_STAT, ACTION_LSTAT, - ACTION_PATHCONF + ACTION_PATHCONF, +#ifdef HAS_FREEBSD_ACL + ACTION_PREPENDACL, + ACTION_READACL, +#endif + ACTION_WRITE, }; #define TYPE_NONE 0x0000 @@ -118,6 +126,11 @@ static struct syscall_desc syscalls[] = { "stat", ACTION_STAT, { TYPE_STRING, TYPE_STRING, TYPE_NONE } }, { "lstat", ACTION_LSTAT, { TYPE_STRING, TYPE_STRING, TYPE_NONE } }, { "pathconf", ACTION_PATHCONF, { TYPE_STRING, TYPE_STRING, TYPE_NONE } }, +#ifdef HAS_FREEBSD_ACL + { "prependacl", ACTION_PREPENDACL, { TYPE_STRING, TYPE_STRING, TYPE_NONE } }, + { "readacl", ACTION_READACL, { TYPE_STRING, TYPE_NONE } }, +#endif + { "write", ACTION_WRITE, { TYPE_STRING, TYPE_NONE } }, { NULL, -1, { TYPE_NONE } } }; @@ -397,6 +410,11 @@ call_syscall(struct syscall_desc *scall, char *str; long long num; } args[MAX_ARGS]; +#ifdef HAS_FREEBSD_ACL + int entry_id = ACL_FIRST_ENTRY; + acl_t acl, newacl; + acl_entry_t entry, newentry; +#endif /* * Verify correctness of the arguments. @@ -540,6 +558,48 @@ call_syscall(struct syscall_desc *scall, rval = -1; break; } +#ifdef HAS_FREEBSD_ACL + case ACTION_PREPENDACL: + rval = -1; + + acl = acl_get_file(STR(0), ACL_TYPE_NFS4); + if (acl == NULL) + break; + + newacl = acl_from_text(STR(1)); + if (acl == NULL) + break; + + while (acl_get_entry(newacl, entry_id, &newentry) == 1) { + entry_id = ACL_NEXT_ENTRY; + + if (acl_create_entry_np(&acl, &entry, 0)) + break; + + if (acl_copy_entry(entry, newentry)) + break; + } + + rval = acl_set_file(STR(0), ACL_TYPE_NFS4, acl); + break; + + case ACTION_READACL: + acl = acl_get_file(STR(0), ACL_TYPE_NFS4); + if (acl == NULL) + rval = -1; + else + rval = 0; + break; +#endif + + case ACTION_WRITE: + rval = open(STR(0), O_WRONLY); + if (rval < 0) + break; + + rval = write(rval, "x", 1); + break; + default: fprintf(stderr, "unsupported syscall\n"); exit(1); diff -urNp current/tools/regression/fstest/tests/chmod/12.t nfs4acl/tools/regression/fstest/tests/chmod/12.t --- current/tools/regression/fstest/tests/chmod/12.t 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/tools/regression/fstest/tests/chmod/12.t 2009-04-29 09:37:32.000000000 +0200 @@ -0,0 +1,31 @@ +#!/bin/sh + +desc="verify SUID/SGID bit behaviour" + +dir=`dirname $0` +. ${dir}/../misc.sh + +echo "1..10" + +n0=`namegen` +n1=`namegen` +n2=`namegen` + +expect 0 mkdir ${n2} 0755 +cdir=`pwd` +cd ${n2} + +# Check whether writing to the file by non-owner clears the SUID. +expect 0 create ${n0} 04777 +expect 0 -u 65534 -g 65534 write ${n0} +expect 0777 stat ${n0} mode +expect 0 unlink ${n0} + +# Check whether writing to the file by non-owner clears the SGID. +expect 0 create ${n0} 02777 +expect 0 -u 65534 -g 65534 write ${n0} +expect 0777 stat ${n0} mode +expect 0 unlink ${n0} + +cd ${cdir} +expect 0 rmdir ${n2} diff -urNp current/tools/regression/fstest/tests/granular/00.t nfs4acl/tools/regression/fstest/tests/granular/00.t --- current/tools/regression/fstest/tests/granular/00.t 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/tools/regression/fstest/tests/granular/00.t 2009-05-26 17:15:44.000000000 +0200 @@ -0,0 +1,109 @@ +#!/bin/sh + +desc="NFSv4 granular permissions checking - WRITE_DATA vs APPEND_DATA on directories" + +dir=`dirname $0` +. ${dir}/../misc.sh + +echo "1..49" + +n0=`namegen` +n1=`namegen` +n2=`namegen` +n3=`namegen` + +expect 0 mkdir ${n2} 0755 +expect 0 mkdir ${n3} 0777 +cdir=`pwd` +cd ${n2} + +# Tests 2..7 - check out whether root user can do stuff. +# Can create files? +expect 0 create ${n0} 0644 + +# Can create symlinks? +expect 0 link ${n0} ${n1} +expect 0 unlink ${n1} +expect 0 unlink ${n0} + +# Can create directories? +expect 0 mkdir ${n0} 0755 +expect 0 rmdir ${n0} + +# Check whether user 65534 is permitted to create and remove +# files, but not subdirectories. +expect 0 prependacl . user:65534:write_data::allow,user:65534:append_data::deny + +# Can create files? +expect 0 -u 65534 -g 65534 create ${n0} 0644 + +# Can create symlinks? +expect 0 -u 65534 -g 65534 link ${n0} ${n1} +expect 0 -u 65534 -g 65534 unlink ${n1} +expect 0 -u 65534 -g 65534 unlink ${n0} + +# Can create directories? +expect EACCES -u 65534 -g 65534 mkdir ${n0} 0755 +expect ENOENT -u 65534 -g 65534 rmdir ${n0} +expect 0 mkdir ${n0} 0755 +expect 0 -u 65534 -g 65534 rmdir ${n0} + +# Can move files from other directory? +expect 0 create ../${n3}/${n1} 0644 +expect 0 -u 65534 -g 65534 rename ../${n3}/${n1} ${n0} + +# Can move files from other directory overwriting existing files? +expect 0 create ../${n3}/${n1} 0644 +expect 0 -u 65534 -g 65534 rename ../${n3}/${n1} ${n0} + +expect 0 -u 65534 -g 65534 unlink ${n0} + +# Can move directories from other directory? +expect 0 mkdir ../${n3}/${n1} 0777 +expect EACCES -u 65534 -g 65534 rename ../${n3}/${n1} ${n0} + +# Can move directories from other directory overwriting existing directory? +expect EACCES -u 65534 -g 65534 rename ../${n3}/${n1} ${n0} +expect 0 -u 65534 -g 65534 rmdir ../${n3}/${n1} + +# Check whether user 65534 is permitted to create +# subdirectories, but not files - and to remove neither of them. +expect 0 prependacl . user:65534:write_data::deny,user:65534:append_data::allow + +# Can create files? +expect EACCES -u 65534 -g 65534 create ${n0} 0644 + +# Can create symlinks? +expect 0 create ${n0} 0644 +expect EACCES -u 65534 -g 65534 link ${n0} ${n1} +expect ENOENT -u 65534 -g 65534 unlink ${n1} +expect EACCES -u 65534 -g 65534 unlink ${n0} +expect 0 unlink ${n0} + +# Can create directories? +expect 0 -u 65534 -g 65534 mkdir ${n0} 0755 +expect EACCES -u 65534 -g 65534 rmdir ${n0} +expect 0 rmdir ${n0} + +# Can move files from other directory? +expect 0 create ../${n3}/${n1} 0644 +expect EACCES -u 65534 -g 65534 rename ../${n3}/${n1} ${n0} + +# Can move files from other directory overwriting existing files? +expect EACCES -u 65534 -g 65534 rename ../${n3}/${n1} ${n0} +expect 0 -u 65534 -g 65534 unlink ../${n3}/${n1} + +# Can move directories from other directory? +expect 0 mkdir ../${n3}/${n1} 0777 +expect 0 -u 65534 -g 65534 rename ../${n3}/${n1} ${n0} + +# Can move directories from other directory overwriting existing directory? +expect 0 mkdir ../${n3}/${n1} 0777 +expect EACCES -u 65534 -g 65534 rename ../${n3}/${n1} ${n0} +expect 0 prependacl . user:65534:delete_child::allow +expect 0 -u 65534 -g 65534 rename ../${n3}/${n1} ${n0} +expect 0 -u 65534 -g 65534 rmdir ${n0} + +cd ${cdir} +expect 0 rmdir ${n2} +expect 0 rmdir ${n3} diff -urNp current/tools/regression/fstest/tests/granular/01.t nfs4acl/tools/regression/fstest/tests/granular/01.t --- current/tools/regression/fstest/tests/granular/01.t 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/tools/regression/fstest/tests/granular/01.t 2009-05-26 17:15:44.000000000 +0200 @@ -0,0 +1,34 @@ +#!/bin/sh + +desc="NFSv4 granular permissions checking - ACL_READ_ATTRIBUTES and ACL_WRITE_ATTRIBUTES" + +dir=`dirname $0` +. ${dir}/../misc.sh + +echo "1..12" + +n0=`namegen` +n1=`namegen` +n2=`namegen` + +expect 0 mkdir ${n2} 0755 +cdir=`pwd` +cd ${n2} + +# Tests 1..12 - check out whether user 65534 is permitted to read attributes. +expect 0 create ${n0} 0644 +expect 0 lstat ${n0} size +expect 0 -u 65534 -g 65534 stat ${n0} size +expect 0 prependacl ${n0} user:65534:read_attributes::deny +expect 0 lstat ${n0} size +expect EACCES -u 65534 -g 65534 stat ${n0} size +expect 0 prependacl ${n0} user:65534:read_attributes::allow +expect 0 -u 65534 -g 65534 stat ${n0} size +expect 0 lstat ${n0} size +expect 0 unlink ${n0} + +# Tests 12..12 - check out whether user 65534 is permitted to write attributes. +# XXX: Check if ACL_WRITE_ATTRIBUTES allows for modifying access times. + +cd ${cdir} +expect 0 rmdir ${n2} diff -urNp current/tools/regression/fstest/tests/granular/02.t nfs4acl/tools/regression/fstest/tests/granular/02.t --- current/tools/regression/fstest/tests/granular/02.t 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/tools/regression/fstest/tests/granular/02.t 2009-05-26 17:15:44.000000000 +0200 @@ -0,0 +1,141 @@ +#!/bin/sh + +desc="NFSv4 granular permissions checking - ACL_READ_ACL and ACL_WRITE_ACL" + +dir=`dirname $0` +. ${dir}/../misc.sh + +echo "1..83" + +n0=`namegen` +n1=`namegen` +n2=`namegen` + +expect 0 mkdir ${n2} 0755 +cdir=`pwd` +cd ${n2} + +# Check whether user 65534 is permitted to read ACL. +expect 0 create ${n0} 0644 +expect 0 readacl ${n0} +expect 0 -u 65534 -g 65534 readacl ${n0} +expect 0 prependacl ${n0} user:65534:read_acl::deny +expect 0 readacl ${n0} +expect EACCES -u 65534 -g 65534 readacl ${n0} +expect 0 prependacl ${n0} user:65534:read_acl::allow +expect 0 -u 65534 -g 65534 readacl ${n0} +expect 0 readacl ${n0} +expect 0 unlink ${n0} + +# Check whether user 65534 is permitted to write ACL. +expect 0 create ${n0} 0644 +expect EPERM -u 65534 -g 65534 prependacl ${n0} user:65534:read_data::allow +expect 0 prependacl ${n0} user:65534:write_acl::allow +expect 0 -u 65534 -g 65534 prependacl ${n0} user:65534:read_data::allow +expect 0 unlink ${n0} + +# Check whether user 65534 is permitted to write mode. +expect 0 create ${n0} 0755 +expect EPERM -u 65534 -g 65534 chmod ${n0} 0777 +expect 0 prependacl ${n0} user:65534:write_acl::allow +expect 0 -u 65534 -g 65534 chmod ${n0} 0777 +expect 0 unlink ${n0} + +# There is an interesting problem with interaction between ACL_WRITE_ACL +# and SUID/SGID bits. In case user does have ACL_WRITE_ACL, but is not +# a file owner, Solaris does the following: +# 1. Setting SUID fails with EPERM. +# 2. Setting SGID succeeds, but mode is not changed. +# 3. Modifying ACL does not clear SUID nor SGID bits. +# 4. Writing the file does clear both SUID and SGID bits. +# +# What we are doing is the following: +# 1. Setting SUID or SGID fails with EPERM. +# 2. Modifying ACL does not clear SUID nor SGID bits. +# 3. Writing the file does clear both SUID and SGID bits. +# +# Check whether user 65534 is denied to write mode with SUID bit. +expect 0 create ${n0} 0755 +expect EPERM -u 65534 -g 65534 chmod ${n0} 04777 +expect 0 prependacl ${n0} user:65534:write_acl::allow +expect EPERM -u 65534 -g 65534 chmod ${n0} 04777 +expect 0 unlink ${n0} + +# Check whether user 65534 is denied to write mode with SGID bit. +expect 0 create ${n0} 0755 +expect EPERM -u 65534 -g 65534 chmod ${n0} 02777 +expect 0 prependacl ${n0} user:65534:write_acl::allow +expect EPERM -u 65534 -g 65534 chmod ${n0} 02777 +expect 0 unlink ${n0} + +# Check whether user 65534 is allowed to write mode with sticky bit. +expect 0 mkdir ${n0} 0755 +expect EPERM -u 65534 -g 65534 chmod ${n0} 01777 +expect 0 prependacl ${n0} user:65534:write_acl::allow +expect 0 -u 65534 -g 65534 chmod ${n0} 01777 +expect 0 rmdir ${n0} + +# Check whether modifying the ACL by not-owner preserves the SUID. +expect 0 create ${n0} 04755 +expect 0 prependacl ${n0} user:65534:write_acl::allow +expect 0 -u 65534 -g 65534 prependacl ${n0} user:65534:write_data::allow +expect 04755 stat ${n0} mode +expect 0 unlink ${n0} + +# Check whether modifying the ACL by not-owner preserves the SGID. +expect 0 create ${n0} 02755 +expect 0 prependacl ${n0} user:65534:write_acl::allow +expect 0 -u 65534 -g 65534 prependacl ${n0} user:65534:write_data::allow +expect 02755 stat ${n0} mode +expect 0 unlink ${n0} + +# Check whether modifying the ACL by not-owner preserves the sticky bit. +expect 0 mkdir ${n0} 0755 +expect 0 chmod ${n0} 01755 +expect 0 prependacl ${n0} user:65534:write_acl::allow +expect 0 -u 65534 -g 65534 prependacl ${n0} user:65534:write_data::allow +expect 01755 stat ${n0} mode +expect 0 rmdir ${n0} + +# Clearing the SUID and SGID bits when being written to by non-owner +# is checked in chmod/12.t. + +# Check whether the file owner is always permitted to get and set +# ACL and file mode, even if ACL_{READ,WRITE}_ACL would deny it. +expect 0 chmod . 0777 +expect 0 -u 65534 -g 65534 create ${n0} 0600 +expect 0 -u 65534 -g 65534 prependacl ${n0} user:65534:write_acl::deny +expect 0 -u 65534 -g 65534 prependacl ${n0} user:65534:read_acl::deny +expect 0 -u 65534 -g 65534 readacl ${n0} +expect 0600 -u 65534 -g 65534 stat ${n0} mode +expect 0 -u 65534 -g 65534 chmod ${n0} 0777 +expect 0 unlink ${n0} + +expect 0 -u 65534 -g 65534 mkdir ${n0} 0600 +expect 0 -u 65534 -g 65534 prependacl ${n0} user:65534:write_acl::deny +expect 0 -u 65534 -g 65534 prependacl ${n0} user:65534:read_acl::deny +expect 0 -u 65534 -g 65534 readacl ${n0} +expect 0600 -u 65534 -g 65534 stat ${n0} mode +expect 0 -u 65534 -g 65534 chmod ${n0} 0777 +expect 0 rmdir ${n0} + +# Check whether the root is allowed for these as well. +expect 0 -u 65534 -g 65534 create ${n0} 0600 +expect 0 prependacl ${n0} everyone@:write_acl::deny +expect 0 prependacl ${n0} everyone@:read_acl::deny +expect 0 readacl ${n0} +expect 0600 stat ${n0} mode +expect 0 chmod ${n0} 0777 +expect 0 unlink ${n0} + +expect 0 -u 65534 -g 65534 mkdir ${n0} 0600 +expect 0 prependacl ${n0} everyone@:write_acl::deny +expect 0 prependacl ${n0} everyone@:read_acl::deny +expect 0600 stat ${n0} mode +expect 0 readacl ${n0} +expect 0600 stat ${n0} mode +expect 0 chmod ${n0} 0777 +expect 0 rmdir ${n0} + +cd ${cdir} +expect 0 rmdir ${n2} diff -urNp current/tools/regression/fstest/tests/granular/03.t nfs4acl/tools/regression/fstest/tests/granular/03.t --- current/tools/regression/fstest/tests/granular/03.t 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/tools/regression/fstest/tests/granular/03.t 2009-05-26 17:15:44.000000000 +0200 @@ -0,0 +1,131 @@ +#!/bin/sh + +desc="NFSv4 granular permissions checking - DELETE and DELETE_CHILD" + +dir=`dirname $0` +. ${dir}/../misc.sh + +echo "1..65" + +n0=`namegen` +n1=`namegen` +n2=`namegen` +n3=`namegen` + +expect 0 mkdir ${n2} 0755 +expect 0 mkdir ${n3} 0777 +cdir=`pwd` +cd ${n2} + +# Unlink allowed on writable directory. +expect 0 create ${n0} 0644 +expect EACCES -u 65534 -g 65534 unlink ${n0} +expect 0 prependacl . user:65534:write_data::allow +expect 0 -u 65534 -g 65534 unlink ${n0} + +# Moving file elsewhere allowed on writable directory. +expect 0 create ${n0} 0644 +expect 0 prependacl . user:65534:write_data::deny +expect EACCES -u 65534 -g 65534 rename ${n0} ../${n3}/${n0} +expect 0 prependacl . user:65534:write_data::allow +expect 0 -u 65534 -g 65534 rename ${n0} ../${n3}/${n0} + +# Moving file from elsewhere allowed on writable directory. +expect 0 -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} +expect 0 -u 65534 -g 65534 unlink ${n0} + +# Moving file from elsewhere overwriting local file allowed +# on writable directory. +expect 0 create ${n0} 0644 +expect 0 create ../${n3}/${n0} 0644 +expect 0 prependacl . user:65534:write_data::deny +expect EACCES -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} +expect 0 prependacl . user:65534:write_data::allow +expect 0 -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} +expect 0 -u 65534 -g 65534 unlink ${n0} + +# Denied DELETE changes nothing wrt removing. +expect 0 create ${n0} 0644 +expect 0 prependacl ${n0} user:65534:delete::deny +expect 0 -u 65534 -g 65534 unlink ${n0} + +# Denied DELETE changes nothing wrt moving elsewhere or from elsewhere. +expect 0 create ${n0} 0644 +expect 0 -u 65534 -g 65534 rename ${n0} ../${n3}/${n0} +expect 0 -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} +expect 0 -u 65534 -g 65534 unlink ${n0} + +# DELETE_CHILD denies unlink on writable directory. +expect 0 create ${n0} 0644 +expect 0 prependacl . user:65534:delete_child::deny +expect EPERM -u 65534 -g 65534 unlink ${n0} +expect 0 unlink ${n0} + +# DELETE_CHILD denies moving file elsewhere. +expect 0 create ${n0} 0644 +expect EPERM -u 65534 -g 65534 rename ${n0} ../${n3}/${n0} +expect 0 rename ${n0} ../${n3}/${n0} + +# DELETE_CHILD does not deny moving file from elsewhere +# to a writable directory. +expect 0 -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} + +# DELETE_CHILD denies moving file from elsewhere +# to a writable directory overwriting local file. +expect 0 create ../${n3}/${n0} 0644 +expect EPERM -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} + +# DELETE allowed on file allows for unlinking, no matter +# what permissions on containing directory are. +expect 0 prependacl ${n0} user:65534:delete::allow +expect 0 -u 65534 -g 65534 unlink ${n0} + +# Same for moving the file elsewhere. +expect 0 create ${n0} 0644 +expect 0 prependacl ${n0} user:65534:delete::allow +expect 0 -u 65534 -g 65534 rename ${n0} ../${n3}/${n0} + +# Same for moving the file from elsewhere into a writable +# directory with DELETE_CHILD denied. +expect 0 -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} +expect 0 unlink ${n0} + +# DELETE does not allow for overwriting a file in a unwritable +# directory with DELETE_CHILD denied. +expect 0 create ${n0} 0644 +expect 0 create ../${n3}/${n0} 0644 +expect 0 prependacl . user:65534:write_data::deny +expect 0 prependacl . user:65534:delete_child::deny +expect EPERM -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} +expect 0 prependacl ${n0} user:65534:delete::allow +expect EACCES -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} + +# But it allows for plain deletion. +expect 0 -u 65534 -g 65534 unlink ${n0} + +# DELETE_CHILD allowed on unwritable directory. +expect 0 create ${n0} 0644 +expect 0 prependacl . user:65534:delete_child::allow +expect 0 -u 65534 -g 65534 unlink ${n0} + +# Moving things elsewhere is allowed. +expect 0 create ${n0} 0644 +expect 0 -u 65534 -g 65534 rename ${n0} ../${n3}/${n0} + +# Moving things back is not. +expect EACCES -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} + +# Even if we're overwriting. +expect 0 create ${n0} 0644 +expect EACCES -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} + +# Even if we have DELETE on the existing file. +expect 0 prependacl ${n0} user:65534:delete::allow +expect EACCES -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} + +# Denied DELETE changes nothing wrt removing. +expect 0 prependacl ${n0} user:65534:delete::deny +expect 0 -u 65534 -g 65534 unlink ${n0} + +cd ${cdir} +expect 0 rmdir ${n2} diff -urNp current/tools/regression/fstest/tests/granular/04.t nfs4acl/tools/regression/fstest/tests/granular/04.t --- current/tools/regression/fstest/tests/granular/04.t 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/tools/regression/fstest/tests/granular/04.t 2009-05-26 17:15:44.000000000 +0200 @@ -0,0 +1,77 @@ +#!/bin/sh + +desc="NFSv4 granular permissions checking - ACL_WRITE_OWNER" + +dir=`dirname $0` +. ${dir}/../misc.sh + +echo "1..52" + +n0=`namegen` +n1=`namegen` +n2=`namegen` + +expect 0 mkdir ${n2} 0755 +cdir=`pwd` +cd ${n2} + +# ACL_WRITE_OWNER permits to set gid to our own only. +expect 0 create ${n0} 0644 +expect 0,0 lstat ${n0} uid,gid +expect EPERM -u 65534 -g 65532,65531 chown ${n0} -1 65532 +expect 0,0 lstat ${n0} uid,gid +expect 0 prependacl ${n0} user:65534:write_owner::allow +expect EPERM -u 65534 -g 65532,65531 chown ${n0} -1 65530 +expect 0,0 lstat ${n0} uid,gid +expect 0 -u 65534 -g 65532,65531 chown ${n0} -1 65532 +expect 0,65532 lstat ${n0} uid,gid +expect 0 unlink ${n0} + +# ACL_WRITE_OWNER permits to set uid to our own only. +expect 0 create ${n0} 0644 +expect 0,0 lstat ${n0} uid,gid +expect EPERM -u 65534 -g 65532,65531 chown ${n0} 65534 65531 +expect 0,0 lstat ${n0} uid,gid +expect 0 prependacl ${n0} user:65534:write_owner::allow +expect EPERM -u 65534 -g 65532,65531 chown ${n0} 65530 65531 +expect 0,0 lstat ${n0} uid,gid +expect 0 -u 65534 -g 65532,65531 chown ${n0} 65534 65531 +expect 65534,65531 lstat ${n0} uid,gid +expect 0 unlink ${n0} + +# When non-owner calls chown(2) successfully, set-uid and set-gid bits are +# removed, except when both uid and gid are equal to -1. +expect 0 create ${n0} 0644 +expect 0 prependacl ${n0} user:65534:write_owner::allow +expect 0 chmod ${n0} 06555 +expect 06555 lstat ${n0} mode +expect 0 -u 65534 -g 65533,65532 chown ${n0} 65534 65532 +expect 0555,65534,65532 lstat ${n0} mode,uid,gid +expect 0 chmod ${n0} 06555 +expect 06555 lstat ${n0} mode +expect 0 -u 65534 -g 65533,65532 chown ${n0} -1 65533 +expect 0555,65534,65533 lstat ${n0} mode,uid,gid +expect 0 chmod ${n0} 06555 +expect 06555 lstat ${n0} mode +expect 0 -u 65534 -g 65533,65532 chown ${n0} -1 -1 +expect 06555,65534,65533 lstat ${n0} mode,uid,gid +expect 0 unlink ${n0} + +expect 0 mkdir ${n0} 0755 +expect 0 prependacl ${n0} user:65534:write_owner::allow +expect 0 chmod ${n0} 06555 +expect 06555 lstat ${n0} mode +expect 0 -u 65534 -g 65533,65532 chown ${n0} 65534 65532 +expect 0555,65534,65532 lstat ${n0} mode,uid,gid +expect 0 chmod ${n0} 06555 +expect 06555 lstat ${n0} mode +expect 0 -u 65534 -g 65533,65532 chown ${n0} -1 65533 +expect 0555,65534,65533 lstat ${n0} mode,uid,gid +expect 0 chmod ${n0} 06555 +expect 06555 lstat ${n0} mode +expect 0 -u 65534 -g 65533,65532 chown ${n0} -1 -1 +expect 06555,65534,65533 lstat ${n0} mode,uid,gid +expect 0 rmdir ${n0} + +cd ${cdir} +expect 0 rmdir ${n2} diff -urNp current/tools/regression/fstest/tests/granular/05.t nfs4acl/tools/regression/fstest/tests/granular/05.t --- current/tools/regression/fstest/tests/granular/05.t 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/tools/regression/fstest/tests/granular/05.t 2009-05-26 17:15:44.000000000 +0200 @@ -0,0 +1,146 @@ +#!/bin/sh + +desc="NFSv4 granular permissions checking - DELETE and DELETE_CHILD with directories" + +dir=`dirname $0` +. ${dir}/../misc.sh + +echo "1..68" + +n0=`namegen` +n1=`namegen` +n2=`namegen` +n3=`namegen` + +expect 0 mkdir ${n2} 0755 +expect 0 mkdir ${n3} 0777 +cdir=`pwd` +cd ${n2} + +# Unlink allowed on writable directory. +expect 0 mkdir ${n0} 0755 +expect EACCES -u 65534 -g 65534 rmdir ${n0} +expect 0 prependacl . user:65534:write_data::allow +expect 0 -u 65534 -g 65534 rmdir ${n0} + +# Moving directory elsewhere allowed on writable directory. +expect 0 mkdir ${n0} 0777 +expect 0 prependacl . user:65534:write_data::deny +expect EACCES -u 65534 -g 65534 rename ${n0} ../${n3}/${n0} +expect 0 prependacl . user:65534:write_data::allow +expect 0 -u 65534 -g 65534 rename ${n0} ../${n3}/${n0} + +# 12 +# Moving directory from elsewhere allowed on writable directory. +expect EACCES -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} +expect 0 prependacl . user:65534:append_data::allow +expect 0 -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} +expect 0 -u 65534 -g 65534 rmdir ${n0} + +# Moving directory from elsewhere overwriting local directory allowed +# on writable directory. +expect 0 mkdir ${n0} 0755 +expect 0 mkdir ../${n3}/${n0} 0777 +expect 0 prependacl . user:65534:write_data::deny +expect EACCES -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} +expect 0 prependacl . user:65534:write_data::allow +expect 0 -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} +expect 0 -u 65534 -g 65534 rmdir ${n0} + +# 23 +# Denied DELETE changes nothing wrt removing. +expect 0 mkdir ${n0} 0755 +expect 0 prependacl ${n0} user:65534:delete::deny +expect 0 -u 65534 -g 65534 rmdir ${n0} + +# Denied DELETE changes nothing wrt moving elsewhere or from elsewhere. +expect 0 mkdir ${n0} 0777 +expect 0 -u 65534 -g 65534 rename ${n0} ../${n3}/${n0} +expect 0 -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} +expect 0 -u 65534 -g 65534 rmdir ${n0} + +# DELETE_CHILD denies unlink on writable directory. +expect 0 mkdir ${n0} 0755 +expect 0 prependacl . user:65534:delete_child::deny +expect EPERM -u 65534 -g 65534 rmdir ${n0} +expect 0 rmdir ${n0} + +# 35 +# DELETE_CHILD denies moving directory elsewhere. +expect 0 mkdir ${n0} 0777 +expect EPERM -u 65534 -g 65534 rename ${n0} ../${n3}/${n0} +expect 0 rename ${n0} ../${n3}/${n0} + +# DELETE_CHILD does not deny moving directory from elsewhere +# to a writable directory. +expect 0 -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} + +# DELETE_CHILD denies moving directory from elsewhere +# to a writable directory overwriting local directory. +expect 0 mkdir ../${n3}/${n0} 0755 +expect EACCES -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} + +# DELETE allowed on directory allows for unlinking, no matter +# what permissions on containing directory are. +expect 0 prependacl ${n0} user:65534:delete::allow +expect 0 -u 65534 -g 65534 rmdir ${n0} + +# Same for moving the directory elsewhere. +expect 0 mkdir ${n0} 0777 +expect 0 prependacl ${n0} user:65534:delete::allow +expect 0 -u 65534 -g 65534 rename ${n0} ../${n3}/${n0} + +# 46 +# Same for moving the directory from elsewhere into a writable +# directory with DELETE_CHILD denied. +expect 0 -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} +expect 0 rmdir ${n0} + +# DELETE does not allow for overwriting a directory in a unwritable +# directory with DELETE_CHILD denied. +expect 0 mkdir ${n0} 0755 +expect 0 mkdir ../${n3}/${n0} 0777 +expect 0 prependacl . user:65534:write_data::deny +expect 0 prependacl . user:65534:delete_child::deny +expect EPERM -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} +expect 0 prependacl ${n0} user:65534:delete::allow +# XXX: expect EACCES -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} +expect 0 -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} + +# 54 +# But it allows for plain deletion. +# XXX: expect 0 -u 65534 -g 65534 rmdir ${n0} +expect 0 rmdir ${n0} + +# DELETE_CHILD allowed on unwritable directory. +expect 0 mkdir ${n0} 0755 +expect 0 prependacl . user:65534:delete_child::allow +expect 0 -u 65534 -g 65534 rmdir ${n0} + +# Moving things elsewhere is allowed. +expect 0 mkdir ${n0} 0777 +expect 0 -u 65534 -g 65534 rename ${n0} ../${n3}/${n0} + +# 60 +# Moving things back is not. +# XXX: expect EACCES -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} +expect 0 -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} + +# Even if we're overwriting. +# XXX: expect 0 mkdir ${n0} 0755 +expect 0 mkdir ../${n3}/${n0} 0777 +# XXX: expect EACCES -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} +expect 0 -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} +expect 0 mkdir ../${n3}/${n0} 0777 + +# Even if we have DELETE on the existing directory. +expect 0 prependacl ${n0} user:65534:delete::allow +# XXX: expect EACCES -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} +expect 0 -u 65534 -g 65534 rename ../${n3}/${n0} ${n0} + +# Denied DELETE changes nothing wrt removing. +expect 0 prependacl ${n0} user:65534:delete::deny +expect 0 -u 65534 -g 65534 rmdir ${n0} + +cd ${cdir} +expect 0 rmdir ${n2}