diff -urN current/TODO nfs4acl/TODO --- current/TODO 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/TODO 2008-11-25 09:46:16.000000000 +0100 @@ -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 NFS4 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 -urN current/bin/chmod/chmod.c nfs4acl/bin/chmod/chmod.c --- current/bin/chmod/chmod.c 2008-11-25 20:34:55.000000000 +0100 +++ nfs4acl/bin/chmod/chmod.c 2008-11-21 10:16:59.000000000 +0100 @@ -54,6 +54,7 @@ #include void usage(void); +int may_have_nfs4acl(const FTSENT *ent); int main(int argc, char *argv[]) @@ -180,8 +181,15 @@ break; } newmode = getmode(set, p->fts_statp->st_mode); - if ((newmode & ALLPERMS) == (p->fts_statp->st_mode & ALLPERMS)) - continue; + /* + * With NFS4 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: 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 -urN current/bin/cp/utils.c nfs4acl/bin/cp/utils.c --- current/bin/cp/utils.c 2008-11-25 20:34:55.000000000 +0100 +++ nfs4acl/bin/cp/utils.c 2008-11-21 10:17:00.000000000 +0100 @@ -377,21 +377,57 @@ 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 ? "NFS4" : "POSIX", + dest_type == ACL_TYPE_NFS4 ? "NFS4" : "POSIX"); + 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 @@ 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 ? "NFS4" : "POSIX", + dest_type == ACL_TYPE_NFS4 ? "NFS4" : "POSIX"); + + return (0); + } + /* * If the file is a link we will not follow it */ @@ -419,31 +486,35 @@ 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 -urN current/bin/getfacl/getfacl.1 nfs4acl/bin/getfacl/getfacl.1 --- current/bin/getfacl/getfacl.1 2008-11-25 20:35:02.000000000 +0100 +++ nfs4acl/bin/getfacl/getfacl.1 2008-11-21 10:17:05.000000000 +0100 @@ -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 @@ access ACL. An error is generated if a default ACL cannot be associated with .Ar file . +This option is not valid for NFS4 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 NFS4 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 NFS4 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 -urN current/bin/getfacl/getfacl.c nfs4acl/bin/getfacl/getfacl.c --- current/bin/getfacl/getfacl.c 2008-11-25 20:35:02.000000000 +0100 +++ nfs4acl/bin/getfacl/getfacl.c 2008-11-21 10:17:06.000000000 +0100 @@ -54,7 +54,7 @@ usage(void) { - fprintf(stderr, "getfacl [-dhq] [file ...]\n"); + fprintf(stderr, "getfacl [-dhnqv] [file ...]\n"); } static char * @@ -175,19 +175,20 @@ } 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 @@ else more_than_one++; + if (pathconf(path, _PC_EXTENDED_SECURITY_NP)) { + if (type == ACL_TYPE_DEFAULT) { + warnx("%s: there are no default entries in NFS4 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 @@ 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 @@ } 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 @@ 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 @@ 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 @@ 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 @@ 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 -urN current/bin/ls/print.c nfs4acl/bin/ls/print.c --- current/bin/ls/print.c 2008-11-25 20:35:03.000000000 +0100 +++ nfs4acl/bin/ls/print.c 2008-11-21 10:17:07.000000000 +0100 @@ -616,9 +616,8 @@ 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 @@ *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 -urN current/bin/mv/mv.c nfs4acl/bin/mv/mv.c --- current/bin/mv/mv.c 2008-11-25 20:35:03.000000000 +0100 +++ nfs4acl/bin/mv/mv.c 2008-11-21 10:17:07.000000000 +0100 @@ -74,6 +74,8 @@ 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 @@ 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 @@ 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 @@ 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 ? "NFS4" : "POSIX", + dest_type == ACL_TYPE_NFS4 ? "NFS4" : "POSIX"); + + 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 -urN current/bin/setfacl/mask.c nfs4acl/bin/setfacl/mask.c --- current/bin/setfacl/mask.c 2008-11-25 20:35:08.000000000 +0100 +++ nfs4acl/bin/setfacl/mask.c 2008-11-21 10:17:16.000000000 +0100 @@ -40,7 +40,7 @@ /* 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 @@ 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 @@ * 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 @@ 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 @@ * 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 -urN current/bin/setfacl/merge.c nfs4acl/bin/setfacl/merge.c --- current/bin/setfacl/merge.c 2008-11-25 20:35:08.000000000 +0100 +++ nfs4acl/bin/setfacl/merge.c 2008-11-21 10:17:16.000000000 +0100 @@ -36,12 +36,15 @@ #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_extended_t extended; + acl_flagset_t flagset; int have_entry; uid_t *id, *id_new; @@ -59,6 +62,18 @@ 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_extended_np(*entry, &extended)) + err(1, "acl_get_extended_np() failed"); + if (acl_set_extended_np(*entry_new, extended)) + err(1, "acl_set_extended_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 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_extended_t extended, extended_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 ? "NFS4" : "POSIX", + acl_brand == ACL_BRAND_NFS4 ? "NFS4" : "POSIX"); + 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 @@ /* 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 NFS4, in addition to "tag" and "id" we also + * compare "extended". + */ + if (acl_brand == ACL_BRAND_NFS4) { + if (acl_get_extended_np(entry, &extended)) + err(1, "%s: acl_get_extended_np() " + "failed", filename); + if (acl_get_extended_np(entry_new, &extended_new)) + err(1, "%s: acl_get_extended_np() " + "failed", filename); + if (extended != extended_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 @@ 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_extended_np(entry, &extended)) + err(1, "%s: acl_get_extended_np() failed", + filename); + if (acl_set_extended_np(entry_new, extended)) + err(1, "%s: acl_set_extended_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); + + /* + * NFS4 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 NFS4 ACLs", + filename); + return (-1); + } + + if (acl_brand != ACL_BRAND_NFS4) { + warnx("%s: branding mismatch; existing ACL is NFS4, " + "entry to be added is %s", filename, + acl_brand == ACL_BRAND_NFS4 ? "NFS4" : "POSIX"); + 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 >= (uint)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 -urN current/bin/setfacl/remove.c nfs4acl/bin/setfacl/remove.c --- current/bin/setfacl/remove.c 2008-11-25 20:35:08.000000000 +0100 +++ nfs4acl/bin/setfacl/remove.c 2008-11-21 10:17:16.000000000 +0100 @@ -41,21 +41,31 @@ * 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 ? "NFS4" : "POSIX", + acl_brand == ACL_BRAND_NFS4 ? "NFS4" : "POSIX"); + 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 @@ 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 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 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 -urN current/bin/setfacl/setfacl.1 nfs4acl/bin/setfacl/setfacl.1 --- current/bin/setfacl/setfacl.1 2008-11-25 20:35:08.000000000 +0100 +++ nfs4acl/bin/setfacl/setfacl.1 2008-11-21 10:17:16.000000000 +0100 @@ -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 @@ .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 NFS4 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 (NFS4 ACLs). +If the POSIX.1e ACL contains a .Dq Li mask entry, the permissions of the .Dq Li group @@ -66,7 +77,7 @@ 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 NFS4 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 @@ 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 NFS4 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 @@ 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 NFS4 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 @@ .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 @@ .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 @@ For more details see the examples below. Default ACLs can be created by using .Fl d . +.Sh NFS4 ACL ENTRIES +An NFS4 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 +NFS4 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 @@ .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 NFS4 ACL. +.Pp .Dl setfacl -M file1 file2 .Pp Sets/updates the ACL entries contained in @@ -271,10 +387,15 @@ .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 NFS4 ACL from +.Pa file . +.Pp .Dl setfacl -bn file .Pp Remove all diff -urN current/bin/setfacl/setfacl.c nfs4acl/bin/setfacl/setfacl.c --- current/bin/setfacl/setfacl.c 2008-11-25 20:35:08.000000000 +0100 +++ nfs4acl/bin/setfacl/setfacl.c 2008-11-21 10:17:16.000000000 +0100 @@ -41,9 +41,9 @@ #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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ /* 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 NFS4 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 @@ 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 NFS4 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 @@ 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 -urN current/bin/setfacl/setfacl.h nfs4acl/bin/setfacl/setfacl.h --- current/bin/setfacl/setfacl.h 2008-11-25 20:35:08.000000000 +0100 +++ nfs4acl/bin/setfacl/setfacl.h 2008-11-21 10:17:17.000000000 +0100 @@ -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 @@ /* 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 -urN current/lib/libc/posix1e/Makefile.inc nfs4acl/lib/libc/posix1e/Makefile.inc --- current/lib/libc/posix1e/Makefile.inc 2008-11-25 21:03:51.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/Makefile.inc 2008-11-21 11:03:43.000000000 +0100 @@ -2,51 +2,74 @@ .PATH: ${.CURDIR}/posix1e -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_extended_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_extended_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 \ @@ -60,15 +83,18 @@ 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 -urN current/lib/libc/posix1e/Symbol.map nfs4acl/lib/libc/posix1e/Symbol.map --- current/lib/libc/posix1e/Symbol.map 2008-11-25 21:03:51.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/Symbol.map 2008-11-21 11:03:43.000000000 +0100 @@ -21,15 +21,12 @@ acl_get_link_np; acl_get_fd; acl_get_fd_np; - acl_get_perm_np; acl_get_permset; acl_get_qualifier; acl_get_tag_type; acl_init; acl_dup; - acl_add_perm; acl_clear_perms; - acl_delete_perm; acl_set_file; acl_set_link_np; acl_set_fd; @@ -67,3 +64,24 @@ mac_set_link; mac_set_proc; }; + +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_extended_np; + acl_get_flag_np; + acl_get_flagset_np; + acl_get_perm_np; + acl_is_trivial_np; + acl_set_extended_np; + acl_set_flagset_np; + acl_strip_np; + acl_to_text_np; +}; + diff -urN current/lib/libc/posix1e/acl.3 nfs4acl/lib/libc/posix1e/acl.3 --- current/lib/libc/posix1e/acl.3 2008-11-25 21:03:51.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl.3 2008-11-21 11:03:43.000000000 +0100 @@ -48,7 +48,7 @@ and validate ACLs associated with file objects. As well as the POSIX.1e routines, there are a number of non-portable extensions defined that allow for alternative ACL semantics than the -POSIX.1e semantics, such as AFS, NTFS, Coda, and NWFS semantics. +POSIX.1e semantics, such as NFS4, AFS, NTFS, Coda, and NWFS semantics. Where routines are non-standard, they are suffixed with _np to indicate that they are not portable. .Pp @@ -59,6 +59,10 @@ .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 @@ 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 @@ 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 @@ 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 @@ These functions are described in .Xr acl_get 3 , and may be used to retrieve ACLs from file system objects. +.It Fn acl_get_extended_np +This function is described in +.Xr acl_get_extended_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 @@ 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 @@ 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_extended_np +This function is described in +.Xr acl_set_extended_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 @@ 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 @@ .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_extended_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_extended_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 -urN 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 2008-11-21 11:03:43.000000000 +0100 @@ -0,0 +1,98 @@ +.\"- +.\" Copyright (c) 2008 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 +.Bd -literal -offset indent -compact +ACL_ENTRY_FILE_INHERIT Will be inherited by files. +ACL_ENTRY_DIRECTORY_INHERIT Will be inherited by directories. +ACL_ENTRY_LIMIT_INHERIT Will not propagate. +ACL_ENTRY_ONLY_INHERIT Inherit-only. +.Ed +.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 -urN current/lib/libc/posix1e/acl_add_perm.3 nfs4acl/lib/libc/posix1e/acl_add_perm.3 --- current/lib/libc/posix1e/acl_add_perm.3 2008-11-25 21:03:51.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_add_perm.3 2008-11-21 11:03:43.000000000 +0100 @@ -49,6 +49,46 @@ .Pp Note: it is not considered an error to attempt to add permissions that already exist in the permission set. +.Pp +For POSIX.1e ACLs, valid values are: +.Pp +.Bd -literal -offset indent -compact +ACL_EXECUTE Execute permission. +ACL_WRITE Write permission. +ACL_READ Read permission. +.Ed +.Pp +For NFS4 ACLs, valid values are: +.Pp +.Bd -literal -offset indent -compact +ACL_READ_DATA Read permission. +ACL_LIST_DIRECTORY Same as ACL_READ_DATA. +ACL_WRITE_DATA Write permission, or permission to create files. +ACL_ADD_FILE Same as ACL_READ_DATA. +ACL_APPEND_DATA Permission to create directories. Unused for files. +ACL_ADD_SUBDIRECTORY Same as ACL_APPEND_DATA. +ACL_READ_NAMED_ATTRS Unused. +ACL_WRITE_NAMED_ATTRS Unused. +ACL_EXECUTE Execute permission. +ACL_DELETE_CHILD Permission to delete files and subdirectories. +ACL_READ_ATTRIBUTES Permission to read basic attributes. +ACL_WRITE_ATTRIBUTES Permission to change basic attributes. +ACL_DELETE Permission to delete the object this ACL is placed on. +ACL_READ_ACL Permission to read ACL. +ACL_WRITE_ACL Permission to change the ACL and file mode. +ACL_SYNCHRONIZE Unused. +.Ed +.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 NFS4. .Sh RETURN VALUES .Rv -std acl_add_perm .Sh ERRORS @@ -65,11 +105,13 @@ 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 -urN 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 2008-11-21 11:03:43.000000000 +0100 @@ -0,0 +1,161 @@ +/*- + * 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. + * + * ALTHOUGH THIS SOFTWARE IS MADE OF WIN AND SCIENCE, IT 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 +#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, wheather it's NFS4, POSIX 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 gets branded as NFS4 if any of the + * NFS4 permissions that are not valid for POSIX 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 NFS4 in ACL branded as POSIX) 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 -urN current/lib/libc/posix1e/acl_calc_mask.c nfs4acl/lib/libc/posix1e/acl_calc_mask.c --- current/lib/libc/posix1e/acl_calc_mask.c 2008-11-25 21:03:52.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_calc_mask.c 2008-11-21 11:03:43.000000000 +0100 @@ -35,6 +35,8 @@ #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,13 @@ 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 -urN 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 2008-11-21 11:03:43.000000000 +0100 @@ -0,0 +1,79 @@ +.\"- +.\" Copyright (c) 2008 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 -urN current/lib/libc/posix1e/acl_compat.c nfs4acl/lib/libc/posix1e/acl_compat.c --- current/lib/libc/posix1e/acl_compat.c 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_compat.c 2008-11-21 11:03:43.000000000 +0100 @@ -0,0 +1,55 @@ +/*- + * 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. + * + * ALTHOUGH THIS SOFTWARE IS MADE OF WIN AND SCIENCE, IT 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 + +/* + * Compatibility wrappers for applications compiled against libc from before + * NFSv4 ACLs were added. + */ +int +__oldacl_get_perm_np(acl_permset_t permset_d, oldacl_perm_t perm) +{ + return (acl_get_perm_np(permset_d, perm)); +} + +int +__oldacl_add_perm(acl_permset_t permset_d, oldacl_perm_t perm) +{ + return (acl_add_perm(permset_d, perm)); +} + +int +__oldacl_delete_perm(acl_permset_t permset_d, oldacl_perm_t perm) +{ + return (acl_delete_perm(permset_d, perm)); +} + +__sym_compat(acl_get_perm_np, __oldacl_get_perm_np, FBSD_1.0); +__sym_compat(acl_add_perm, __oldacl_add_perm, FBSD_1.0); +__sym_compat(acl_delete_perm, __oldacl_delete_perm, FBSD_1.0); + diff -urN current/lib/libc/posix1e/acl_copy.c nfs4acl/lib/libc/posix1e/acl_copy.c --- current/lib/libc/posix1e/acl_copy.c 2008-11-25 21:03:52.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_copy.c 2008-11-21 11:03:44.000000000 +0100 @@ -35,6 +35,8 @@ #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 @@ 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_extended = src_d->ae_extended; + dest_d->ae_flags = src_d->ae_flags; return (0); } diff -urN current/lib/libc/posix1e/acl_create_entry.3 nfs4acl/lib/libc/posix1e/acl_create_entry.3 --- current/lib/libc/posix1e/acl_create_entry.3 2008-11-25 21:03:52.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_create_entry.3 2008-11-21 11:03:44.000000000 +0100 @@ -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 @@ 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 @@ 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 -urN current/lib/libc/posix1e/acl_delete.c nfs4acl/lib/libc/posix1e/acl_delete.c --- current/lib/libc/posix1e/acl_delete.c 2008-11-25 21:03:52.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_delete.c 2008-11-21 11:03:44.000000000 +0100 @@ -38,6 +38,8 @@ #include "un-namespace.h" #include +#include "acl_support.h" + int acl_delete_def_file(const char *path_p) { @@ -56,6 +58,8 @@ acl_delete_file_np(const char *path_p, acl_type_t type) { + type = _acl_type_unold(type); + return (__acl_delete_file(path_p, type)); } @@ -63,6 +67,8 @@ acl_delete_link_np(const char *path_p, acl_type_t type) { + type = _acl_type_unold(type); + return (__acl_delete_link(path_p, type)); } @@ -71,5 +77,7 @@ acl_delete_fd_np(int filedes, acl_type_t type) { + type = _acl_type_unold(type); + return (___acl_delete_fd(filedes, type)); } diff -urN current/lib/libc/posix1e/acl_delete_entry.3 nfs4acl/lib/libc/posix1e/acl_delete_entry.3 --- current/lib/libc/posix1e/acl_delete_entry.3 2008-11-25 21:03:52.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_delete_entry.3 2008-11-21 11:03:44.000000000 +0100 @@ -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 @@ .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 @@ .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 -urN current/lib/libc/posix1e/acl_delete_entry.c nfs4acl/lib/libc/posix1e/acl_delete_entry.c --- current/lib/libc/posix1e/acl_delete_entry.c 2008-11-25 21:03:52.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_delete_entry.c 2008-11-21 11:03:44.000000000 +0100 @@ -33,6 +33,39 @@ #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_extended != b->ae_extended) + 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 @@ 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_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 -urN 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 2008-11-21 11:03:44.000000000 +0100 @@ -0,0 +1,84 @@ +.\"- +.\" Copyright (c) 2008 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 -urN current/lib/libc/posix1e/acl_entry.c nfs4acl/lib/libc/posix1e/acl_entry.c --- current/lib/libc/posix1e/acl_entry.c 2008-11-25 21:03:52.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_entry.c 2008-11-21 11:03:44.000000000 +0100 @@ -51,7 +51,8 @@ acl_int = &(*acl_p)->ats_acl; - if ((acl_int->acl_cnt >= ACL_MAX_ENTRIES) || (acl_int->acl_cnt < 0)) { + /* XXX: There was a bug here. */ + if ((acl_int->acl_cnt + 1 >= ACL_MAX_ENTRIES) || (acl_int->acl_cnt < 0)) { errno = EINVAL; return (-1); } @@ -61,6 +62,48 @@ (**entry_p).ae_tag = ACL_UNDEFINED_TAG; (**entry_p).ae_id = ACL_UNDEFINED_ID; (**entry_p).ae_perm = ACL_PERM_NONE; + (**entry_p).ae_flags= 0; + + (*acl_p)->ats_cur_entry = 0; + + 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_flags= 0; (*acl_p)->ats_cur_entry = 0; diff -urN 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 2008-11-21 11:03:44.000000000 +0100 @@ -0,0 +1,146 @@ +/*- + * 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. + * + * ALTHOUGH THIS SOFTWARE IS MADE OF WIN AND SCIENCE, IT 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 +#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 -urN current/lib/libc/posix1e/acl_from_text.c nfs4acl/lib/libc/posix1e/acl_from_text.c --- current/lib/libc/posix1e/acl_from_text.c 2008-11-25 21:03:52.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_from_text.c 2008-11-21 11:03:44.000000000 +0100 @@ -40,45 +40,15 @@ #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 @@ } } +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_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 @@ /* 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 -urN 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 2008-11-21 11:03:44.000000000 +0100 @@ -0,0 +1,313 @@ +/*- + * 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. + * + * ALTHOUGH THIS SOFTWARE IS MADE OF WIN AND SCIENCE, IT 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 +#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, 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, flags; + + error = _nfs4_parse_flags(str, &flags); + if (error) + return (error); + + error = acl_set_flagset_np(entry, &flags); + + return (error); +} + +static int +parse_extended(const char *str, acl_entry_t entry) +{ + if (strcmp(str, "allow") == 0) + return (acl_set_extended_np(entry, ACL_EXTENDED_ALLOW)); + + if (strcmp(str, "deny") == 0) + return (acl_set_extended_np(entry, ACL_EXTENDED_DENY)); + + if (strcmp(str, "audit") == 0) + return (acl_set_extended_np(entry, ACL_EXTENDED_AUDIT)); + + if (strcmp(str, "alarm") == 0) + return (acl_set_extended_np(entry, ACL_EXTENDED_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_extended(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 -urN current/lib/libc/posix1e/acl_get.3 nfs4acl/lib/libc/posix1e/acl_get.3 --- current/lib/libc/posix1e/acl_get.3 2008-11-25 21:03:52.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_get.3 2008-11-21 11:03:44.000000000 +0100 @@ -91,6 +91,18 @@ .Va fd . The ACL in the working storage shall not participate in any access control decisions. +.Pp +Valid values for the +.Va type +argument are: +.Pp +.Bd -literal -offset indent -compact +ACL_TYPE_ACCESS POSIX.1e access ACL +ACL_TYPE_DEFAULT POSIX.1e default ACL +ACL_TYPE_NFS4 NFS4 ACL +.Ed +.Pp +The ACL returned will be branded accordingly. .Sh IMPLEMENTATION NOTES .Fx Ns 's support for POSIX.1e interfaces and features is still under @@ -137,6 +149,7 @@ .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 -urN current/lib/libc/posix1e/acl_get.c nfs4acl/lib/libc/posix1e/acl_get.c --- current/lib/libc/posix1e/acl_get.c 2008-11-25 21:03:52.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_get.c 2008-11-21 11:03:44.000000000 +0100 @@ -49,23 +49,32 @@ #include #include #include +#include + +#include "acl_support.h" acl_t acl_get_file(const char *path_p, acl_type_t type) { acl_t aclp; - int error; + int error, acl_length; aclp = acl_init(ACL_MAX_ENTRIES); if (aclp == NULL) return (NULL); + type = _acl_type_unold(type); + acl_length = aclp->ats_acl.acl_length; + error = __acl_get_file(path_p, type, &aclp->ats_acl); if (error) { acl_free(aclp); return (NULL); } + aclp->ats_acl.acl_length = acl_length; + _acl_brand_from_type(aclp, type); + return (aclp); } @@ -73,56 +82,58 @@ acl_get_link_np(const char *path_p, acl_type_t type) { acl_t aclp; - int error; + int error, acl_length; aclp = acl_init(ACL_MAX_ENTRIES); if (aclp == NULL) return (NULL); + type = _acl_type_unold(type); + acl_length = aclp->ats_acl.acl_length; + error = __acl_get_link(path_p, type, &aclp->ats_acl); if (error) { acl_free(aclp); return (NULL); } + aclp->ats_acl.acl_length = acl_length; + _acl_brand_from_type(aclp, type); + return (aclp); } acl_t acl_get_fd(int fd) { - acl_t aclp; - int error; + if (fpathconf(fd, _PC_EXTENDED_SECURITY_NP)) + return (acl_get_fd_np(fd, ACL_TYPE_NFS4)); - aclp = acl_init(ACL_MAX_ENTRIES); - if (aclp == NULL) - return (NULL); - - 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 acl_get_fd_np(int fd, acl_type_t type) { acl_t aclp; - int error; + int error, acl_length; aclp = acl_init(ACL_MAX_ENTRIES); if (aclp == NULL) return (NULL); + type = _acl_type_unold(type); + acl_length = aclp->ats_acl.acl_length; + error = ___acl_get_fd(fd, type, &aclp->ats_acl); if (error) { acl_free(aclp); return (NULL); } + aclp->ats_acl.acl_length = acl_length; + _acl_brand_from_type(aclp, type); + return (aclp); } @@ -213,3 +224,22 @@ return (0); } + +int +acl_get_extended_np(acl_entry_t entry_d, acl_extended_t *extended_p) +{ + if (entry_d == NULL || extended_p == NULL) { + errno = EINVAL; + return (-1); + } + + if (!_entry_brand_may_be(entry_d, ACL_BRAND_NFS4)) { + errno = EINVAL; + return (-1); + } + + *extended_p = entry_d->ae_extended; + + return (0); +} + diff -urN 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 2008-11-21 11:03:44.000000000 +0100 @@ -0,0 +1,86 @@ +.\"- +.\" Copyright (c) 2008 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 NFS4 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 +or NFS4 - "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 -urN current/lib/libc/posix1e/acl_get_extended_np.3 nfs4acl/lib/libc/posix1e/acl_get_extended_np.3 --- current/lib/libc/posix1e/acl_get_extended_np.3 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_get_extended_np.3 2008-11-21 11:03:44.000000000 +0100 @@ -0,0 +1,80 @@ +.\"- +.\" Copyright (c) 2008 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_EXTENDED_NP 3 +.Os +.Sh NAME +.Nm acl_get_extended_np +.Nd retrieve the ACL type from an NFS4 ACL entry +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In sys/types.h +.In sys/acl.h +.Ft int +.Fn acl_get_extended_np "acl_entry_t entry_d" "acl_extended_t *extended_p" +.Sh DESCRIPTION +The +.Fn acl_get_extended_np +function +is a non-portable call that returns the ACL type for the NFS4 ACL entry +.Fa entry_d . +Upon successful completion, the location referred to by the argument +.Fa extended_p +will be set to the ACL type of the ACL entry +.Fa entry_d . +.Sh RETURN VALUES +.Rv -std acl_get_extended_np +.Sh ERRORS +The +.Fn acl_get_extended_np +function fails if: +.Bl -tag -width Er +.It Bq Er EINVAL +Argument +.Fa entry_d +is not a valid descriptor for an NFS4 ACL entry; +.El +.Sh SEE ALSO +.Xr acl 3 , +.Xr acl_set_extended_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_extended_np +function was added in +.Fx 8.0 . +.Sh AUTHORS +The +.Fn acl_get_extended_np +function was written by +.An Edward Tomasz Napierala Aq trasz@FreeBSD.org . diff -urN 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 2008-11-21 11:03:44.000000000 +0100 @@ -0,0 +1,94 @@ +.\"- +.\" Copyright (c) 2008 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 -urN 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 2008-11-21 11:03:44.000000000 +0100 @@ -0,0 +1,83 @@ +.\"- +.\" Copyright (c) 2008 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 NFS4 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 -urN current/lib/libc/posix1e/acl_init.c nfs4acl/lib/libc/posix1e/acl_init.c --- current/lib/libc/posix1e/acl_init.c 2008-11-25 21:03:52.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_init.c 2008-11-21 11:03:44.000000000 +0100 @@ -38,10 +38,14 @@ #include #include #include +#include + +#include "acl_support.h" acl_t acl_init(int count) { + int error; acl_t acl; if (count > ACL_MAX_ENTRIES) { @@ -53,9 +57,17 @@ return (NULL); } - acl = malloc(sizeof(struct acl_t_struct)); - if (acl != NULL) - bzero(acl, sizeof(struct acl_t_struct)); + assert(1 << _ACL_T_ALIGNMENT_BITS > sizeof(struct acl_t_struct)); + 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_magic = ACL_MAGIC; + acl->ats_acl.acl_length = ACL_MAX_ENTRIES; return (acl); } diff -urN 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 2008-11-21 11:03:44.000000000 +0100 @@ -0,0 +1,84 @@ +.\"- +.\" Copyright (c) 2008 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 NFS4 ACLs, ACL is trivial if it has the "canonical six" entries. +File having non-trivial ACL have a plus sign appended after mode bits +in "ls -al" 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 -urN current/lib/libc/posix1e/acl_set.3 nfs4acl/lib/libc/posix1e/acl_set.3 --- current/lib/libc/posix1e/acl_set.3 2008-11-25 21:03:52.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_set.3 2008-11-21 11:03:44.000000000 +0100 @@ -75,6 +75,21 @@ .Fn acl_set_link_np function acts on a symlink rather than its target, if the target of the path is a symlink. +.Pp +Valid values for the +.Va type +argument are: +.Pp +.Bd -literal -offset indent -compact +ACL_TYPE_ACCESS POSIX.1e access ACL +ACL_TYPE_DEFAULT POSIX.1e default ACL +ACL_TYPE_NFS4 NFS4 ACL +.Ed +.Pp +Trying to set ACL_TYPE_NFS4 with +.Va acl +branded as POSIX, or ACL_TYPE_ACCESS or ACL_TYPE_DEFAULT with ACL +branded as NFS4, will result in error. .Sh IMPLEMENTATION NOTES .Fx Ns 's support for POSIX.1e interfaces and features is still under @@ -100,7 +115,7 @@ 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. @@ -123,6 +138,7 @@ .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 -urN current/lib/libc/posix1e/acl_set.c nfs4acl/lib/libc/posix1e/acl_set.c --- current/lib/libc/posix1e/acl_set.c 2008-11-25 21:03:52.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_set.c 2008-11-21 11:03:44.000000000 +0100 @@ -40,6 +40,7 @@ #include #include #include +#include #include "acl_support.h" @@ -58,6 +59,15 @@ errno = EINVAL; return (-1); } + + /* This is for old binaries using new libc. */ + 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) { @@ -80,6 +90,14 @@ 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) { @@ -96,17 +114,10 @@ int acl_set_fd(int fd, acl_t acl) { - int error; + if (fpathconf(fd, _PC_EXTENDED_SECURITY_NP)) + return (acl_set_fd_np(fd, acl, ACL_TYPE_NFS4)); - error = _posix1e_acl_sort(acl); - if (error) { - errno = error; - return(-1); - } - - acl->ats_cur_entry = 0; - - return (___acl_set_fd(fd, ACL_TYPE_ACCESS, &acl->ats_acl)); + return (acl_set_fd_np(fd, acl, ACL_TYPE_ACCESS)); } int @@ -114,6 +125,18 @@ { 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) { @@ -140,6 +163,21 @@ 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); @@ -184,12 +222,34 @@ } 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); } @@ -197,3 +257,33 @@ errno = EINVAL; return (-1); } + +int +acl_set_extended_np(acl_entry_t entry_d, acl_extended_t extended) +{ + 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 (extended) { + case ACL_EXTENDED_ALLOW: + case ACL_EXTENDED_DENY: + case ACL_EXTENDED_AUDIT: + case ACL_EXTENDED_ALARM: + + entry_d->ae_extended = extended; + return (0); + } + + errno = EINVAL; + return (-1); +} + diff -urN current/lib/libc/posix1e/acl_set_extended_np.3 nfs4acl/lib/libc/posix1e/acl_set_extended_np.3 --- current/lib/libc/posix1e/acl_set_extended_np.3 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_set_extended_np.3 2008-11-21 11:03:44.000000000 +0100 @@ -0,0 +1,95 @@ +.\"- +.\" Copyright (c) 2008 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_EXTENDED_NP 3 +.Os +.Sh NAME +.Nm acl_set_extended_np +.Nd set NFS4 ACL entry type +.Sh LIBRARY +.Lb libc +.Sh SYNOPSIS +.In sys/types.h +.In sys/acl.h +.Ft int +.Fn acl_set_extended_np "acl_entry_t entry_d" "acl_extended_t extended" +.Sh DESCRIPTION +The +.Fn acl_set_extended_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 extended . +.Pp +Valid values are: +.Pp +.Bd -literal -offset indent -compact +ACL_EXTENDED_ALLOW "allow" type entry +ACL_EXTENDED_DENY "deny" type entry +.Ed +.Pp +This call brands the ACL as NFS4. +.Sh RETURN VALUES +.Rv -std acl_set_extended_np +.Sh ERRORS +The +.Fn acl_set_extended_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 extended +is not valid. +ACL is already branded as POSIX. +.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_extended_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_extended_np +function was added in +.Fx 8.0 . +.Sh AUTHORS +The +.Fn acl_get_extended_np +function was written by +.An Edward Tomasz Napierala Aq trasz@FreeBSD.org . diff -urN 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 2008-11-21 11:03:44.000000000 +0100 @@ -0,0 +1,85 @@ +.\"- +.\" Copyright (c) 2008 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 NFS4 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 NFS4. +.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. +.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 -urN current/lib/libc/posix1e/acl_set_qualifier.3 nfs4acl/lib/libc/posix1e/acl_set_qualifier.3 --- current/lib/libc/posix1e/acl_set_qualifier.3 2008-11-25 21:03:52.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_set_qualifier.3 2008-11-21 11:03:44.000000000 +0100 @@ -42,7 +42,7 @@ The .Fn acl_set_qualifier function -is a POSIX.1e call that sets the qualifier of the tag for the ACl entry +is a POSIX.1e call that sets the qualifier of the tag for the ACL entry .Fa entry_d to the value referred to by .Fa tag_qualifier_p . diff -urN 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 2008-11-25 21:03:52.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_set_tag_type.3 2008-11-21 11:03:44.000000000 +0100 @@ -46,6 +46,26 @@ .Fa entry_d to the value of .Fa tag_type . +.Pp +Valid values are: +.Pp +.Bd -literal -offset indent -compact +ACL_USER_OBJ Permissions apply to file owner. +ACL_USER Permissions apply to user specified by qualifier. +ACL_GROUP_OBJ Permissions apply to file owning group. +ACL_GROUP Permissions apply to group specified by qualifier. +ACL_MASK Permissions specify mask. +ACL_OTHER Permissions apply to "other". +ACL_OTHER_OBJ Same as ACL_OTHER +ACL_EVERYONE Permissions apply to "everyone@". +.Ed +.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. +Calling it with ACL_EVERYONE brands the ACL as NFS4. .Sh RETURN VALUES .Rv -std acl_set_tag_type .Sh ERRORS @@ -60,9 +80,11 @@ 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 -urN 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 2008-11-21 11:03:44.000000000 +0100 @@ -0,0 +1,198 @@ +/*- + * 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 +#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 -urN 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 2008-11-21 11:03:44.000000000 +0100 @@ -0,0 +1,109 @@ +.\"- +.\" Copyright (c) 2008 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 -urN current/lib/libc/posix1e/acl_support.c nfs4acl/lib/libc/posix1e/acl_support.c --- current/lib/libc/posix1e/acl_support.c 2008-11-25 21:03:52.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_support.c 2008-11-21 11:03:44.000000000 +0100 @@ -40,6 +40,8 @@ #include #include #include +#include +#include #include "acl_support.h" @@ -49,6 +51,55 @@ #define ACL_STRING_PERM_NONE '-' /* + * Convert "old" type - ACL_TYPE_{ACCESS,DEFAULT}_OLD - into its "new" + * counterpart. It's neccessary for the old (pre-NFS4 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) +{ + if (type == ACL_TYPE_ACCESS_OLD) + return (ACL_TYPE_ACCESS); + + if (type == ACL_TYPE_DEFAULT_OLD) + return (ACL_TYPE_DEFAULT); + + return (type); +} + +/* + * 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_extended != entryb->ae_extended || + 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 +110,9 @@ 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. @@ -115,6 +169,8 @@ 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 +299,8 @@ * 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 +308,10 @@ 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 +324,10 @@ 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 @@ -376,3 +439,37 @@ return (0); } + +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 -urN current/lib/libc/posix1e/acl_support.h nfs4acl/lib/libc/posix1e/acl_support.h --- current/lib/libc/posix1e/acl_support.h 2008-11-25 21:03:52.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_support.h 2008-11-21 11:03:44.000000000 +0100 @@ -33,16 +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, uint32_t var, int verbose); +int _nfs4_format_access_mask(char *str, size_t size, uint32_t var, int verbose); +int _nfs4_parse_flags(const char *str, uint32_t *var); +int _nfs4_parse_access_mask(const char *str, uint32_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 -urN 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 2008-11-21 11:03:44.000000000 +0100 @@ -0,0 +1,246 @@ +/*- + * 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. + * + * ALTHOUGH THIS SOFTWARE IS MADE OF WIN AND SCIENCE, IT 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 +#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_ONLY_INHERIT, "inherit_only", 'i'}, + { ACL_ENTRY_LIMIT_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_EXTATTRIBUTES, "read_xattr", 'R'}, + { ACL_WRITE_EXTATTRIBUTES, "write_xattr", 'W'}, + { ACL_READ_SECURITY, "read_acl", 'c'}, + { ACL_WRITE_SECURITY, "write_acl", 'C'}, + { ACL_CHANGE_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, uint32_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, uint32_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, uint32_t *var) +{ + int error, try_compact; + + error = parse_flags_verbose(str, var, a_flags, "flags", &try_compact); + if (error && try_compact) + error = parse_flags_compact(str, var, a_flags, "flags"); + + return (error); +} + +int +_nfs4_parse_access_mask(const char *str, uint32_t *var) +{ + int error, try_compact; + + error = parse_flags_verbose(str, var, a_access_masks, + "access permissions", &try_compact); + if (error && try_compact) + error = parse_flags_compact(str, var, + a_access_masks, "access permissions"); + + return (error); +} + diff -urN current/lib/libc/posix1e/acl_to_text.3 nfs4acl/lib/libc/posix1e/acl_to_text.3 --- current/lib/libc/posix1e/acl_to_text.3 2008-11-25 21:03:52.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_to_text.3 2008-11-21 11:03:44.000000000 +0100 @@ -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 @@ 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, 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 NFS4, 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 +.Bd -literal -offset indent -compact +ACL_TEXT_VERBOSE format ACL using verbose form +ACL_TEXT_NUMERIC_IDS do not resolve IDs into user or group names +ACL_TEXT_APPEND_ID in addition to user and group names, append numeric IDs +.Ed .Pp This function allocates any memory necessary to contain the string and returns a pointer to the string. diff -urN current/lib/libc/posix1e/acl_to_text.c nfs4acl/lib/libc/posix1e/acl_to_text.c --- current/lib/libc/posix1e/acl_to_text.c 2008-11-25 21:03:52.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_to_text.c 2008-11-21 11:03:44.000000000 +0100 @@ -50,8 +50,11 @@ * 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 @@ 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 @@ 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,26 @@ 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 -urN 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 2008-11-21 11:03:44.000000000 +0100 @@ -0,0 +1,269 @@ +/*- + * 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. + * + * ALTHOUGH THIS SOFTWARE IS MADE OF WIN AND SCIENCE, IT 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 +#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_extended(char *str, size_t size, const acl_entry_t entry) +{ + int error; + acl_extended_t extended; + + error = acl_get_extended_np(entry, &extended); + if (error) + return (error); + + switch (extended) { + case ACL_EXTENDED_ALLOW: + snprintf(str, size, "allow"); + break; + + case ACL_EXTENDED_DENY: + snprintf(str, size, "deny"); + break; + + case ACL_EXTENDED_AUDIT: + snprintf(str, size, "audit"); + break; + + case ACL_EXTENDED_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_extended(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 -urN current/lib/libc/posix1e/acl_valid.c nfs4acl/lib/libc/posix1e/acl_valid.c --- current/lib/libc/posix1e/acl_valid.c 2008-11-25 21:03:52.000000000 +0100 +++ nfs4acl/lib/libc/posix1e/acl_valid.c 2008-11-21 11:03:44.000000000 +0100 @@ -62,6 +62,12 @@ 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) { @@ -81,6 +87,9 @@ errno = EINVAL; return (-1); } + + type = _acl_type_unold(type); + if (_posix1e_acl(acl, type)) { error = _posix1e_acl_sort(acl); if (error) { @@ -101,6 +110,9 @@ errno = EINVAL; return (-1); } + + type = _acl_type_unold(type); + if (_posix1e_acl(acl, type)) { error = _posix1e_acl_sort(acl); if (error) { @@ -121,6 +133,9 @@ errno = EINVAL; return (-1); } + + type = _acl_type_unold(type); + if (_posix1e_acl(acl, type)) { error = _posix1e_acl_sort(acl); if (error) { diff -urN current/sbin/mount/mntopts.h nfs4acl/sbin/mount/mntopts.h --- current/sbin/mount/mntopts.h 2008-11-25 21:07:19.000000000 +0100 +++ nfs4acl/sbin/mount/mntopts.h 2008-11-21 11:04:55.000000000 +0100 @@ -54,6 +54,7 @@ #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 @@ 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 -urN current/sbin/mount/mount.8 nfs4acl/sbin/mount/mount.8 --- current/sbin/mount/mount.8 2008-11-25 21:07:19.000000000 +0100 +++ nfs4acl/sbin/mount/mount.8 2008-11-21 11:04:55.000000000 +0100 @@ -120,11 +120,14 @@ 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 @@ -177,6 +180,15 @@ .Xr mac 4 for more information, which cause the multilabel mount flag to be set automatically at mount-time. +.It Cm nfs4acls +Enable NFS4 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 -urN current/sbin/mount/mount.c nfs4acl/sbin/mount/mount.c --- current/sbin/mount/mount.c 2008-11-25 21:07:19.000000000 +0100 +++ nfs4acl/sbin/mount/mount.c 2008-11-21 11:04:55.000000000 +0100 @@ -107,6 +107,7 @@ { MNT_SOFTDEP, "soft-updates" }, { MNT_MULTILABEL, "multilabel" }, { MNT_ACLS, "acls" }, + { MNT_NFS4ACLS, "nfs4acls" }, { MNT_GJOURNAL, "gjournal" }, { 0, NULL } }; @@ -877,6 +878,7 @@ 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 -urN current/share/man/man9/Makefile nfs4acl/share/man/man9/Makefile --- current/share/man/man9/Makefile 2008-11-25 21:09:17.000000000 +0100 +++ nfs4acl/share/man/man9/Makefile 2008-11-26 09:52:37.000000000 +0100 @@ -241,6 +241,7 @@ usbdi.9 \ utopia.9 \ vaccess.9 \ + vaccess_acl_nfs4.9 \ vaccess_acl_posix1e.9 \ vcount.9 \ vflush.9 \ diff -urN current/share/man/man9/VOP_ACCESS.9 nfs4acl/share/man/man9/VOP_ACCESS.9 --- current/share/man/man9/VOP_ACCESS.9 2008-11-25 21:09:18.000000000 +0100 +++ nfs4acl/share/man/man9/VOP_ACCESS.9 2008-11-26 13:18:12.000000000 +0100 @@ -112,6 +112,7 @@ .El .Sh SEE ALSO .Xr vaccess 9 , +.Xr vaccess_acl_nfs4 9 , .Xr vaccess_acl_posix1e 9 , .Xr vnode 9 .Sh AUTHORS diff -urN current/share/man/man9/acl.9 nfs4acl/share/man/man9/acl.9 --- current/share/man/man9/acl.9 2008-11-25 21:09:19.000000000 +0100 +++ nfs4acl/share/man/man9/acl.9 2008-11-21 11:05:35.000000000 +0100 @@ -97,13 +97,20 @@ .It Dv ACL_MASK The maximum discretionary access rights that can be granted to a process in the file group class. +This is only valid for POSIX.1e ACLs. .It Dv ACL_OTHER Discretionary access rights for processes not covered by any other ACL entry. +This is only valid for POSIX.1e ACLs. .It Dv ACL_OTHER_OBJ Same as .Dv ACL_OTHER . -Each ACL entry must contain exactly one +.It Dv ACL_EVERYONE +Discretionary access rights for all users. +This is only valid for NFS4 ACLs. +.El +.Pp +Each POSIX.1e ACL must contain exactly one .Dv ACL_USER_OBJ , one .Dv ACL_GROUP_OBJ , @@ -117,13 +124,13 @@ are present, then exactly one .Dv ACL_MASK entry should be present. -.El .It Vt uid_t Va ae_id The ID of user for whom this ACL describes access permissions. .It Vt acl_perm_t Va ae_perm This field defines what kind of access the process matching this ACL has for accessing the associated file. -.Bl -tag -width ".Dv ACL_POSIX1E_BITS" +For POSIX.1e ACLs, the following are valid: +.Bl -tag -width ".Dv ACL_WRITE_NAMED_ATTRS" .It Dv ACL_EXECUTE The process may execute the associated file. .It Dv ACL_WRITE @@ -134,70 +141,56 @@ The process has no read, write or execute permissions to the associated file. .El +.Pp +For NFS4 ACLs, the following are valid: +.Bl -tag -width ".Dv ACL_WRITE_NAMED_ATTRS" +.It Dv ACL_READ_DATA +.It Dv ACL_LIST_DIRECTORY +Same as +.Dv ACL_READ_DATA . +.It Dv ACL_WRITE_DATA +.It Dv ACL_ADD_FILE +Same as +.Dv ACL_ACL_WRITE_DATA . +.It Dv ACL_APPEND_DATA +.It Dv ACL_ADD_SUBDIRECTORY +Same as +.Dv ACL_APPEND_DATA . +.It Dv ACL_READ_NAMED_ATTRS +.It Dv ACL_WRITE_NAMED_ATTRS +.It Dv ACL_EXECUTE +.It Dv ACL_DELETE_CHILD +.It Dv ACL_READ_ATTRIBUTES +.It Dv ACL_WRITE_ATTRIBUTES +.It Dv ACL_DELETE +.It Dv ACL_READ_ACL +.It Dv ACL_WRITE_ACL +.It Dv ACL_WRITE_OWNER +.It Dv ACL_SYNCHRONIZE +.El +.It Vt acl_extended_t Va ae_extended +This field defines the type of NFS4 ACL entry. +It is not used with POSIX.1e ACLs. +The following values are valid: +.Bl -tag -width ".Dv ACL_WRITE_NAMED_ATTRS" +.It Dv ACL_EXTENDED_ALLOW +.It Dv ACL_EXTENDED_DENY +.El +.It Vt acl_flag_t Va ae_flags +This field defines the inheritance flags of NFS4 ACL entry. +It is not used with POSIX.1e ACLs. +The following values are valid: +.Bl -tag -width ".Dv ACL_ENTRY_DIRECTORY_INHERIT" +.It Dv ACL_ENTRY_FILE_INHERIT +.It Dv ACL_ENTRY_DIRECTORY_INHERIT +.It Dv ACL_ENTRY_LIMIT_INHERIT +.It Dv ACL_ENTRY_ONLY_INHERIT +.El .El -.Sh IMPLEMENTATION NOTES -.Bd -literal -typedef mode_t *acl_permset_t; - -/* internal ACL structure */ -struct acl { - int acl_cnt; - struct acl_entry acl_entry[ACL_MAX_ENTRIES]; -}; - -/* external ACL structure */ -struct acl_t_struct { - struct acl ats_acl; - int ats_cur_entry; -}; -typedef struct acl_t_struct *acl_t; - -/* - * Possible valid values for ae_tag field. - */ -#define ACL_UNDEFINED_TAG 0x00000000 -#define ACL_USER_OBJ 0x00000001 -#define ACL_USER 0x00000002 -#define ACL_GROUP_OBJ 0x00000004 -#define ACL_GROUP 0x00000008 -#define ACL_MASK 0x00000010 -#define ACL_OTHER 0x00000020 -#define ACL_OTHER_OBJ ACL_OTHER - -/* - * Possible valid values for acl_type_t arguments. - */ -#define ACL_TYPE_ACCESS 0x00000000 -#define ACL_TYPE_DEFAULT 0x00000001 -#define ACL_TYPE_AFS 0x00000002 -#define ACL_TYPE_CODA 0x00000003 -#define ACL_TYPE_NTFS 0x00000004 -#define ACL_TYPE_NWFS 0x00000005 - -/* - * Possible flags in ae_perm field. - */ -#define ACL_EXECUTE 0x0001 -#define ACL_WRITE 0x0002 -#define ACL_READ 0x0004 -#define ACL_PERM_NONE 0x0000 -#define ACL_PERM_BITS (ACL_EXECUTE | ACL_WRITE | ACL_READ) -#define ACL_POSIX1E_BITS (ACL_EXECUTE | ACL_WRITE | ACL_READ) - -/* - * Possible entry_id values for acl_get_entry() - */ -#define ACL_FIRST_ENTRY 0 -#define ACL_NEXT_ENTRY 1 - -/* - * Undefined value in ae_id field - */ -#define ACL_UNDEFINED_ID ((uid_t)-1) -.Ed .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 -urN 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 2008-11-21 11:05:38.000000000 +0100 @@ -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 NFS4 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 NFS4 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 NFS4 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 NFS4 ACL +evaluation algorithm, or cross reference another page that does. diff -urN current/sys/cddl/compat/opensolaris/kern/opensolaris_policy.c nfs4acl/sys/cddl/compat/opensolaris/kern/opensolaris_policy.c --- current/sys/cddl/compat/opensolaris/kern/opensolaris_policy.c 2008-11-25 21:10:53.000000000 +0100 +++ nfs4acl/sys/cddl/compat/opensolaris/kern/opensolaris_policy.c 2008-11-26 09:52:40.000000000 +0100 @@ -302,6 +302,14 @@ if (error) return (error); } + /* + * Deny setting setuid if we are not the file owner. + */ + if ((vap->va_mode & S_ISUID) && ovap->va_uid != cred->cr_uid) { + error = priv_check_cred(cred, PRIV_VFS_ADMIN, 0); + if (error) + return (error); + } return (0); } diff -urN current/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/acl_compat.c nfs4acl/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/acl_compat.c --- current/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/acl_compat.c 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/acl_compat.c 2008-11-21 11:06:13.000000000 +0100 @@ -0,0 +1,254 @@ +/*- + * 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. + * + * ALTHOUGH THIS SOFTWARE IS MADE OF WIN AND SCIENCE, IT 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. + * + */ + +#define acl_t _zfs_acl_t +#define acl_type_t _zfs_acl_type_t + +/* XXX: Somewhere in these includes must be bzero(9). */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* XXX: Most of these isn't needed either. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef acl_t +#undef acl_type_t + +/* + * Pull FreeBSD sys/acl.h, not the ZFS one. + */ +#include "../../../../../../../sys/acl.h" + +#include "acl_compat.h" + +struct artwo { + uint32_t sun; + int bsd; +}; + +struct artwo perms[] = {{ACE_READ_DATA, ACL_READ_DATA}, + {ACE_WRITE_DATA, ACL_WRITE_DATA}, + {ACE_EXECUTE, ACL_EXECUTE}, + {ACE_APPEND_DATA, ACL_APPEND_DATA}, + {ACE_DELETE_CHILD, ACL_DELETE_CHILD}, + {ACE_DELETE, ACL_DELETE}, + {ACE_READ_ATTRIBUTES, ACL_READ_ATTRIBUTES}, + {ACE_WRITE_ATTRIBUTES, ACL_WRITE_ATTRIBUTES}, + {ACE_READ_NAMED_ATTRS, ACL_READ_NAMED_ATTRS}, + {ACE_WRITE_NAMED_ATTRS, ACL_WRITE_NAMED_ATTRS}, + {ACE_READ_ACL, ACL_READ_ACL}, + {ACE_WRITE_ACL, ACL_WRITE_ACL}, + {ACE_WRITE_OWNER, ACL_WRITE_OWNER}, + {ACE_SYNCHRONIZE, ACL_SYNCHRONIZE}, + {0, 0}}; + +struct artwo flags[] = {{ACE_FILE_INHERIT_ACE, ACL_ENTRY_FILE_INHERIT}, + {ACE_DIRECTORY_INHERIT_ACE, ACL_ENTRY_DIRECTORY_INHERIT}, + {ACE_NO_PROPAGATE_INHERIT_ACE, ACL_ENTRY_LIMIT_INHERIT}, + {ACE_INHERIT_ONLY_ACE, ACL_ENTRY_ONLY_INHERIT}, + {ACE_SUCCESSFUL_ACCESS_ACE_FLAG, ACL_ENTRY_SUCCESSFUL_ACCESS}, + {ACE_FAILED_ACCESS_ACE_FLAG, ACL_ENTRY_FAILED_ACCESS}, + {0, 0}}; + +static void +_bsd_from_sun(int *bsd, uint32_t sun, const struct artwo *table) +{ + const struct artwo *tmp; + + for (tmp = table; tmp->sun != 0; tmp++) { + if (sun & tmp->sun) + *bsd |= tmp->bsd; + } +} + +static void +_sun_from_bsd(uint32_t *sun, int bsd, const struct artwo *table) +{ + const struct artwo *tmp; + + for (tmp = table; tmp->bsd != 0; tmp++) { + if (bsd & tmp->bsd) + *sun |= tmp->sun; + } +} + +int +acl_from_aces(struct acl *aclp, const ace_t *aces, int nentries) +{ + int i; + struct acl_entry *entry; + const ace_t *ace; + + bzero(aclp, sizeof(*aclp)); + + aclp->acl_cnt = nentries; + aclp->acl_magic = ACL_MAGIC; + + for (i = 0; i < nentries; i++) { + entry = &(aclp->acl_entry[i]); + ace = &(aces[i]); + + if (ace->a_flags & ACE_OWNER) + entry->ae_tag = ACL_USER_OBJ; + else if (ace->a_flags & ACE_GROUP) + entry->ae_tag = ACL_GROUP_OBJ; + else if (ace->a_flags & ACE_EVERYONE) + entry->ae_tag = ACL_EVERYONE; + else if (ace->a_flags & ACE_IDENTIFIER_GROUP) + entry->ae_tag = ACL_GROUP; + else + entry->ae_tag = ACL_USER; + + if (entry->ae_tag == ACL_USER || entry->ae_tag == ACL_GROUP) + entry->ae_id = ace->a_who; + else + entry->ae_id = ACL_UNDEFINED_ID; + + _bsd_from_sun(&(entry->ae_perm), ace->a_access_mask, perms); + _bsd_from_sun(&(entry->ae_flags), ace->a_flags, flags); + + switch (ace->a_type) { + case ACE_ACCESS_ALLOWED_ACE_TYPE: + entry->ae_extended = ACL_EXTENDED_ALLOW; + break; + case ACE_ACCESS_DENIED_ACE_TYPE: + entry->ae_extended = ACL_EXTENDED_DENY; + break; + case ACE_SYSTEM_AUDIT_ACE_TYPE: + entry->ae_extended = ACL_EXTENDED_AUDIT; + break; + case ACE_SYSTEM_ALARM_ACE_TYPE: + entry->ae_extended = ACL_EXTENDED_ALARM; + break; + default: + printf("_acl_from_aces: a_type is 0x%x; panic?\n", ace->a_type); + return (EINVAL); + } + } + + return (0); +} + +int +aces_from_acl(ace_t *aces, int *nentries, const struct acl *aclp) +{ + int i; + const struct acl_entry *entry; + ace_t *ace; + uint32_t tmpflags = 0; + + bzero(aces, sizeof(*aces) * aclp->acl_cnt); + + *nentries = aclp->acl_cnt; + + for (i = 0; i < aclp->acl_cnt; i++) { + entry = &(aclp->acl_entry[i]); + ace = &(aces[i]); + + ace->a_who = entry->ae_id; + + if (entry->ae_tag == ACL_USER_OBJ) + tmpflags = ACE_OWNER; + else if (entry->ae_tag == ACL_GROUP_OBJ) + tmpflags = (ACE_GROUP | ACE_IDENTIFIER_GROUP); + else if (entry->ae_tag == ACL_GROUP) + tmpflags = ACE_IDENTIFIER_GROUP; + else if (entry->ae_tag == ACL_EVERYONE) + tmpflags = ACE_EVERYONE; + else /* ACL_USER */ + tmpflags = 0; + + _sun_from_bsd(&(ace->a_access_mask), entry->ae_perm, perms); + _sun_from_bsd(&tmpflags, entry->ae_flags, flags); + + ace->a_flags = tmpflags; + + switch (entry->ae_extended) { + case ACL_EXTENDED_ALLOW: + ace->a_type = ACE_ACCESS_ALLOWED_ACE_TYPE; + break; + case ACL_EXTENDED_DENY: + ace->a_type = ACE_ACCESS_DENIED_ACE_TYPE; + break; + case ACL_EXTENDED_ALARM: + ace->a_type = ACE_SYSTEM_ALARM_ACE_TYPE; + break; + case ACL_EXTENDED_AUDIT: + ace->a_type = ACE_SYSTEM_AUDIT_ACE_TYPE; + break; + default: + printf("_aces_from_acl: ae_extended is 0x%x; panic?\n", entry->ae_extended); + return (EINVAL); + } + } + + return (0); +} + diff -urN current/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/acl_compat.h nfs4acl/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/acl_compat.h --- current/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/acl_compat.h 1970-01-01 01:00:00.000000000 +0100 +++ nfs4acl/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/acl_compat.h 2008-11-21 11:06:13.000000000 +0100 @@ -0,0 +1,35 @@ +/*- + * 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. + * + * ALTHOUGH THIS SOFTWARE IS MADE OF WIN AND SCIENCE, IT 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. + * + */ + +#ifndef ACL_COMPAT_H +#define ACL_COMPAT_H + +int aces_from_acl(ace_t *aces, int *nentries, const struct acl *aclp); +int acl_from_aces(struct acl *aclp, const ace_t *aces, int nentries); + +#endif /* ACL_COMPAT_H */ + diff -urN current/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_acl.h nfs4acl/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_acl.h --- current/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_acl.h 2008-11-25 21:11:27.000000000 +0100 +++ nfs4acl/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_acl.h 2008-11-21 11:06:15.000000000 +0100 @@ -188,10 +188,8 @@ #ifdef _KERNEL void zfs_perm_init(struct znode *, struct znode *, int, vattr_t *, dmu_tx_t *, cred_t *, zfs_acl_t *, zfs_fuid_info_t **); -#ifdef TODO int zfs_getacl(struct znode *, vsecattr_t *, boolean_t, cred_t *); int zfs_setacl(struct znode *, vsecattr_t *, boolean_t, cred_t *); -#endif void zfs_acl_rele(void *); void zfs_oldace_byteswap(ace_t *, int); void zfs_ace_byteswap(void *, size_t, boolean_t); diff -urN current/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_acl.c nfs4acl/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_acl.c --- current/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_acl.c 2008-11-25 21:11:30.000000000 +0100 +++ nfs4acl/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_acl.c 2008-11-21 11:06:16.000000000 +0100 @@ -1909,7 +1909,6 @@ zfs_acl_free(aclp); } -#ifdef TODO /* * Retrieve a files ACL */ @@ -2005,7 +2004,6 @@ return (0); } -#endif /* TODO */ int zfs_vsec_2_aclp(zfsvfs_t *zfsvfs, vtype_t obj_type, @@ -2062,7 +2060,6 @@ return (0); } -#ifdef TODO /* * Set a files ACL */ @@ -2170,7 +2167,6 @@ return (error); } -#endif /* TODO */ /* * working_mode returns the permissions that were not granted diff -urN 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 2008-11-25 21:11:33.000000000 +0100 +++ nfs4acl/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c 2008-11-26 18:28:24.000000000 +0100 @@ -25,6 +25,9 @@ /* Portions Copyright 2007 Jeremy Teo */ +#define acl_t _zfs_acl_t +#define acl_type_t _zfs_acl_type_t + #include #include #include @@ -45,7 +48,6 @@ #include #include #include -#include #include #include #include @@ -68,6 +70,16 @@ #include #include +#undef acl_t +#undef acl_type_t + +/* + * Pull FreeBSD sys/acl.h, not the ZFS one. + */ +#include "../../../../../../../sys/acl.h" + +#include "acl_compat.h" + /* * Programming rules. * @@ -3827,12 +3839,15 @@ *valp = (int)SPA_MINBLOCKSIZE; return (0); + case _PC_EXTENDED_SECURITY_NP: + *valp = 1; + return (0); + default: return (EOPNOTSUPP); } } -#ifdef TODO /*ARGSUSED*/ static int zfs_getsecattr(vnode_t *vp, vsecattr_t *vsecp, int flag, cred_t *cr, @@ -3850,9 +3865,7 @@ return (error); } -#endif /* TODO */ -#ifdef TODO /*ARGSUSED*/ static int zfs_setsecattr(vnode_t *vp, vsecattr_t *vsecp, int flag, cred_t *cr, @@ -3869,7 +3882,6 @@ ZFS_EXIT(zfsvfs); return (error); } -#endif /* TODO */ static int zfs_freebsd_open(ap) @@ -3949,26 +3961,31 @@ zfs_freebsd_access(ap) struct vop_access_args /* { struct vnode *a_vp; - int a_accmode; + accmode_t a_accmode; struct ucred *a_cred; struct thread *a_td; } */ *ap; { + int error; + accmode_t accmode = ap->a_accmode; + + if (unixify_vaccess(&accmode, &error)) + return (error); /* * ZFS itself only knowns about VREAD, VWRITE and VEXEC, the rest * we have to handle by calling vaccess(). */ - if ((ap->a_accmode & ~(VREAD|VWRITE|VEXEC)) != 0) { + if ((accmode & ~(VREAD|VWRITE|VEXEC)) != 0) { vnode_t *vp = ap->a_vp; znode_t *zp = VTOZ(vp); znode_phys_t *zphys = zp->z_phys; return (vaccess(vp->v_type, zphys->zp_mode, zphys->zp_uid, - zphys->zp_gid, ap->a_accmode, ap->a_cred, NULL)); + zphys->zp_gid, accmode, ap->a_cred, NULL)); } - return (zfs_access(ap->a_vp, ap->a_accmode, 0, ap->a_cred, NULL)); + return (zfs_access(ap->a_vp, accmode, 0, ap->a_cred, NULL)); } static int @@ -4765,6 +4782,111 @@ return (error); } +int +zfs_freebsd_getacl(ap) + struct vop_getacl_args /* { + struct vnode *vp; + acl_type_t type; + struct acl *aclp; + struct ucred *cred; + struct thread *td; + } */ *ap; +{ + int error; + vsecattr_t vsecattr; + int aclbsize; /* size of acl list in bytes */ + + if (ap->a_type != ACL_TYPE_NFS4) + return (EOPNOTSUPP); + + /* Mostly taken from common/syscall/acl.c. */ + + vsecattr.vsa_mask = VSA_ACE | VSA_ACECNT; + if (error = zfs_getsecattr(ap->a_vp, &vsecattr, 0, ap->a_cred, NULL)) + return (error); + + aclbsize = vsecattr.vsa_aclcnt * sizeof (ace_t); + if (vsecattr.vsa_aclcnt > ACL_MAX_ENTRIES) { + printf("GETACL: no space\n"); + error = ENOSPC; + goto errout; + } + + error = acl_from_aces(ap->a_aclp, vsecattr.vsa_aclentp, vsecattr.vsa_aclcnt); + if (error != 0) + goto errout; + + if (vsecattr.vsa_aclcnt != 0) + kmem_free(vsecattr.vsa_aclentp, vsecattr.vsa_aclentsz); + + return (0); + +errout: + if (aclbsize != 0 && vsecattr.vsa_aclentp != NULL) + kmem_free(vsecattr.vsa_aclentp, aclbsize); + return (error); +} + +int +zfs_freebsd_setacl(ap) + struct vop_setacl_args /* { + struct vnode *vp; + acl_type_t type; + struct acl *aclp; + struct ucred *cred; + struct thread *td; + } */ *ap; +{ + int error; + vsecattr_t vsecattr; + int aclbsize; /* size of acl list in bytes */ + aclent_t *aaclp; + + if (ap->a_type != ACL_TYPE_NFS4) + return (EOPNOTSUPP); + + if (ap->a_aclp->acl_cnt < 1 || ap->a_aclp->acl_cnt > MAX_ACL_ENTRIES) + return (EINVAL); + + vsecattr.vsa_mask = VSA_ACE; + aclbsize = ap->a_aclp->acl_cnt * sizeof(ace_t); + vsecattr.vsa_aclentp = kmem_alloc(aclbsize, KM_SLEEP); + aaclp = vsecattr.vsa_aclentp; + vsecattr.vsa_aclentsz = aclbsize; + + error = aces_from_acl(vsecattr.vsa_aclentp, &vsecattr.vsa_aclcnt, ap->a_aclp); + if (error != 0) { + kmem_free(aaclp, aclbsize); + return (EINVAL); + } + + if (error = zfs_setsecattr(ap->a_vp, &vsecattr, 0, ap->a_cred, NULL)) { + kmem_free(aaclp, aclbsize); + return (error); + } + + kmem_free(aaclp, aclbsize); + return (0); +} + +int +zfs_freebsd_aclcheck(ap) + struct vop_aclcheck_args /* { + struct vnode *vp; + acl_type_t type; + struct acl *aclp; + struct ucred *cred; + struct thread *td; + } */ *ap; +{ + if (ap->a_type != ACL_TYPE_NFS4) + return (EOPNOTSUPP); + + printf("ACLCHECK\n"); + + return (EOPNOTSUPP); +} + struct vop_vector zfs_vnodeops; struct vop_vector zfs_fifoops; @@ -4804,6 +4926,9 @@ .vop_deleteextattr = zfs_deleteextattr, .vop_setextattr = zfs_setextattr, .vop_listextattr = zfs_listextattr, + .vop_getacl = zfs_freebsd_getacl, + .vop_setacl = zfs_freebsd_setacl, + .vop_aclcheck = zfs_freebsd_aclcheck, }; struct vop_vector zfs_fifoops = { @@ -4817,4 +4942,7 @@ .vop_setattr = zfs_freebsd_setattr, .vop_write = VOP_PANIC, .vop_fid = zfs_freebsd_fid, + .vop_getacl = zfs_freebsd_getacl, + .vop_setacl = zfs_freebsd_setacl, + .vop_aclcheck = zfs_freebsd_aclcheck, }; diff -urN current/sys/cddl/contrib/opensolaris/uts/common/sys/acl.h nfs4acl/sys/cddl/contrib/opensolaris/uts/common/sys/acl.h --- current/sys/cddl/contrib/opensolaris/uts/common/sys/acl.h 2008-11-25 21:11:34.000000000 +0100 +++ nfs4acl/sys/cddl/contrib/opensolaris/uts/common/sys/acl.h 2008-11-21 11:06:18.000000000 +0100 @@ -36,7 +36,7 @@ #endif #define MAX_ACL_ENTRIES (1024) /* max entries of each type */ -typedef struct acl { +typedef struct { int a_type; /* the type of ACL entry */ uid_t a_id; /* the entry in -uid or gid */ o_mode_t a_perm; /* the permission field */ diff -urN current/sys/conf/files nfs4acl/sys/conf/files --- current/sys/conf/files 2008-11-25 21:12:01.000000000 +0100 +++ nfs4acl/sys/conf/files 2008-11-26 09:52:41.000000000 +0100 @@ -1804,6 +1804,7 @@ 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 -urN 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 2008-11-25 20:33:12.000000000 +0100 @@ -0,0 +1,1105 @@ +/*- + * 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. + * + * ALTHOUGH THIS SOFTWARE IS MADE OF WIN AND SCIENCE, IT 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 + +#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_extended != ACL_EXTENDED_ALLOW && + entry->ae_extended != ACL_EXTENDED_DENY) + continue; + + if (entry->ae_flags & ACL_ENTRY_ONLY_INHERIT) + 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_extended == ACL_EXTENDED_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. + * Taken from kern/subr_acl_posix1e.c. + */ + 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; + + /* + * XXX: PRIV_VFS_STAT? + */ + if ((accmode & VSTAT_PERMS) && + !priv_check_cred(cred, PRIV_VFS_READ, 0)) + priv_granted |= VSTAT_PERMS; + + 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 +_entry_does_not_match(struct acl_entry *entry, acl_tag_t tag, acl_perm_t perm, + acl_extended_t extended) +{ + if (entry->ae_tag != tag) + return (1); + + if (entry->ae_id != ACL_UNDEFINED_ID) + return (1); + + if (entry->ae_perm != perm) + return (1); + + if (entry->ae_extended != extended) + return (1); + + if (entry->ae_flags != 0) + return (1); + + return (0); +} + +static struct acl_entry * +_acl_append(struct acl *aclp, acl_tag_t tag, acl_perm_t perm, + acl_extended_t extended) +{ + 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_extended = extended; + 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_extended != ACL_EXTENDED_ALLOW && + entry->ae_extended != ACL_EXTENDED_DENY) + continue; + + /* + * 1.2. If ACL_ENTRY_ONLY_INHERIT is set - skip. + */ + if (entry->ae_flags & ACL_ENTRY_ONLY_INHERIT) + 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_ONLY_INHERIT is set. + */ + entry->ae_flags |= ACL_ENTRY_ONLY_INHERIT; + + /* + * 1.3.3. In the second ACE, the following flags + * are cleared: + * ACL_ENTRY_FILE_INHERIT, + * ACL_ENTRY_DIRECTORY_INHERIT, + * ACL_ENTRY_LIMIT_INHERIT. + */ + copy->ae_flags &= ~(ACL_ENTRY_FILE_INHERIT | + ACL_ENTRY_DIRECTORY_INHERIT | + ACL_ENTRY_LIMIT_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_extended != ACL_EXTENDED_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_extended != ACL_EXTENDED_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 extended the ACL. */ + 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_extended = ACL_EXTENDED_DENY; + } + + /* + * 1.5.2. + */ + /* + * XXX: Verify all these shifts. + */ + 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_extended == ACL_EXTENDED_ALLOW) { + /* + * XXX: Verify. + */ + 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 (_entry_does_not_match(a1, ACL_USER_OBJ, 0, + ACL_EXTENDED_DENY)) + must_append = 1; + if (_entry_does_not_match(a2, ACL_USER_OBJ, ACL_WRITE_ACL | + ACL_WRITE_OWNER | ACL_WRITE_ATTRIBUTES | + ACL_WRITE_NAMED_ATTRS, ACL_EXTENDED_ALLOW)) + must_append = 1; + if (_entry_does_not_match(a3, ACL_GROUP_OBJ, 0, + ACL_EXTENDED_DENY)) + must_append = 1; + if (_entry_does_not_match(a4, ACL_GROUP_OBJ, 0, + ACL_EXTENDED_ALLOW)) + must_append = 1; + if (_entry_does_not_match(a5, ACL_EVERYONE, ACL_WRITE_ACL | + ACL_WRITE_OWNER | ACL_WRITE_ATTRIBUTES | + ACL_WRITE_NAMED_ATTRS, ACL_EXTENDED_DENY)) + must_append = 1; + if (_entry_does_not_match(a6, ACL_EVERYONE, ACL_READ_ACL | + ACL_READ_ATTRIBUTES | ACL_READ_NAMED_ATTRS | + ACL_SYNCHRONIZE, ACL_EXTENDED_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_EXTENDED_DENY); + a2 = _acl_append(aclp, ACL_USER_OBJ, ACL_WRITE_ACL | + ACL_WRITE_OWNER | ACL_WRITE_ATTRIBUTES | + ACL_WRITE_NAMED_ATTRS, ACL_EXTENDED_ALLOW); + a3 = _acl_append(aclp, ACL_GROUP_OBJ, 0, ACL_EXTENDED_DENY); + a4 = _acl_append(aclp, ACL_GROUP_OBJ, 0, ACL_EXTENDED_ALLOW); + a5 = _acl_append(aclp, ACL_EVERYONE, ACL_WRITE_ACL | + ACL_WRITE_OWNER | ACL_WRITE_ATTRIBUTES | + ACL_WRITE_NAMED_ATTRS, ACL_EXTENDED_DENY); + a6 = _acl_append(aclp, ACL_EVERYONE, ACL_READ_ACL | + ACL_READ_ATTRIBUTES | ACL_READ_NAMED_ATTRS | + ACL_SYNCHRONIZE, ACL_EXTENDED_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_extended != ACL_EXTENDED_ALLOW && + entry->ae_extended != ACL_EXTENDED_DENY) + continue; + + if (entry->ae_flags & ACL_ENTRY_ONLY_INHERIT) + 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_extended == ACL_EXTENDED_ALLOW) + mode |= S_IRUSR; + } + + if ((entry->ae_perm & ACL_WRITE_DATA) && + ((seen & S_IWUSR) == 0)) { + seen |= S_IWUSR; + if (entry->ae_extended == ACL_EXTENDED_ALLOW) + mode |= S_IWUSR; + } + + if ((entry->ae_perm & ACL_EXECUTE) && + ((seen & S_IXUSR) == 0)) { + seen |= S_IXUSR; + if (entry->ae_extended == ACL_EXTENDED_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_extended == ACL_EXTENDED_ALLOW) + mode |= S_IRGRP; + } + + if ((entry->ae_perm & ACL_WRITE_DATA) && + ((seen & S_IWGRP) == 0)) { + seen |= S_IWGRP; + if (entry->ae_extended == ACL_EXTENDED_ALLOW) + mode |= S_IWGRP; + } + + if ((entry->ae_perm & ACL_EXECUTE) && + ((seen & S_IXGRP) == 0)) { + seen |= S_IXGRP; + if (entry->ae_extended == ACL_EXTENDED_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_extended == ACL_EXTENDED_ALLOW) + mode |= S_IRUSR; + } + + if ((seen & S_IRGRP) == 0) { + seen |= S_IRGRP; + if (entry->ae_extended == ACL_EXTENDED_ALLOW) + mode |= S_IRGRP; + } + + if ((seen & S_IROTH) == 0) { + seen |= S_IROTH; + if (entry->ae_extended == ACL_EXTENDED_ALLOW) + mode |= S_IROTH; + } + } + + if (entry->ae_perm & ACL_WRITE_DATA) { + if ((seen & S_IWUSR) == 0) { + seen |= S_IWUSR; + if (entry->ae_extended == ACL_EXTENDED_ALLOW) + mode |= S_IWUSR; + } + + if ((seen & S_IWGRP) == 0) { + seen |= S_IWGRP; + if (entry->ae_extended == ACL_EXTENDED_ALLOW) + mode |= S_IWGRP; + } + + if ((seen & S_IWOTH) == 0) { + seen |= S_IWOTH; + if (entry->ae_extended == ACL_EXTENDED_ALLOW) + mode |= S_IWOTH; + } + } + + if (entry->ae_perm & ACL_EXECUTE) { + if ((seen & S_IXUSR) == 0) { + seen |= S_IXUSR; + if (entry->ae_extended == ACL_EXTENDED_ALLOW) + mode |= S_IXUSR; + } + + if ((seen & S_IXGRP) == 0) { + seen |= S_IXGRP; + if (entry->ae_extended == ACL_EXTENDED_ALLOW) + mode |= S_IXGRP; + } + + if ((seen & S_IXOTH) == 0) { + seen |= S_IXOTH; + if (entry->ae_extended == ACL_EXTENDED_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_LIMIT_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_LIMIT_INHERIT) || + !is_directory) && + entry->ae_extended == ACL_EXTENDED_ALLOW) + entry->ae_perm &= ~(ACL_WRITE_ACL | ACL_WRITE_OWNER); + + /* + * 2.A. If the ACL_ENTRY_LIMIT_INHERIT is set, or if the object + * being created is not a directory, then clear the + * following flags: ACL_ENTRY_LIMIT_INHERIT, + * ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT, + * ACL_ENTRY_ONLY_INHERIT. + */ + if (entry->ae_flags & ACL_ENTRY_LIMIT_INHERIT || + !is_directory) { + entry->ae_flags &= ~(ACL_ENTRY_LIMIT_INHERIT | + ACL_ENTRY_FILE_INHERIT | ACL_ENTRY_DIRECTORY_INHERIT | + ACL_ENTRY_ONLY_INHERIT); + + /* + * 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_LIMIT_INHERIT is not set, ensure + * that ACL_ENTRY_ONLY_INHERIT 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_ONLY_INHERIT; + continue; + } + + /* + * 2.C. If the type of the ACE is neither ALLOW nor deny, + * then continue. + */ + if (entry->ae_extended != ACL_EXTENDED_ALLOW && + entry->ae_extended != ACL_EXTENDED_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_ONLY_INHERIT + * is set. + */ + entry->ae_flags |= ACL_ENTRY_ONLY_INHERIT; + + /* + * 2.F. On the second ACE, clear the following flags: + * ACL_ENTRY_LIMIT_INHERIT, ACL_ENTRY_FILE_INHERIT, + * ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_ONLY_INHERIT. + */ + copy->ae_flags &= ~(ACL_ENTRY_LIMIT_INHERIT | + ACL_ENTRY_FILE_INHERIT | ACL_ENTRY_DIRECTORY_INHERIT | + ACL_ENTRY_ONLY_INHERIT); + + /* + * 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_extended == ACL_EXTENDED_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_extended != entryb->ae_extended || + entrya->ae_flags != entryb->ae_flags) + return (0); + } + + return (1); +} + +/* + * This routine is used to determine whether to remove extended 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 extended ACL entries - it's + * critical for performance to not use EA when they are not + * needed. + */ + tmpaclp = acl_alloc(); + 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) { +#if 0 + printf("ae_id is not ACL_UNDEFINED_ID, " + "but it should be.\n"); +#endif + return (EINVAL); + } + break; + + case ACL_USER: + case ACL_GROUP: + if (entry->ae_id == ACL_UNDEFINED_ID) { +#if 0 + printf("ae_id is ACL_UNDEFINED_ID, " + "but it shouldn't be.\n"); +#endif + return (EINVAL); + } + break; + + default: + return (EINVAL); + } + + if ((entry->ae_perm | ACL_NFS4_PERM_BITS) != ACL_NFS4_PERM_BITS) + return (EINVAL); + + /* + * Disallow ACL_EXTENDED_AUDIT and ACL_EXTENDED_ALARM for now. + */ + if (entry->ae_extended != ACL_EXTENDED_ALLOW && + entry->ae_extended != ACL_EXTENDED_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 | ACL_ENTRY_INHERITED)) + 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_LIMIT_INHERIT | ACL_ENTRY_ONLY_INHERIT)) + return (EINVAL); + } + } + + return (0); +} + diff -urN current/sys/kern/subr_acl_posix1e.c nfs4acl/sys/kern/subr_acl_posix1e.c --- current/sys/kern/subr_acl_posix1e.c 2008-11-25 21:20:09.000000000 +0100 +++ nfs4acl/sys/kern/subr_acl_posix1e.c 2008-11-21 11:11:38.000000000 +0100 @@ -59,7 +59,7 @@ accmode_t dac_granted; accmode_t priv_granted; accmode_t acl_mask_granted; - int group_matched, i; + int group_matched, i, error; /* * Look for a normal, non-privileged way to access the file/directory @@ -71,6 +71,9 @@ if (privused != NULL) *privused = 0; + if (unixify_vaccess(&accmode, &error)) + return (error); + /* * Determine privileges now, but don't apply until we've found a DAC * entry that matches but has failed to allow access. @@ -427,6 +430,9 @@ printf("acl_posix1e_mode_to_entry: invalid tag (%d)\n", tag); } + acl_entry.ae_extended = ACL_EXTENDED_ALLOW; + acl_entry.ae_flags = 0; + return (acl_entry); } diff -urN current/sys/kern/vfs_acl.c nfs4acl/sys/kern/vfs_acl.c --- current/sys/kern/vfs_acl.c 2008-11-25 21:20:19.000000000 +0100 +++ nfs4acl/sys/kern/vfs_acl.c 2008-11-21 11:11:49.000000000 +0100 @@ -66,6 +66,124 @@ static int vacl_aclcheck(struct thread *td, struct vnode *vp, acl_type_t type, struct acl *aclp); +int +acl_copy_oldacl_into_acl(const struct oldacl *source, struct acl *dest) +{ + int i; + + if (source->acl_cnt < 0 || source->acl_cnt > OLDACL_MAX_ENTRIES) + return (EINVAL); + + bzero(dest, sizeof(*dest)); + + dest->acl_cnt = source->acl_cnt; + + for (i = 0; i < dest->acl_cnt; i++) { + dest->acl_entry[i].ae_tag = source->acl_entry[i].ae_tag; + dest->acl_entry[i].ae_id = source->acl_entry[i].ae_id; + dest->acl_entry[i].ae_perm = source->acl_entry[i].ae_perm; + } + + return (0); +} + +int +acl_copy_acl_into_oldacl(const struct acl *source, struct oldacl *dest) +{ + int i; + + if (source->acl_cnt < 0 || source->acl_cnt > OLDACL_MAX_ENTRIES) + return (EINVAL); + + bzero(dest, sizeof(*dest)); + + dest->acl_cnt = source->acl_cnt; + + for (i = 0; i < dest->acl_cnt; i++) { + dest->acl_entry[i].ae_tag = source->acl_entry[i].ae_tag; + dest->acl_entry[i].ae_id = source->acl_entry[i].ae_id; + dest->acl_entry[i].ae_perm = source->acl_entry[i].ae_perm; + } + + return (0); +} + +/* + * At one time, "struct ACL" was extended in order to add support for NFSv4 + * ACLs. Instead of creating compatibility versions of all the ACL-related + * syscalls, they were left intact. It's possible to find out what the code + * calling these syscalls (libc) expects basing on "type" argument - if it's + * either ACL_TYPE_ACCESS_OLD or ACL_TYPE_DEFAULT_OLD (which previously were + * known as ACL_TYPE_ACCESS and ACL_TYPE_DEFAULT), then it's the "struct + * oldacl". If it's something else, then it's the new "struct acl". In the + * latter case, the routines below just copyin/copyout the contents. In the + * former case, they copyin the "struct oldacl" and convert it to the new + * format. + */ +static int +copyin_acl(void *user_acl, struct acl *kernel_acl, acl_type_t type) +{ + int error, acl_length; + struct oldacl old; + + acl_length = kernel_acl->acl_length; + + switch (type) { + case ACL_TYPE_ACCESS_OLD: + case ACL_TYPE_DEFAULT_OLD: + error = copyin(user_acl, &old, sizeof(struct oldacl)); + if (error != 0) + break; + error = acl_copy_oldacl_into_acl(&old, kernel_acl); + break; + + default: + error = copyin(user_acl, kernel_acl, sizeof(struct acl)); + } + + kernel_acl->acl_length = acl_length; + + return (error); +} + +static int +copyout_acl(struct acl *kernel_acl, void *user_acl, acl_type_t type) +{ + int error; + struct oldacl old; + + switch (type) { + case ACL_TYPE_ACCESS_OLD: + case ACL_TYPE_DEFAULT_OLD: + error = acl_copy_acl_into_oldacl(kernel_acl, &old); + if (error != 0) + break; + + error = copyout(&old, user_acl, sizeof(struct oldacl)); + break; + + default: + error = copyout(kernel_acl, user_acl, sizeof(struct acl)); + } + + return (error); +} + +static int +type_unold(int type) +{ + switch (type) { + case ACL_TYPE_ACCESS_OLD: + return (ACL_TYPE_ACCESS); + + case ACL_TYPE_DEFAULT_OLD: + return (ACL_TYPE_DEFAULT); + + default: + return (type); + } +} + /* * These calls wrap the real vnode operations, and are called by the syscall * code once the syscall has converted the path or file descriptor to a vnode @@ -81,29 +199,44 @@ vacl_set_acl(struct thread *td, struct vnode *vp, acl_type_t type, struct acl *aclp) { - struct acl inkernacl; + struct acl *inkernelacl; struct mount *mp; int error; - error = copyin(aclp, &inkernacl, sizeof(struct acl)); - if (error) - return(error); + inkernelacl = acl_alloc(); + error = copyin_acl(aclp, inkernelacl, type); + if (error != 0) + goto out_free; + + /* + * With NFS4 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_free; + } + error = vn_start_write(vp, &mp, V_WAIT | PCATCH); if (error != 0) - return (error); + goto out_free; VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); #ifdef MAC - error = mac_vnode_check_setacl(td->td_ucred, vp, type, &inkernacl); + error = mac_vnode_check_setacl(td->td_ucred, vp, type, inkernelacl); if (error != 0) goto out; #endif - error = VOP_SETACL(vp, type, &inkernacl, td->td_ucred, td); + error = VOP_SETACL(vp, type_unold(type), inkernelacl, td->td_ucred, td); #ifdef MAC out: #endif VOP_UNLOCK(vp, 0); vn_finished_write(mp); +out_free: + acl_free(inkernelacl); return(error); } @@ -114,9 +247,10 @@ vacl_get_acl(struct thread *td, struct vnode *vp, acl_type_t type, struct acl *aclp) { - struct acl inkernelacl; + struct acl *inkernelacl; int error; + inkernelacl = acl_alloc(); VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); #ifdef MAC @@ -124,13 +258,18 @@ if (error != 0) goto out; #endif - error = VOP_GETACL(vp, type, &inkernelacl, td->td_ucred, td); -#ifdef MAC + error = VOP_ACCESS(vp, VREAD_ACL, td->td_ucred, td); + if (error != 0) + goto out; + + error = VOP_GETACL(vp, type_unold(type), inkernelacl, + td->td_ucred, td); + out: -#endif VOP_UNLOCK(vp, 0); if (error == 0) - error = copyout(&inkernelacl, aclp, sizeof(struct acl)); + error = copyout_acl(inkernelacl, aclp, type); + acl_free(inkernelacl); return (error); } @@ -144,16 +283,16 @@ int error; error = vn_start_write(vp, &mp, V_WAIT | PCATCH); - if (error) + if (error != 0) return (error); VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); 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, type, 0, td->td_ucred, td); + error = VOP_SETACL(vp, type_unold(type), 0, td->td_ucred, td); #ifdef MAC out: #endif @@ -169,13 +308,29 @@ vacl_aclcheck(struct thread *td, struct vnode *vp, acl_type_t type, struct acl *aclp) { - struct acl inkernelacl; + struct acl *inkernelacl; int error; - error = copyin(aclp, &inkernelacl, sizeof(struct acl)); - if (error) - return(error); - error = VOP_ACLCHECK(vp, type, &inkernelacl, td->td_ucred, td); + inkernelacl = acl_alloc(); + error = copyin_acl(aclp, inkernelacl, type); + if (error != 0) + goto out_free; + + /* + * With NFS4 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_free; + } + + error = VOP_ACLCHECK(vp, type_unold(type), inkernelacl, + td->td_ucred, td); +out_free: + acl_free(inkernelacl); return (error); } @@ -420,6 +575,43 @@ return (error); } +/* + * Allocate "struct acl" instance. + */ +struct acl * +acl_alloc(void) +{ + struct acl *aclp; + + aclp = uma_zalloc(acl_zone, M_WAITOK | M_ZERO); + aclp->acl_magic = ACL_MAGIC; + aclp->acl_length = ACL_MAX_ENTRIES; + aclp->acl_cnt = 0; + + return (aclp); +} + +/* + * Free "struct acl". + */ +void +acl_free(struct acl *aclp) +{ + uma_zfree(acl_zone, aclp); +} + +/* + * Return the size, in bytes, required to store ACL with "cnt" entries. + */ +int +acl_size_for_cnt(int cnt) +{ + int size = sizeof(struct acl) - (ACL_MAX_ENTRIES - cnt) * + sizeof(struct acl_entry); + + return size; +} + /* ARGUSED */ static void diff -urN current/sys/kern/vfs_subr.c nfs4acl/sys/kern/vfs_subr.c --- current/sys/kern/vfs_subr.c 2008-11-25 21:20:23.000000000 +0100 +++ nfs4acl/sys/kern/vfs_subr.c 2008-11-21 11:11:54.000000000 +0100 @@ -3465,6 +3465,7 @@ vaccess(enum vtype type, mode_t file_mode, uid_t file_uid, gid_t file_gid, accmode_t accmode, struct ucred *cred, int *privused) { + int error; accmode_t dac_granted; accmode_t priv_granted; @@ -3478,6 +3479,9 @@ dac_granted = 0; + if (unixify_vaccess(&accmode, &error)) + return (error); + /* Check the owner. */ if (cred->cr_uid == file_uid) { dac_granted |= VADMIN; @@ -3546,7 +3550,8 @@ !priv_check_cred(cred, PRIV_VFS_READ, 0)) priv_granted |= VREAD; - if ((accmode & VWRITE) && ((dac_granted & VWRITE) == 0) && + if ((((accmode & VWRITE) && ((dac_granted & VWRITE) == 0)) || + ((accmode & VAPPEND) && ((dac_granted & VAPPEND) == 0))) && !priv_check_cred(cred, PRIV_VFS_WRITE, 0)) priv_granted |= (VWRITE | VAPPEND); @@ -3588,6 +3593,13 @@ /* 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_ACCESS(vp, VREAD_NAMED_ATTRS, cred, td)); + if (accmode == VWRITE) + return (VOP_ACCESS(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); @@ -4195,3 +4207,44 @@ (void)VOP_SETATTR(vp, &atimeattr, cred); } } + +/* + * The purpose of this routine is to remove granularity from vaccess_t, + * reducing it into standard unix access bits. + * + * This routine is supposed to be called from the beginning of vaccess + * implementations that don't know anything about granularity. If it + * returns 1, then the caller is supposed to return whatever this routine + * puts into variable pointed to by "error". + */ +int +unixify_vaccess(accmode_t *accmode, int *error) +{ + /* + * Unix does not provide any explicit "deny" access rules. + */ + if (*accmode & VEXPLICIT_DENY) { + *error = 0; + return (1); + } + + if (*accmode & (VDELETE_CHILD | VDELETE)) { + *error = EPERM; + return (1); + } + + if (*accmode & VADMIN_PERMS) { + *accmode &= ~VADMIN_PERMS; + *accmode |= VADMIN; + } + + *accmode &= ~VSTAT_PERMS; + + if (*accmode == 0) { + *error = 0; + return (1); + } + + return (0); +} + diff -urN current/sys/kern/vfs_vnops.c nfs4acl/sys/kern/vfs_vnops.c --- current/sys/kern/vfs_vnops.c 2008-11-25 21:20:24.000000000 +0100 +++ nfs4acl/sys/kern/vfs_vnops.c 2008-11-21 11:11:55.000000000 +0100 @@ -702,6 +702,9 @@ if (error) return (error); #endif + error = VOP_ACCESS(vp, VREAD_ATTRIBUTES, active_cred, td); + if (error) + return (error); vap = &vattr; diff -urN current/sys/modules/zfs/Makefile nfs4acl/sys/modules/zfs/Makefile --- current/sys/modules/zfs/Makefile 2008-11-25 21:20:57.000000000 +0100 +++ nfs4acl/sys/modules/zfs/Makefile 2008-11-21 11:12:36.000000000 +0100 @@ -67,6 +67,7 @@ ZFS_SRCS= ${ZFS_OBJS:C/.o$/.c/} SRCS+= ${ZFS_SRCS} SRCS+= vdev_geom.c +SRCS+= acl_compat.c # Use UMA for ZIO allocation. This is not stable. #CFLAGS+=-DZIO_USE_UMA diff -urN current/sys/security/mac_biba/mac_biba.c nfs4acl/sys/security/mac_biba/mac_biba.c --- current/sys/security/mac_biba/mac_biba.c 2008-11-25 21:22:46.000000000 +0100 +++ nfs4acl/sys/security/mac_biba/mac_biba.c 2008-11-21 11:13:44.000000000 +0100 @@ -2893,11 +2893,11 @@ obj = SLOT(vplabel); /* XXX privilege override for admin? */ - if (accmode & (VREAD | VEXEC | VSTAT)) { + if (accmode & (VREAD | VEXEC | VSTAT_PERMS)) { if (!biba_dominate_effective(obj, subj)) return (EACCES); } - if (accmode & (VWRITE | VAPPEND | VADMIN)) { + if (accmode & VMODIFY_PERMS) { if (!biba_dominate_effective(subj, obj)) return (EACCES); } diff -urN current/sys/security/mac_lomac/mac_lomac.c nfs4acl/sys/security/mac_lomac/mac_lomac.c --- current/sys/security/mac_lomac/mac_lomac.c 2008-11-25 21:22:46.000000000 +0100 +++ nfs4acl/sys/security/mac_lomac/mac_lomac.c 2008-11-21 11:13:45.000000000 +0100 @@ -2441,7 +2441,7 @@ 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 -urN current/sys/security/mac_mls/mac_mls.c nfs4acl/sys/security/mac_mls/mac_mls.c --- current/sys/security/mac_mls/mac_mls.c 2008-11-25 21:22:47.000000000 +0100 +++ nfs4acl/sys/security/mac_mls/mac_mls.c 2008-11-21 11:13:45.000000000 +0100 @@ -2516,11 +2516,11 @@ obj = SLOT(vplabel); /* XXX privilege override for admin? */ - if (accmode & (VREAD | VEXEC | VSTAT)) { + if (accmode & (VREAD | VEXEC | VSTAT_PERMS)) { if (!mls_dominate_effective(subj, obj)) return (EACCES); } - if (accmode & (VWRITE | VAPPEND | VADMIN)) { + if (accmode & VMODIFY_PERMS) { if (!mls_dominate_effective(obj, subj)) return (EACCES); } diff -urN current/sys/sys/acl.h nfs4acl/sys/sys/acl.h --- current/sys/sys/acl.h 2008-11-25 21:23:02.000000000 +0100 +++ nfs4acl/sys/sys/acl.h 2008-11-25 20:33:12.000000000 +0100 @@ -47,35 +47,107 @@ #define POSIX1E_ACL_ACCESS_EXTATTR_NAME "posix1e.acl_access" #define POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE EXTATTR_NAMESPACE_SYSTEM #define POSIX1E_ACL_DEFAULT_EXTATTR_NAME "posix1e.acl_default" -#define ACL_MAX_ENTRIES 32 /* maximum entries in an ACL */ +#define NFS4_ACL_EXTATTR_NAMESPACE EXTATTR_NAMESPACE_SYSTEM +#define NFS4_ACL_EXTATTR_NAME "nfs4.acl" +#define OLDACL_MAX_ENTRIES 32 -typedef int acl_type_t; +/* + * With 204 entries, "struct acl" is exactly one page big. + * Note that with NFS4 ACLs, the maximum number of ACL entries one + * may set on file or directory is about half of ACL_MAX_ENTRIES. + * + * If you increase this, you might also need to increase + * _ACL_T_ALIGNMENT_BITS in lib/libc/posix1e/acl_support.h. + * + * The maximum number of POSIX.1e ACLs is controlled + * by OLDACL_MAX_ENTRIES. Changing that one will break binary + * compatibility with userland and change on-disk ACL layout. + */ +#define ACL_MAX_ENTRIES 204 + +/* + * "struct oldacl" is used in compatibility ACL syscalls and for on-disk + * storage of POSIX.1e ACLs. + */ +typedef int oldacl_tag_t; +typedef mode_t oldacl_perm_t; + +struct oldacl_entry { + oldacl_tag_t ae_tag; + uid_t ae_id; + oldacl_perm_t ae_perm; +}; +typedef struct oldacl_entry *oldacl_entry_t; + +struct oldacl { + int acl_cnt; + struct oldacl_entry acl_entry[OLDACL_MAX_ENTRIES]; +}; + +/* + * Current "struct acl". + */ typedef int acl_tag_t; -typedef mode_t acl_perm_t; -typedef mode_t *acl_permset_t; +typedef int acl_flag_t; +typedef int acl_perm_t; +typedef int acl_extended_t; +typedef int acl_type_t; +typedef int *acl_permset_t; +typedef int *acl_flagset_t; struct acl_entry { acl_tag_t ae_tag; uid_t ae_id; acl_perm_t ae_perm; + /* "allow" or "deny". Unused in POSIX ACLs. */ + acl_extended_t ae_extended; + /* Flags control inheritance. Unused in POSIX ACLs. */ + acl_flag_t ae_flags; }; typedef struct acl_entry *acl_entry_t; -/* internal ACL structure */ +/* + * Internal ACL structure, used in libc, kernel APIs and for on-disk + * storage of NFS4 ACLs. POSIX.1e ACLs use "struct oldacl" for on-disk + * storage. + */ struct acl { + int acl_magic; int acl_cnt; + int acl_length; + int acl_spare_field; struct acl_entry acl_entry[ACL_MAX_ENTRIES]; }; -/* external ACL structure */ +/* + * External ACL structure, used in API for userland applications (acl(3)). + */ struct acl_t_struct { struct acl ats_acl; int ats_cur_entry; + /* + * 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 NFS4 ACL. + */ + int ats_brand; }; typedef struct acl_t_struct *acl_t; +#define ACL_MAGIC 0x5452535a + /* - * Possible valid values for ae_tag field. + * 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 #define ACL_USER_OBJ 0x00000001 @@ -85,15 +157,28 @@ #define ACL_MASK 0x00000010 #define ACL_OTHER 0x00000020 #define ACL_OTHER_OBJ ACL_OTHER +#define ACL_EVERYONE 0x00000040 + +/* + * Possible valid values for ae_extended field. + */ +#define ACL_EXTENDED_ALLOW 0x00000100 +#define ACL_EXTENDED_DENY 0x00000200 +#define ACL_EXTENDED_AUDIT 0x00000400 +#define ACL_EXTENDED_ALARM 0x00000800 /* - * Possible valid values for acl_type_t arguments. + * Possible valid values for acl_type_t arguments. First two + * are provided only for backwards binary compatibility. */ -#define ACL_TYPE_ACCESS 0x00000000 -#define ACL_TYPE_DEFAULT 0x00000001 +#define ACL_TYPE_ACCESS_OLD 0x00000000 +#define ACL_TYPE_DEFAULT_OLD 0x00000001 +#define ACL_TYPE_ACCESS 0x00000002 +#define ACL_TYPE_DEFAULT 0x00000003 +#define ACL_TYPE_NFS4 0x00000004 /* - * Possible flags in ae_perm field. + * Possible flags in ae_perm field for POSIX ACLs. */ #define ACL_EXECUTE 0x0001 #define ACL_WRITE 0x0002 @@ -103,20 +188,79 @@ #define ACL_POSIX1E_BITS (ACL_EXECUTE | ACL_WRITE | ACL_READ) /* + * Possible flags in ae_perm field for NFSv4 ACLs. + * XXX: Change values of these to match rfc3530. + */ +#define ACL_READ_DATA 0x00010000 +#define ACL_LIST_DIRECTORY 0x00010000 +#define ACL_WRITE_DATA 0x00020000 +#define ACL_ADD_FILE 0x00020000 +#define ACL_APPEND_DATA 0x00040000 +#define ACL_ADD_SUBDIRECTORY 0x00040000 +#define ACL_READ_NAMED_ATTRS 0x00080000 +#define ACL_READ_EXTATTRIBUTES ACL_READ_NAMED_ATTRS /* Darwin compatibility. */ +#define ACL_WRITE_NAMED_ATTRS 0x00100000 +#define ACL_WRITE_EXTATTRIBUTES ACL_WRITE_NAMED_ATTRS /* Darwin compatibility. */ +#ifdef XXX_conflicting_defines +#define ACL_EXECUTE 0x00200000 +#endif +#define ACL_SEARCH ACL_EXECUTE /* Darwin compatibility. */ +#define ACL_DELETE_CHILD 0x00400000 +#define ACL_READ_ATTRIBUTES 0x00800000 +#define ACL_WRITE_ATTRIBUTES 0x01000000 +#define ACL_DELETE 0x02000000 +#define ACL_READ_ACL 0x04000000 +#define ACL_READ_SECURITY ACL_READ_ACL /* Darwin compatibility. */ +#define ACL_WRITE_ACL 0x08000000 +#define ACL_WRITE_SECURITY ACL_WRITE_ACL /* Darwin compatibility. */ +#define ACL_WRITE_OWNER 0x10000000 +#define ACL_CHANGE_OWNER ACL_WRITE_OWNER /* Darwin compatibility. */ +#define ACL_SYNCHRONIZE 0x20000000 + +#define ACL_NFS4_PERM_BITS (ACL_READ_DATA | ACL_WRITE_DATA | ACL_APPEND_DATA | \ + ACL_READ_NAMED_ATTRS | ACL_WRITE_NAMED_ATTRS | ACL_EXECUTE | ACL_DELETE_CHILD | \ + ACL_READ_ATTRIBUTES | ACL_WRITE_ATTRIBUTES | ACL_DELETE | ACL_READ_ACL | \ + ACL_WRITE_ACL | ACL_WRITE_OWNER | ACL_SYNCHRONIZE) + +/* * Possible entry_id values for acl_get_entry() */ #define ACL_FIRST_ENTRY 0 #define ACL_NEXT_ENTRY 1 /* - * Undefined value in ae_id field + * Possible values in ae_flags field; valid only for NFSv4 ACLs. */ -#define ACL_UNDEFINED_ID ((uid_t)-1) - +/* + * ACL_FLAG_DEFER_INHERIT does not seem to be used anywhere in Darwin, + * and there is no such flag in NFSv4 spec. + * + * #define ACL_FLAG_DEFER_INHERIT + */ +#define ACL_ENTRY_FILE_INHERIT 0x00000001 +#define ACL_ENTRY_DIRECTORY_INHERIT 0x00000002 +#define ACL_ENTRY_LIMIT_INHERIT 0x00000004 /* "NO_PROPAGATE_INHERIT" */ +#define ACL_ENTRY_ONLY_INHERIT 0x00000008 /* "INHERIT_ONLY" */ +#define ACL_ENTRY_SUCCESSFUL_ACCESS 0x00000010 +#define ACL_ENTRY_FAILED_ACCESS 0x00000020 +#define ACL_ENTRY_INHERITED 0x00000080 /* Currently unused. */ + +#define ACL_FLAGS_BITS (ACL_ENTRY_FILE_INHERIT | ACL_ENTRY_DIRECTORY_INHERIT | \ + ACL_ENTRY_LIMIT_INHERIT | ACL_ENTRY_ONLY_INHERIT | ACL_ENTRY_SUCCESSFUL_ACCESS | \ + ACL_ENTRY_FAILED_ACCESS) -#ifdef _KERNEL +/* + * Undefined value in ae_id field. ae_id should be set to this value + * iff ae_tag is ACL_USER_OBJ, ACL_GROUP_OBJ, ACL_OTHER or ACL_EVERYONE. + */ +#define ACL_UNDEFINED_ID ((uid_t)-1) -extern uma_zone_t acl_zone; +/* + * 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 @@ -127,8 +271,9 @@ #define ACL_OVERRIDE_MASK (S_IRWXU | S_IRWXG | S_IRWXO) #define ACL_PRESERVE_MASK (~ACL_OVERRIDE_MASK) +#ifdef _KERNEL /* - * File system independent code to move back and forth between POSIX mode and + * Filesystem-independent code to move back and forth between POSIX mode and * POSIX.1e ACL representations. */ acl_perm_t acl_posix1e_mode_to_perm(acl_tag_t tag, mode_t mode); @@ -142,16 +287,35 @@ mode_t acl_posix1e_newfilemode(mode_t cmode, struct acl *dacl); +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, + struct oldacl *dest); + /* - * File system independent syntax check for a POSIX.1e 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); +struct acl *acl_alloc(void); +void acl_free(struct acl *aclp); +int acl_size_for_cnt(int cnt); #else /* !_KERNEL */ /* * Syscall interface -- use the library calls instead as the syscalls have - * strict acl entry ordering requirements. + * strict ACL entry ordering requirements. */ __BEGIN_DECLS int __acl_aclcheck_fd(int _filedes, acl_type_t _type, struct acl *_aclp); @@ -177,46 +341,60 @@ * 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_extended_np(acl_entry_t _entry_d, acl_extended_t *_extended_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_extended_np(acl_entry_t _entry_d, acl_extended_t _extended); 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 -urN current/sys/sys/mount.h nfs4acl/sys/sys/mount.h --- current/sys/sys/mount.h 2008-11-25 21:23:08.000000000 +0100 +++ nfs4acl/sys/sys/mount.h 2008-11-21 11:13:54.000000000 +0100 @@ -231,6 +231,7 @@ #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. @@ -266,7 +267,7 @@ 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 | \ @@ -274,7 +275,7 @@ 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. @@ -292,10 +293,6 @@ #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 -urN current/sys/sys/unistd.h nfs4acl/sys/sys/unistd.h --- current/sys/sys/unistd.h 2008-11-25 21:23:14.000000000 +0100 +++ nfs4acl/sys/sys/unistd.h 2008-11-21 11:13:56.000000000 +0100 @@ -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 -urN current/sys/sys/vnode.h nfs4acl/sys/sys/vnode.h --- current/sys/sys/vnode.h 2008-11-25 21:23:15.000000000 +0100 +++ nfs4acl/sys/sys/vnode.h 2008-11-21 11:13:56.000000000 +0100 @@ -309,15 +309,52 @@ #define IO_SEQSHIFT 16 /* seq heuristic in upper 16 bits */ /* - * Flags for accmode_t. + * Flags for accmode_t. */ -#define VEXEC 000100 /* execute/search permission */ -#define VWRITE 000200 /* write permission */ -#define VREAD 000400 /* read permission */ -#define VADMIN 010000 /* permission to administer */ -#define VSTAT 020000 /* permission to retrieve attrs */ -#define VAPPEND 040000 /* permission to write/append */ -#define VALLPERM (VEXEC | VWRITE | VREAD | VADMIN | VSTAT | VAPPEND) +#define VEXEC 000000000100 /* execute/search permission */ +#define VWRITE 000000000200 /* write permission */ +#define VREAD 000000000400 /* read permission */ +#define VADMIN 000000010000 /* being the file owner */ +#define VSTAT 000000020000 /* permission to retrieve attrs */ +#define VAPPEND 000000040000 /* permission to write/append */ +/* + * Return EPERM or EACCES only if permission was denied explicitly, + * by a "deny" rule in NFS4 ACL. This never happens with ordinary + * unix access rights or POSIX.1e ACLs. Obviously, VEXPLICIT_DENY + * must be OR-ed with some other Vflag. + */ +#define VEXPLICIT_DENY 000000100000 +#define VREAD_NAMED_ATTRS 000000200000 /* not used */ +#define VWRITE_NAMED_ATTRS 000000400000 /* not used */ +#define VDELETE_CHILD 000001000000 +#define VREAD_ATTRIBUTES 000002000000 +#define VWRITE_ATTRIBUTES 000004000000 +#define VDELETE 000010000000 +#define VREAD_ACL 000020000000 +#define VWRITE_ACL 000040000000 +#define VWRITE_OWNER 000100000000 +#define VSYNCHRONIZE 000200000000 /* not used */ +#define VALLPERM (VEXEC | VWRITE | VREAD | VADMIN | VSTAT | VAPPEND \ + VEXPLICIT_DENY | VREAD_NAMED_ATTRS | VWRITE_NAMED_ATTRS | VDELETE_CHILD \ + VREAD_ATTRIBUTES | VWRITE_ATTRIBUTES | VDELETE | VREAD_ACL | VWRITE_ACL \ + VWRITE_OWNER | VSYNCHRONIZE) + +/* + * Permissions that were traditionally granted only to the file owner. + */ +#define VADMIN_PERMS (VADMIN | VWRITE_ATTRIBUTES | VWRITE_ACL | \ + VWRITE_OWNER) + +/* + * Permissions that were traditionally granted to everyone. + */ +#define VSTAT_PERMS (VSTAT | VREAD_ATTRIBUTES | VREAD_ACL | VSYNCHRONIZE) + +/* + * Permissions that allow to change the state of the file in any way. + */ +#define VMODIFY_PERMS (VWRITE | VAPPEND | VADMIN_PERMS | VDELETE_CHILD | \ + VDELETE) /* * Token indicating no attribute value yet assigned. @@ -589,6 +626,10 @@ 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); +int unixify_vaccess(accmode_t *accmode, int *error); void vattr_null(struct vattr *vap); int vcount(struct vnode *vp); void vdrop(struct vnode *); diff -urN current/sys/ufs/ffs/ffs_vfsops.c nfs4acl/sys/ufs/ffs/ffs_vfsops.c --- current/sys/ufs/ffs/ffs_vfsops.c 2008-11-25 21:23:17.000000000 +0100 +++ nfs4acl/sys/ufs/ffs/ffs_vfsops.c 2008-11-21 11:13:58.000000000 +0100 @@ -129,7 +129,7 @@ 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, struct thread *td) @@ -176,6 +176,14 @@ 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); @@ -832,7 +840,12 @@ 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 -urN current/sys/ufs/ufs/ufs_acl.c nfs4acl/sys/ufs/ufs/ufs_acl.c --- current/sys/ufs/ufs/ufs_acl.c 2008-11-25 21:23:18.000000000 +0100 +++ nfs4acl/sys/ufs/ufs/ufs_acl.c 2008-11-21 11:13:58.000000000 +0100 @@ -140,6 +140,73 @@ DIP_SET(ip, i_mode, ip->i_mode); } +static int +ufs_getacl_nfs4(struct vop_getacl_args *ap) +{ + int error, len, acl_length; + struct inode *ip = VTOI(ap->a_vp); + + if ((ap->a_vp->v_mount->mnt_flag & MNT_NFS4ACLS) == 0) + return (EOPNOTSUPP); + + acl_length = ap->a_aclp->acl_length; + 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_length = acl_length; + 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 != acl_size_for_cnt(ap->a_aclp->acl_cnt)) { + /* + * 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); + } + + if (ap->a_aclp->acl_magic != ACL_MAGIC) { + printf("ufs_getacl_nfs4(): Loaded invalid ACL " + "(wrong acl_magic: 0x%x), inumber %d on %s\n", + ap->a_aclp->acl_magic, + 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); +} + /* * Retrieve the ACL on a file. * @@ -147,18 +214,12 @@ * assemble both into a final ACL product. Right now this is not done * very efficiently. */ -int -ufs_getacl(ap) - struct vop_getacl_args /* { - struct vnode *vp; - struct acl_type_t type; - struct acl *aclp; - struct ucred *cred; - struct thread *td; - } */ *ap; +static int +ufs_getacl_posix1e(struct vop_getacl_args *ap) { struct inode *ip = VTOI(ap->a_vp); int error, len; + struct oldacl old; /* * XXX: If ufs_getacl() should work on file systems not supporting @@ -170,8 +231,8 @@ /* * Attempt to retrieve the ACL based on the ACL type. */ - bzero(ap->a_aclp, sizeof(*ap->a_aclp)); - len = sizeof(*ap->a_aclp); + bzero(&old, sizeof(old)); + len = sizeof(old); switch(ap->a_type) { case ACL_TYPE_ACCESS: /* @@ -183,7 +244,7 @@ */ error = vn_extattr_get(ap->a_vp, IO_NODELOCKED, POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE, - POSIX1E_ACL_ACCESS_EXTATTR_NAME, &len, (char *) ap->a_aclp, + POSIX1E_ACL_ACCESS_EXTATTR_NAME, &len, (char *) &old, ap->a_td); switch (error) { /* XXX: If ufs_getacl() should work on filesystems without @@ -195,35 +256,46 @@ * be updated when the ACL is synchronized with * the inode later. */ - ap->a_aclp->acl_cnt = 3; - ap->a_aclp->acl_entry[0].ae_tag = ACL_USER_OBJ; - ap->a_aclp->acl_entry[0].ae_id = ACL_UNDEFINED_ID; - ap->a_aclp->acl_entry[0].ae_perm = ACL_PERM_NONE; - ap->a_aclp->acl_entry[1].ae_tag = ACL_GROUP_OBJ; - ap->a_aclp->acl_entry[1].ae_id = ACL_UNDEFINED_ID; - ap->a_aclp->acl_entry[1].ae_perm = ACL_PERM_NONE; - ap->a_aclp->acl_entry[2].ae_tag = ACL_OTHER; - ap->a_aclp->acl_entry[2].ae_id = ACL_UNDEFINED_ID; - ap->a_aclp->acl_entry[2].ae_perm = ACL_PERM_NONE; + old.acl_cnt = 3; + old.acl_entry[0].ae_tag = ACL_USER_OBJ; + old.acl_entry[0].ae_id = ACL_UNDEFINED_ID; + old.acl_entry[0].ae_perm = ACL_PERM_NONE; + old.acl_entry[1].ae_tag = ACL_GROUP_OBJ; + old.acl_entry[1].ae_id = ACL_UNDEFINED_ID; + old.acl_entry[1].ae_perm = ACL_PERM_NONE; + old.acl_entry[2].ae_tag = ACL_OTHER; + old.acl_entry[2].ae_id = ACL_UNDEFINED_ID; + old.acl_entry[2].ae_perm = ACL_PERM_NONE; + + error = acl_copy_oldacl_into_acl(&old, ap->a_aclp); + if (error) + return (error); + ufs_sync_acl_from_inode(ip, ap->a_aclp); - error = 0; - break; + + return (0); case 0: - if (len != sizeof(*ap->a_aclp)) { + if (len != sizeof(old)) { /* * 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(): Loaded invalid ACL (" - "%d bytes), inumber %d on %s\n", len, + printf("ufs_getacl_posix1e(): Loaded invalid " + "ACL (%d bytes), inumber %d on %s\n", len, ip->i_number, ip->i_fs->fs_fsmnt); return (EPERM); } + + error = acl_copy_oldacl_into_acl(&old, ap->a_aclp); + if (error) + return (error); + ufs_sync_acl_from_inode(ip, ap->a_aclp); - break; + + return (0); default: break; @@ -238,7 +310,7 @@ error = vn_extattr_get(ap->a_vp, IO_NODELOCKED, POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE, POSIX1E_ACL_DEFAULT_EXTATTR_NAME, &len, - (char *) ap->a_aclp, ap->a_td); + (char *) &old, ap->a_td); /* * Unlike ACL_TYPE_ACCESS, there is no relationship between * the inode contents and the ACL, and it is therefore @@ -250,13 +322,13 @@ /* XXX: If ufs_getacl() should work on filesystems without * the EA configured, add case EOPNOTSUPP here. */ case ENOATTR: - bzero(ap->a_aclp, sizeof(*ap->a_aclp)); - ap->a_aclp->acl_cnt = 0; + bzero(&old, sizeof(old)); + old.acl_cnt = 0; error = 0; break; case 0: - if (len != sizeof(*ap->a_aclp)) { + if (len != sizeof(old)) { /* * A short (or long) read, meaning that for * some reason the ACL is corrupted. Return @@ -279,9 +351,108 @@ error = EINVAL; } + if (error) + return (error); + + error = acl_copy_oldacl_into_acl(&old, ap->a_aclp); + return (error); } +int +ufs_getacl(ap) + struct vop_getacl_args /* { + struct vnode *vp; + acl_type_t type; + struct acl *aclp; + struct ucred *cred; + struct thread *td; + } */ *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_ACCESS(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 { + ap->a_aclp->acl_magic = ACL_MAGIC; + error = vn_extattr_set(ap->a_vp, IO_NODELOCKED, + NFS4_ACL_EXTATTR_NAMESPACE, + NFS4_ACL_EXTATTR_NAME, + acl_size_for_cnt(ap->a_aclp->acl_cnt), + (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. * @@ -291,18 +462,12 @@ * a fair number of different access checks may be required to go ahead * with the operation at all. */ -int -ufs_setacl(ap) - struct vop_setacl_args /* { - struct vnode *vp; - acl_type_t type; - struct acl *aclp; - struct ucred *cred; - struct proc *p; - } */ *ap; +static int +ufs_setacl_posix1e(struct vop_setacl_args *ap) { struct inode *ip = VTOI(ap->a_vp); int error; + struct oldacl old; if ((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0) return (EOPNOTSUPP); @@ -320,6 +485,10 @@ ap->a_cred, ap->a_td); if (error != 0) return (error); + + error = acl_copy_acl_into_oldacl(ap->a_aclp, &old); + if (error) + return (error); } else { /* * Delete operation. @@ -351,8 +520,8 @@ case ACL_TYPE_ACCESS: error = vn_extattr_set(ap->a_vp, IO_NODELOCKED, POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE, - POSIX1E_ACL_ACCESS_EXTATTR_NAME, sizeof(*ap->a_aclp), - (char *) ap->a_aclp, ap->a_td); + POSIX1E_ACL_ACCESS_EXTATTR_NAME, sizeof(old), + (char *) &old, ap->a_td); break; case ACL_TYPE_DEFAULT: @@ -376,7 +545,7 @@ error = vn_extattr_set(ap->a_vp, IO_NODELOCKED, POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE, POSIX1E_ACL_DEFAULT_EXTATTR_NAME, - sizeof(*ap->a_aclp), (char *) ap->a_aclp, ap->a_td); + sizeof(old), (char *) &old, ap->a_td); break; default: @@ -404,12 +573,9 @@ return (0); } -/* - * Check the validity of an ACL for a file. - */ int -ufs_aclcheck(ap) - struct vop_aclcheck_args /* { +ufs_setacl(ap) + struct vop_setacl_args /* { struct vnode *vp; acl_type_t type; struct acl *aclp; @@ -417,7 +583,29 @@ struct thread *td; } */ *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) +{ if ((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0) return (EOPNOTSUPP); @@ -441,4 +629,23 @@ return (acl_posix1e_check(ap->a_aclp)); } +/* + * Check the validity of an ACL for a file. + */ +int +ufs_aclcheck(ap) + struct vop_aclcheck_args /* { + struct vnode *vp; + acl_type_t type; + struct acl *aclp; + struct ucred *cred; + struct thread *td; + } */ *ap; +{ + if (ap->a_type == ACL_TYPE_NFS4) + return (ufs_aclcheck_nfs4(ap)); + + return (ufs_aclcheck_posix1e(ap)); +} + #endif /* !UFS_ACL */ diff -urN current/sys/ufs/ufs/ufs_lookup.c nfs4acl/sys/ufs/ufs/ufs_lookup.c --- current/sys/ufs/ufs/ufs_lookup.c 2008-11-25 21:23:19.000000000 +0100 +++ nfs4acl/sys/ufs/ufs/ufs_lookup.c 2008-11-26 09:53:03.000000000 +0100 @@ -77,6 +77,61 @@ /* true if old FS format...*/ #define OFSFMT(vp) ((vp)->v_mount->mnt_maxsymlinklen <= 0) +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_ACCESS(tdp, VDELETE, cred, td); + if (error == 0) + return (0); + + error = VOP_ACCESS(vdp, VDELETE_CHILD, cred, td); + if (error == 0) + return (0); + + error = VOP_ACCESS(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. @@ -405,8 +460,13 @@ /* * 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_ACCESS(vdp, VAPPEND, cred, cnp->cn_thread); + else + error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_thread); if (error) return (error); /* @@ -488,12 +548,17 @@ 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 @@ -513,23 +578,10 @@ 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); } @@ -541,7 +593,11 @@ * 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_ACCESS(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. @@ -553,6 +609,33 @@ 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_ACCESS(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 -urN current/sys/ufs/ufs/ufs_vnops.c nfs4acl/sys/ufs/ufs/ufs_vnops.c --- current/sys/ufs/ufs/ufs_vnops.c 2008-11-25 21:23:19.000000000 +0100 +++ nfs4acl/sys/ufs/ufs/ufs_vnops.c 2008-11-21 11:13:59.000000000 +0100 @@ -315,6 +315,7 @@ #endif #ifdef UFS_ACL struct acl *acl; + acl_type_t type; #endif /* @@ -322,7 +323,7 @@ * 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 @@ } } - /* 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) { - acl = uma_zalloc(acl_zone, M_WAITOK); - error = VOP_GETACL(vp, ACL_TYPE_ACCESS, acl, ap->a_cred, + 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(); + error = VOP_GETACL(vp, type, acl, ap->a_cred, ap->a_td); switch (error) { case EOPNOTSUPP: @@ -382,8 +393,13 @@ 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( @@ -397,7 +413,7 @@ error = vaccess(vp->v_type, ip->i_mode, ip->i_uid, ip->i_gid, ap->a_accmode, ap->a_cred, NULL); } - uma_zfree(acl_zone, acl); + acl_free(acl); } else #endif /* !UFS_ACL */ error = vaccess(vp->v_type, ip->i_mode, ip->i_uid, ip->i_gid, @@ -618,11 +634,20 @@ * 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_ACCESS(vp, VWRITE_ATTRIBUTES, cred, td); if (error) error = VOP_ACCESS(vp, VWRITE, cred, td); } else - error = VOP_ACCESS(vp, VADMIN, cred, td); + error = VOP_ACCESS(vp, VWRITE_ATTRIBUTES, cred, td); if (error) return (error); if (vap->va_atime.tv_sec != VNOVAL) @@ -662,6 +687,35 @@ 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(); + + 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 */ + /* * Change the mode on a file. * Inode must be locked before calling. @@ -680,7 +734,7 @@ * To modify the permissions on a file, must possess VADMIN * for that file. */ - if ((error = VOP_ACCESS(vp, VADMIN, cred, td))) + if ((error = VOP_ACCESS(vp, VWRITE_ACL, cred, td))) return (error); /* * Privileged processes may set the sticky bit on non-directories, @@ -697,11 +751,25 @@ 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); } /* @@ -733,14 +801,14 @@ * To modify the ownership of a file, must possess VADMIN for that * file. */ - if ((error = VOP_ACCESS(vp, VADMIN, cred, td))) + if ((error = VOP_ACCESS(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); @@ -1386,6 +1454,36 @@ 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(); + child_aclp = acl_alloc(); + + 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 */ @@ -1498,8 +1596,8 @@ #ifdef UFS_ACL acl = dacl = NULL; if ((dvp->v_mount->mnt_flag & MNT_ACLS) != 0) { - acl = uma_zalloc(acl_zone, M_WAITOK); - dacl = uma_zalloc(acl_zone, M_WAITOK); + acl = acl_alloc(); + dacl = acl_alloc(); /* * Retrieve default ACL from parent, if any. @@ -1529,16 +1627,16 @@ */ ip->i_mode = dmode; DIP_SET(ip, i_mode, dmode); - uma_zfree(acl_zone, acl); - uma_zfree(acl_zone, dacl); + acl_free(acl); + acl_free(dacl); dacl = acl = NULL; break; default: UFS_VFREE(tvp, ip->i_number, dmode); vput(tvp); - uma_zfree(acl_zone, acl); - uma_zfree(acl_zone, dacl); + acl_free(acl); + acl_free(dacl); return (error); } } else { @@ -1608,15 +1706,22 @@ break; default: - uma_zfree(acl_zone, acl); - uma_zfree(acl_zone, dacl); + acl_free(acl); + acl_free(dacl); dacl = acl = NULL; goto bad; } - uma_zfree(acl_zone, acl); - uma_zfree(acl_zone, dacl); + acl_free(acl); + 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 */ /* @@ -1680,9 +1785,9 @@ } else { #ifdef UFS_ACL if (acl != NULL) - uma_zfree(acl_zone, acl); + acl_free(acl); if (dacl != NULL) - uma_zfree(acl_zone, dacl); + acl_free(dacl); #endif dp->i_effnlink--; dp->i_nlink--; @@ -2133,6 +2238,18 @@ *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) @@ -2316,7 +2433,7 @@ #ifdef UFS_ACL acl = NULL; if ((dvp->v_mount->mnt_flag & MNT_ACLS) != 0) { - acl = uma_zalloc(acl_zone, M_WAITOK); + acl = acl_alloc(); /* * Retrieve default ACL for parent, if any. @@ -2351,14 +2468,14 @@ */ ip->i_mode = mode; DIP_SET(ip, i_mode, mode); - uma_zfree(acl_zone, acl); + acl_free(acl); acl = NULL; break; default: UFS_VFREE(tvp, ip->i_number, mode); vput(tvp); - uma_zfree(acl_zone, acl); + acl_free(acl); acl = NULL; return (error); } @@ -2424,10 +2541,17 @@ break; default: - uma_zfree(acl_zone, acl); + acl_free(acl); goto bad; } - uma_zfree(acl_zone, acl); + 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); diff -urN 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 2008-11-21 11:14:05.000000000 +0100 @@ -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 > /dev/null + +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 -urN 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 2008-11-25 20:33:12.000000000 +0100 @@ -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 > /dev/null + +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 -urN 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 2008-11-21 11:14:05.000000000 +0100 @@ -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 > /dev/null + +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 -urN 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 2008-11-21 11:14:05.000000000 +0100 @@ -0,0 +1,198 @@ +#!/bin/sh +# This is an NFS4 ACL fuzzer. It expects to be run by non-root +# in a scratch directory on a filesystem with NFS4 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 -urN 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 2008-11-21 11:14:05.000000000 +0100 @@ -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 NFS4 ACLs, POSIX 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 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 to NFS4. +$ rm -f nfs4/xxx +$ mv posix/yyy nfs4/xxx +> mv: ACL brand of source is POSIX, but destination is NFS4; 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 NFS4 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 NFS4 to POSIX. +$ rm -f posix/xxx +$ mv nfs4/yyy posix/xxx +> mv: ACL brand of source is NFS4, but destination is POSIX; ACL not copied +$ ls -l posix/xxx | cut -d' ' -f1 +> -r--rwxrw- + +# mv from POSIX 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 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 to NFS4. +$ rm -f nfs4/xxx +$ cp -p posix/xxx nfs4/xxx +> cp: ACL brand of source is POSIX, but destination is NFS4; ACL not copied +$ ls -l nfs4/xxx | cut -d' ' -f1 +> -rw-rwxr-- + +# cp with NFS4 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 NFS4 to POSIX. +$ rm -f posix/xxx +$ cp -p nfs4/xxx posix/xxx +> cp: ACL brand of source is NFS4, but destination is POSIX; ACL not copied +$ ls -l posix/xxx | cut -d' ' -f1 +> -rw-rwxr-- + +$ cp -p nfs4/yyy none/xxx + diff -urN 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 2008-11-21 11:14:05.000000000 +0100 @@ -0,0 +1,802 @@ +# This is a tools-level test for NFS4 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 -urN current/tools/regression/acltools/tools-posix.test nfs4acl/tools/regression/acltools/tools-posix.test --- current/tools/regression/acltools/tools-posix.test 2008-11-25 21:23:30.000000000 +0100 +++ nfs4acl/tools/regression/acltools/tools-posix.test 2008-11-21 11:14:06.000000000 +0100 @@ -4,8 +4,6 @@ # /usr/src/tools/regression/acltools/run /usr/src/tools/regression/acltools/tools-posix.test # # WARNING: Creates files in unsafe way. -# -# $FreeBSD: src/tools/regression/acltools/tools-posix.test,v 1.1 2008/11/25 18:29:33 trasz Exp $ $ whoami > root @@ -13,7 +11,7 @@ # Smoke test for getfacl(1). $ touch xxx -$ getfacl xxx +$ getfacl -n xxx > # file: xxx > # owner: root > # group: wheel @@ -27,7 +25,7 @@ > other::r-- $ setfacl -m u:42:r,g:43:w xxx -$ getfacl xxx +$ getfacl -n xxx > # file: xxx > # owner: root > # group: wheel @@ -42,9 +40,9 @@ $ ls -l xxx | cut -d' ' -f1 > -rw-rw-r--+ -# Test removing entries. -$ setfacl -x user:42: xxx -$ getfacl xxx +# Test removing entries by number. +$ setfacl -x 1 xxx +$ getfacl -n xxx > # file: xxx > # owner: root > # group: wheel @@ -55,7 +53,7 @@ > other::r-- $ setfacl -m g:43:r xxx -$ getfacl xxx +$ getfacl -n xxx > # file: xxx > # owner: root > # group: wheel @@ -73,7 +71,7 @@ # 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 @@ -88,7 +86,7 @@ # 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 @@ -100,7 +98,7 @@ # Test setfacl -b. $ setfacl -b xxx -$ getfacl xxx +$ getfacl -n xxx > # file: xxx > # owner: root > # group: wheel @@ -113,7 +111,7 @@ > -rw-r--r--+ $ setfacl -nb xxx -$ getfacl xxx +$ getfacl -n xxx > # file: xxx > # owner: root > # group: wheel @@ -133,7 +131,7 @@ > -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 @@ -141,8 +139,8 @@ > -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-- @@ -165,7 +163,7 @@ > 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 @@ -179,7 +177,7 @@ $ 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 @@ -191,7 +189,7 @@ > other::--- $ chmod 060 xxx -$ getfacl xxx +$ getfacl -n xxx > # file: xxx > # owner: root > # group: wheel @@ -205,14 +203,14 @@ # Test default ACLs. $ umask 022 $ mkdir ddd -$ getfacl -q ddd +$ getfacl -qn ddd > user::rwx > group::r-x > other::r-x -$ getfacl -dq ddd +$ getfacl -dqn ddd $ setfacl -d -m u::rwx,g::rx,o::rx,mask::rwx ddd -$ getfacl -dq ddd +$ getfacl -dqn ddd > user::rwx > group::r-x > mask::rwx @@ -220,7 +218,7 @@ $ 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- @@ -229,7 +227,7 @@ > other::r-x $ setfacl -dx group:42: ddd -$ getfacl -dq ddd +$ getfacl -dqn ddd > user::rwx > user:42:r-- > group::-w- diff -urN current/tools/regression/fstest/Makefile nfs4acl/tools/regression/fstest/Makefile --- current/tools/regression/fstest/Makefile 2008-11-25 21:23:39.000000000 +0100 +++ nfs4acl/tools/regression/fstest/Makefile 2008-11-26 09:53:03.000000000 +0100 @@ -4,7 +4,7 @@ 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" diff -urN current/tools/regression/fstest/fstest.c nfs4acl/tools/regression/fstest/fstest.c --- current/tools/regression/fstest/fstest.c 2008-11-25 21:23:40.000000000 +0100 +++ nfs4acl/tools/regression/fstest/fstest.c 2008-11-26 09:53:03.000000000 +0100 @@ -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) @@ -76,6 +79,11 @@ ACTION_STAT, ACTION_LSTAT, ACTION_PATHCONF +#ifdef HAS_FREEBSD_ACL + ACTION_PREPENDACL, + ACTION_READACL, +#endif + ACTION_WRITE, }; #define TYPE_NONE 0x0000 @@ -118,6 +126,11 @@ { "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 @@ 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 @@ 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 -urN 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 2008-11-21 11:14:10.000000000 +0100 @@ -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 -urN 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 2008-11-21 11:14:10.000000000 +0100 @@ -0,0 +1,109 @@ +#!/bin/sh + +desc="NFS4 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 -urN 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 2008-11-21 11:14:10.000000000 +0100 @@ -0,0 +1,34 @@ +#!/bin/sh + +desc="NFS4 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 -urN 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 2008-11-21 11:14:10.000000000 +0100 @@ -0,0 +1,141 @@ +#!/bin/sh + +desc="NFS4 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 -urN 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 2008-11-21 11:14:10.000000000 +0100 @@ -0,0 +1,131 @@ +#!/bin/sh + +desc="NFS4 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 -urN 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 2008-11-26 18:28:24.000000000 +0100 @@ -0,0 +1,77 @@ +#!/bin/sh + +desc="NFS4 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 -urN 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 2008-11-21 11:14:10.000000000 +0100 @@ -0,0 +1,146 @@ +#!/bin/sh + +desc="NFS4 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}