diff options
Diffstat (limited to 'bin/setfacl')
-rw-r--r-- | bin/setfacl/Makefile | 7 | ||||
-rw-r--r-- | bin/setfacl/Makefile.depend | 17 | ||||
-rw-r--r-- | bin/setfacl/file.c | 76 | ||||
-rw-r--r-- | bin/setfacl/mask.c | 114 | ||||
-rw-r--r-- | bin/setfacl/merge.c | 293 | ||||
-rw-r--r-- | bin/setfacl/remove.c | 173 | ||||
-rw-r--r-- | bin/setfacl/setfacl.1 | 517 | ||||
-rw-r--r-- | bin/setfacl/setfacl.c | 505 | ||||
-rw-r--r-- | bin/setfacl/setfacl.h | 62 | ||||
-rw-r--r-- | bin/setfacl/util.c | 79 |
10 files changed, 1843 insertions, 0 deletions
diff --git a/bin/setfacl/Makefile b/bin/setfacl/Makefile new file mode 100644 index 000000000000..378541398e5b --- /dev/null +++ b/bin/setfacl/Makefile @@ -0,0 +1,7 @@ +# $FreeBSD$ + +PACKAGE=runtime +PROG= setfacl +SRCS= file.c mask.c merge.c remove.c setfacl.c util.c + +.include <bsd.prog.mk> diff --git a/bin/setfacl/Makefile.depend b/bin/setfacl/Makefile.depend new file mode 100644 index 000000000000..6cfaab1c3644 --- /dev/null +++ b/bin/setfacl/Makefile.depend @@ -0,0 +1,17 @@ +# $FreeBSD$ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + gnu/lib/csu \ + include \ + include/xlocale \ + lib/${CSU_DIR} \ + lib/libc \ + lib/libcompiler_rt \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/bin/setfacl/file.c b/bin/setfacl/file.c new file mode 100644 index 000000000000..f7e9672c3ce8 --- /dev/null +++ b/bin/setfacl/file.c @@ -0,0 +1,76 @@ +/*- + * 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 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/acl.h> + +#include <err.h> +#include <stdio.h> +#include <string.h> + +#include "setfacl.h" + +/* + * read acl text from a file and return the corresponding acl + */ +acl_t +get_acl_from_file(const char *filename) +{ + FILE *file; + size_t len; + char buf[BUFSIZ+1]; + + if (filename == NULL) + err(1, "(null) filename in get_acl_from_file()"); + + if (strcmp(filename, "-") == 0) { + if (have_stdin) + err(1, "cannot specify more than one stdin"); + file = stdin; + have_stdin = true; + } else { + file = fopen(filename, "r"); + if (file == NULL) + err(1, "fopen() %s failed", filename); + } + + len = fread(buf, (size_t)1, sizeof(buf) - 1, file); + buf[len] = '\0'; + if (ferror(file) != 0) { + fclose(file); + err(1, "error reading from %s", filename); + } else if (feof(file) == 0) { + fclose(file); + errx(1, "line too long in %s", filename); + } + + fclose(file); + + return (acl_from_text(buf)); +} diff --git a/bin/setfacl/mask.c b/bin/setfacl/mask.c new file mode 100644 index 000000000000..69ca52a2f2db --- /dev/null +++ b/bin/setfacl/mask.c @@ -0,0 +1,114 @@ +/*- + * Copyright (c) 2001-2002 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 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/acl.h> +#include <sys/stat.h> + +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> + +#include "setfacl.h" + +/* set the appropriate mask the given ACL's */ +int +set_acl_mask(acl_t *prev_acl, const char *filename) +{ + acl_entry_t entry; + acl_t acl; + acl_tag_t tag; + int entry_id; + + entry = NULL; + + /* + * ... if a mask entry is specified, then the permissions of the mask + * entry in the resulting ACL shall be set to the permissions in the + * specified ACL mask entry. + */ + if (have_mask) + return (0); + + acl = acl_dup(*prev_acl); + if (acl == NULL) + err(1, "%s: acl_dup() failed", filename); + + if (!n_flag) { + /* + * If no mask entry is specified and the -n option is not + * specified, then the permissions of the resulting ACL mask + * entry shall be set to the union of the permissions + * associated with all entries which belong to the file group + * class in the resulting ACL + */ + if (acl_calc_mask(&acl)) { + warn("%s: acl_calc_mask() failed", filename); + acl_free(acl); + return (-1); + } + } else { + /* + * If no mask entry is specified and the -n option is + * specified, then the permissions of the resulting ACL + * mask entry shall remain unchanged ... + */ + + entry_id = ACL_FIRST_ENTRY; + + while (acl_get_entry(acl, entry_id, &entry) == 1) { + entry_id = ACL_NEXT_ENTRY; + if (acl_get_tag_type(entry, &tag) == -1) + err(1, "%s: acl_get_tag_type() failed", + filename); + + if (tag == ACL_MASK) { + acl_free(acl); + return (0); + } + } + + /* + * If no mask entry is specified, the -n option is specified, + * and no ACL mask entry exists in the ACL associated with the + * file, then write an error message to standard error and + * continue with the next file. + */ + warnx("%s: warning: no mask entry", filename); + acl_free(acl); + return (0); + } + + acl_free(*prev_acl); + *prev_acl = acl_dup(acl); + acl_free(acl); + + return (0); +} diff --git a/bin/setfacl/merge.c b/bin/setfacl/merge.c new file mode 100644 index 000000000000..fe519dd3bff0 --- /dev/null +++ b/bin/setfacl/merge.c @@ -0,0 +1,293 @@ +/*- + * 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 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/acl.h> +#include <sys/stat.h> + +#include <err.h> +#include <stdio.h> + +#include "setfacl.h" + +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, int acl_brand) +{ + acl_permset_t permset; + acl_entry_type_t entry_type; + acl_flagset_t flagset; + int have_entry; + uid_t *id, *id_new; + + have_entry = 0; + + id = acl_get_qualifier(*entry); + if (id == NULL) + err(1, "acl_get_qualifier() failed"); + id_new = acl_get_qualifier(*entry_new); + if (id_new == NULL) + err(1, "acl_get_qualifier() failed"); + if (*id == *id_new) { + /* any other matches */ + if (acl_get_permset(*entry, &permset) == -1) + err(1, "acl_get_permset() failed"); + if (acl_set_permset(*entry_new, permset) == -1) + err(1, "acl_set_permset() failed"); + + if (acl_brand == ACL_BRAND_NFS4) { + if (acl_get_entry_type_np(*entry, &entry_type)) + err(1, "acl_get_entry_type_np() failed"); + if (acl_set_entry_type_np(*entry_new, entry_type)) + err(1, "acl_set_entry_type_np() failed"); + if (acl_get_flagset_np(*entry, &flagset)) + err(1, "acl_get_flagset_np() failed"); + if (acl_set_flagset_np(*entry_new, flagset)) + err(1, "acl_set_flagset_np() failed"); + } + + have_entry = 1; + } + acl_free(id); + acl_free(id_new); + + return (have_entry); +} + +/* + * merge an ACL into existing file's ACL + */ +int +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; + acl_entry_type_t entry_type, entry_type_new; + acl_flagset_t flagset; + int entry_id, entry_id_new, have_entry, had_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 (branding_mismatch(acl_brand, prev_acl_brand)) { + warnx("%s: branding mismatch; existing ACL is %s, " + "entry to be merged is %s", filename, + brand_name(prev_acl_brand), brand_name(acl_brand)); + 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; + have_entry = 0; + had_entry = 0; + + /* keep track of existing ACL_MASK entries */ + if (acl_get_tag_type(entry, &tag) == -1) + err(1, "%s: acl_get_tag_type() failed - " + "invalid ACL entry", filename); + if (tag == ACL_MASK) + have_mask = true; + + /* check against the existing ACL entries */ + entry_id_new = ACL_FIRST_ENTRY; + 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, "%s: acl_get_tag_type() failed", + filename); + if (acl_get_tag_type(entry_new, &tag_new) == -1) + err(1, "%s: acl_get_tag_type() failed", + filename); + if (tag != tag_new) + continue; + + /* + * For NFSv4, in addition to "tag" and "id" we also + * compare "entry_type". + */ + if (acl_brand == ACL_BRAND_NFS4) { + if (acl_get_entry_type_np(entry, &entry_type)) + err(1, "%s: acl_get_entry_type_np() " + "failed", filename); + if (acl_get_entry_type_np(entry_new, &entry_type_new)) + err(1, "%s: acl_get_entry_type_np() " + "failed", filename); + if (entry_type != entry_type_new) + continue; + } + + switch(tag) { + case ACL_USER: + case ACL_GROUP: + have_entry = merge_user_group(&entry, + &entry_new, acl_brand); + if (have_entry == 0) + break; + /* FALLTHROUGH */ + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + case ACL_OTHER: + case ACL_MASK: + case ACL_EVERYONE: + if (acl_get_permset(entry, &permset) == -1) + err(1, "%s: acl_get_permset() failed", + filename); + if (acl_set_permset(entry_new, permset) == -1) + err(1, "%s: acl_set_permset() failed", + filename); + + if (acl_brand == ACL_BRAND_NFS4) { + if (acl_get_entry_type_np(entry, &entry_type)) + err(1, "%s: acl_get_entry_type_np() failed", + filename); + if (acl_set_entry_type_np(entry_new, entry_type)) + err(1, "%s: acl_set_entry_type_np() failed", + filename); + if (acl_get_flagset_np(entry, &flagset)) + err(1, "%s: acl_get_flagset_np() failed", + filename); + if (acl_set_flagset_np(entry_new, flagset)) + err(1, "%s: acl_set_flagset_np() failed", + filename); + } + had_entry = have_entry = 1; + break; + default: + /* should never be here */ + errx(1, "%s: invalid tag type: %i", filename, tag); + break; + } + } + + /* if this entry has not been found, it must be new */ + if (had_entry == 0) { + + /* + * NFSv4 ACL entries must be prepended to the ACL. + * Appending them at the end makes no sense, since + * in most cases they wouldn't even get evaluated. + */ + if (acl_brand == ACL_BRAND_NFS4) { + if (acl_create_entry_np(&acl_new, &entry_new, entry_number) == -1) { + warn("%s: acl_create_entry_np() failed", filename); + acl_free(acl_new); + return (-1); + } + /* + * Without this increment, adding several + * entries at once, for example + * "setfacl -m user:1:r:allow,user:2:r:allow", + * would make them appear in reverse order. + */ + entry_number++; + } else { + if (acl_create_entry(&acl_new, &entry_new) == -1) { + warn("%s: acl_create_entry() failed", filename); + acl_free(acl_new); + return (-1); + } + } + if (acl_copy_entry(entry_new, entry) == -1) + err(1, "%s: acl_copy_entry() failed", filename); + } + } + + acl_free(*prev_acl); + *prev_acl = acl_new; + + return (0); +} + +int +add_acl(acl_t acl, uint entry_number, acl_t *prev_acl, const char *filename) +{ + acl_entry_t entry, entry_new; + acl_t acl_new; + int entry_id, acl_brand, prev_acl_brand; + + acl_get_brand_np(acl, &acl_brand); + acl_get_brand_np(*prev_acl, &prev_acl_brand); + + if (prev_acl_brand != ACL_BRAND_NFS4) { + warnx("%s: the '-a' option is only applicable to NFSv4 ACLs", + filename); + return (-1); + } + + if (branding_mismatch(acl_brand, ACL_BRAND_NFS4)) { + warnx("%s: branding mismatch; existing ACL is NFSv4, " + "entry to be added is %s", filename, + brand_name(acl_brand)); + 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) { + 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 --git a/bin/setfacl/remove.c b/bin/setfacl/remove.c new file mode 100644 index 000000000000..545562ac007e --- /dev/null +++ b/bin/setfacl/remove.c @@ -0,0 +1,173 @@ +/*- + * 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 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/acl.h> +#include <sys/stat.h> + +#include <err.h> +#include <stdio.h> +#include <string.h> + +#include "setfacl.h" + +/* + * remove ACL entries from an ACL + */ +int +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, 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 (branding_mismatch(acl_brand, prev_acl_brand)) { + warnx("%s: branding mismatch; existing ACL is %s, " + "entry to be removed is %s", filename, + brand_name(prev_acl_brand), brand_name(acl_brand)); + return (-1); + } + + 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 and delete the entry */ + entry_id = ACL_FIRST_ENTRY; + while (acl_get_entry(acl, entry_id, &entry) == 1) { + entry_id = ACL_NEXT_ENTRY; + if (acl_get_tag_type(entry, &tag) == -1) + err(1, "%s: acl_get_tag_type() failed", filename); + if (tag == ACL_MASK) + have_mask = true; + if (acl_delete_entry(acl_new, entry) == -1) { + carried_error++; + warnx("%s: cannot remove non-existent ACL entry", + filename); + } + } + + 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 = true; + } + + if (acl_delete_entry_np(acl_new, entry_number) == -1) { + carried_error++; + warn("%s: acl_delete_entry_np() failed", filename); + } + + acl_free(*prev_acl); + *prev_acl = acl_new; + + if (carried_error) + return (-1); + + return (0); +} + +/* + * remove default entries + */ +int +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); + + return (0); +} + +/* + * remove extended entries + */ +void +remove_ext(acl_t *prev_acl, const char *filename) +{ + acl_t acl_new; + + acl_new = acl_strip_np(*prev_acl, !n_flag); + if (acl_new == NULL) + err(1, "%s: acl_strip_np() failed", filename); + + acl_free(*prev_acl); + *prev_acl = acl_new; +} diff --git a/bin/setfacl/setfacl.1 b/bin/setfacl/setfacl.1 new file mode 100644 index 000000000000..31040a61558c --- /dev/null +++ b/bin/setfacl/setfacl.1 @@ -0,0 +1,517 @@ +.\"- +.\" Copyright (c) 2001 Chris D. Faulhaber +.\" Copyright (c) 2011 Edward Tomasz NapieraĆa +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd October 26, 2018 +.Dt SETFACL 1 +.Os +.Sh NAME +.Nm setfacl +.Nd set ACL information +.Sh SYNOPSIS +.Nm +.Op Fl R Op Fl H | L | P +.Op Fl bdhkn +.Op Fl a Ar position entries +.Op Fl m Ar entries +.Op Fl M Ar file +.Op Fl x Ar entries | position +.Op Fl X Ar file +.Op Ar +.Sh DESCRIPTION +The +.Nm +utility sets discretionary access control information on +the specified file(s). +If no files are specified, or the list consists of the only +.Sq Fl , +the file names are taken from the standard input. +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl a Ar position entries +Modify the ACL on the specified files by inserting new +ACL entries +specified in +.Ar entries , +starting at position +.Ar position , +counting from zero. +This option is only applicable to NFSv4 ACLs. +.It Fl b +Remove all ACL entries except for the ones synthesized +from the file mode - the three mandatory entries in case +of POSIX.1e ACL. +If the POSIX.1e ACL contains a +.Dq Li mask +entry, the permissions of the +.Dq Li group +entry in the resulting ACL will be set to the permission +associated with both the +.Dq Li group +and +.Dq Li mask +entries of the current ACL. +.It Fl d +The operations apply to the default ACL entries instead of +access ACL entries. +Currently only directories may have +default ACL's. +This option is not applicable to NFSv4 ACLs. +.It Fl h +If the target of the operation is a symbolic link, perform the operation +on the symbolic link itself, rather than following the link. +.It Fl H +If the +.Fl R +option is specified, symbolic links on the command line are followed +and hence unaffected by the command. +(Symbolic links encountered during tree traversal are not followed.) +.It Fl k +Delete any default ACL entries on the specified files. +It +is not considered an error if the specified files do not have +any default ACL entries. +An error will be reported if any of +the specified files cannot have a default entry (i.e., +non-directories). +This option is not applicable to NFSv4 ACLs. +.It Fl L +If the +.Fl R +option is specified, all symbolic links are followed. +.It Fl m Ar entries +Modify the ACL on the specified file. +New entries will be added, and existing entries will be modified +according to the +.Ar entries +argument. +For NFSv4 ACLs, it is recommended to use the +.Fl a +and +.Fl x +options instead. +.It Fl M Ar file +Modify the ACL entries on the specified files by adding new +ACL entries and modifying existing ACL entries with the ACL +entries specified in the file +.Ar file . +If +.Ar file +is +.Fl , +the input is taken from stdin. +.It Fl n +Do not recalculate the permissions associated with the ACL +mask entry. +This option is not applicable to NFSv4 ACLs. +.It Fl P +If the +.Fl R +option is specified, no symbolic links are followed. +This is the default. +.It Fl R +Perform the action recursively on any specified directories. +When modifying or adding NFSv4 ACL entries, inheritance flags +are applied only to directories. +.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 +from the access or default ACL of the specified files. +.El +.Pp +The above options are evaluated in the order specified +on the command-line. +.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 +.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 owner of the file or a specified user; +.Dq Li group +or +.Ql g +specifying the access granted to the file owning group +or a specified group; +.Dq Li other +or +.Ql o +specifying the access +granted to any process that does not match any user or group +ACL entry; +.Dq Li mask +or +.Ql m +specifying the maximum access +granted to any ACL entry except the +.Dq Li user +ACL entry for the file owner and the +.Dq Li other +ACL entry. +.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, gid or group name, or empty. +For +.Dq Li user +ACL entries, an empty field specifies access granted to the +file owner. +For +.Dq Li group +ACL entries, an empty field specifies access granted to the +file owning group. +.Dq Li mask +and +.Dq Li other +ACL entries do not use this field. +.It Ar "access permissions" +The access permissions field contains up to one of each of +the following: +.Ql r , +.Ql w , +and +.Ql x +to set read, write, and +execute permissions, respectively. +Each of these may be excluded +or replaced with a +.Ql - +character to indicate no access. +.El +.Pp +A +.Dq Li mask +ACL entry is required on a file with any ACL entries other than +the default +.Dq Li user , +.Dq Li group , +and +.Dq Li other +ACL entries. +If the +.Fl n +option is not specified and no +.Dq Li mask +ACL entry was specified, the +.Nm +utility +will apply a +.Dq Li mask +ACL entry consisting of the union of the permissions associated +with all +.Dq Li group +ACL entries in the resulting ACL. +.Pp +Traditional POSIX interfaces acting on file system object modes have +modified semantics in the presence of POSIX.1e extended ACLs. +When a mask entry is present on the access ACL of an object, the mask +entry is substituted for the group bits; this occurs in programs such +as +.Xr stat 1 +or +.Xr ls 1 . +When the mode is modified on an object that has a mask entry, the +changes applied to the group bits will actually be applied to the +mask entry. +These semantics provide for greater application compatibility: +applications modifying the mode instead of the ACL will see +conservative behavior, limiting the effective rights granted by all +of the additional user and group entries; this occurs in programs +such as +.Xr chmod 1 . +.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 +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, +.Dq Li group +ACL entries, and +.Dq Li other +ACL entry. +.Pp +Multiple ACL entries specified on the command line are +separated by commas. +.Pp +It is possible for files and directories to inherit ACL entries from their +parent directory. +This is accomplished through the use of the default ACL. +It should be noted that before you can specify a default ACL, the mandatory +ACL entries for user, group, other and mask must be set. +For more details see the examples below. +Default ACLs can be created by using +.Fl d . +.Sh NFSv4 ACL ENTRIES +An NFSv4 ACL entry contains four or five colon-separated fields: an ACL tag, +an ACL qualifier (only for +.Dq Li user +and +.Dq Li group +tags), discretionary access permissions, ACL inheritance flags, and ACL type: +.Bl -tag -width indent +.It Ar "ACL tag" +The ACL tag specifies the ACL entry type and consists of +one of the following: +.Dq Li user +or +.Ql u +specifying the access +granted to the specified user; +.Dq Li group +or +.Ql g +specifying the access granted to the specified group; +.Dq Li owner@ +specifying the access granted to the owner of the file; +.Dq Li group@ +specifying the access granted to the file owning group; +.Dq Li everyone@ +specifying everyone. +Note that +.Dq Li everyone@ +is not the same as traditional Unix +.Dq Li other +- it means, +literally, everyone, including file owner and owning group. +.It Ar "ACL qualifier" +The ACL qualifier field describes the user or group associated with +the ACL entry. +It may consist of one of the following: uid or +user name, or gid or group name. +In entries whose tag type is one of +.Dq Li owner@ , +.Dq Li group@ , +or +.Dq Li everyone@ , +this field is omitted altogether, including the trailing comma. +.It Ar "access permissions" +Access permissions may be specified in either short or long form. +Short and long forms may not be mixed. +Permissions in long form are separated by the +.Ql / +character; in short form, they are concatenated together. +Valid permissions are: +.Bl -tag -width ".Dv modify_set" +.It Short +Long +.It r +read_data +.It w +write_data +.It x +execute +.It p +append_data +.It D +delete_child +.It d +delete +.It a +read_attributes +.It A +write_attributes +.It R +read_xattr +.It W +write_xattr +.It c +read_acl +.It C +write_acl +.It o +write_owner +.It s +synchronize +.El +.Pp +In addition, the following permission sets may be used: +.Bl -tag -width ".Dv modify_set" +.It Set +Permissions +.It full_set +all permissions, as shown above +.It modify_set +all permissions except write_acl and write_owner +.It read_set +read_data, read_attributes, read_xattr and read_acl +.It write_set +write_data, append_data, write_attributes and write_xattr +.El +.It Ar "ACL inheritance flags" +Inheritance flags may be specified in either short or long form. +Short and long forms may not be mixed. +Access flags in long form are separated by the +.Ql / +character; in short form, they are concatenated together. +Valid inheritance flags are: +.Bl -tag -width ".Dv short" +.It Short +Long +.It f +file_inherit +.It d +dir_inherit +.It i +inherit_only +.It n +no_propagate +.It I +inherited +.El +.Pp +Other than the "inherited" flag, inheritance flags may be only set on directories. +.It Ar "ACL type" +The ACL type field is either +.Dq Li allow +or +.Dq Li deny . +.El +.Pp +ACL entries applied from a file using the +.Fl M +or +.Fl X +options shall be of the following form: one ACL entry per line, as +previously specified; whitespace is ignored; any text after a +.Ql # +is ignored (comments). +.Pp +NFSv4 ACL entries are evaluated in their visible order. +.Pp +Multiple ACL entries specified on the command line are +separated by commas. +.Pp +Note that the file owner is always granted the read_acl, write_acl, +read_attributes, and write_attributes permissions, even if the ACL +would deny it. +.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 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 +inherit these default ACLs upon creation. +.Pp +.Dl setfacl -m u::rwx,g:mail:rw file +.Pp +Sets read, write, and execute permissions for the +.Pa file +owner's POSIX.1e ACL entry and read and write permissions for group mail on +.Pa file . +.Pp +.Dl setfacl -m owner@:rwxp::allow,g:mail:rwp::allow file +.Pp +Semantically equal to the example above, but for NFSv4 ACL. +.Pp +.Dl setfacl -M file1 file2 +.Pp +Sets/updates the ACL entries contained in +.Pa file1 +on +.Pa file2 . +.Pp +.Dl setfacl -x g:mail:rw file +.Pp +Remove the group mail POSIX.1e ACL entry containing read/write permissions +from +.Pa file . +.Pp +.Dl setfacl -x0 file +.Pp +Remove the first entry from the NFSv4 ACL from +.Pa file . +.Pp +.Dl setfacl -bn file +.Pp +Remove all +.Dq Li access +ACL entries except for the three required from +.Pa file . +.Pp +.Dl getfacl file1 | setfacl -b -n -M - file2 +.Pp +Copy ACL entries from +.Pa file1 +to +.Pa file2 . +.Sh SEE ALSO +.Xr getfacl 1 , +.Xr acl 3 , +.Xr getextattr 8 , +.Xr setextattr 8 , +.Xr acl 9 , +.Xr extattr 9 +.Sh STANDARDS +The +.Nm +utility is expected to be +.Tn IEEE +Std 1003.2c compliant. +.Sh HISTORY +Extended Attribute and Access Control List support was developed +as part of the +.Tn TrustedBSD +Project and introduced in +.Fx 5.0 . +NFSv4 ACL support was introduced in +.Fx 8.1 . +.Sh AUTHORS +.An -nosplit +The +.Nm +utility was written by +.An Chris D. Faulhaber Aq Mt jedgar@fxp.org . +NFSv4 ACL support was implemented by +.An Edward Tomasz Napierala Aq Mt trasz@FreeBSD.org . diff --git a/bin/setfacl/setfacl.c b/bin/setfacl/setfacl.c new file mode 100644 index 000000000000..7b0d617812dc --- /dev/null +++ b/bin/setfacl/setfacl.c @@ -0,0 +1,505 @@ +/*- + * 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 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/acl.h> +#include <sys/queue.h> + +#include <err.h> +#include <errno.h> +#include <fts.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "setfacl.h" + +/* file operations */ +#define OP_MERGE_ACL 0x00 /* merge acl's (-mM) */ +#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) */ +#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; +}; +static TAILQ_HEAD(, sf_entry) entrylist; + +bool have_mask; +bool have_stdin; +bool n_flag; +static bool h_flag; +static bool H_flag; +static bool L_flag; +static bool R_flag; +static bool need_mask; +static acl_type_t acl_type = ACL_TYPE_ACCESS; + +static int handle_file(FTS *ftsp, FTSENT *file); +static acl_t clear_inheritance_flags(acl_t acl); +static char **stdin_files(void); +static void usage(void); + +static void +usage(void) +{ + + fprintf(stderr, "usage: setfacl [-R [-H | -L | -P]] [-bdhkn] " + "[-a position entries] [-m entries] [-M file] " + "[-x entries] [-X file] [file ...]\n"); + exit(1); +} + +static char ** +stdin_files(void) +{ + char **files_list; + char filename[PATH_MAX]; + size_t fl_count, i; + + if (have_stdin) + err(1, "cannot have more than one stdin"); + + i = 0; + have_stdin = true; + bzero(&filename, sizeof(filename)); + /* Start with an array size sufficient for basic cases. */ + fl_count = 1024; + files_list = zmalloc(fl_count * sizeof(char *)); + while (fgets(filename, (int)sizeof(filename), stdin)) { + /* remove the \n */ + filename[strlen(filename) - 1] = '\0'; + files_list[i] = strdup(filename); + if (files_list[i] == NULL) + err(1, "strdup() failed"); + /* Grow array if necessary. */ + if (++i == fl_count) { + fl_count <<= 1; + if (fl_count > SIZE_MAX / sizeof(char *)) + errx(1, "Too many input files"); + files_list = zrealloc(files_list, + fl_count * sizeof(char *)); + } + } + + /* fts_open() requires the last array element to be NULL. */ + files_list[i] = NULL; + + return (files_list); +} + +/* + * Remove any inheritance flags from NFSv4 ACLs when running in recursive + * mode. This is to avoid files being assigned identical ACLs to their + * parent directory while also being set to inherit them. + * + * The acl argument is assumed to be valid. + */ +static acl_t +clear_inheritance_flags(acl_t acl) +{ + acl_t nacl; + acl_entry_t acl_entry; + acl_flagset_t acl_flagset; + int acl_brand, entry_id; + + (void)acl_get_brand_np(acl, &acl_brand); + if (acl_brand != ACL_BRAND_NFS4) + return (acl); + + nacl = acl_dup(acl); + if (nacl == NULL) { + warn("acl_dup() failed"); + return (acl); + } + + entry_id = ACL_FIRST_ENTRY; + while (acl_get_entry(nacl, entry_id, &acl_entry) == 1) { + entry_id = ACL_NEXT_ENTRY; + if (acl_get_flagset_np(acl_entry, &acl_flagset) != 0) { + warn("acl_get_flagset_np() failed"); + continue; + } + if (acl_get_flag_np(acl_flagset, ACL_ENTRY_INHERIT_ONLY) == 1) { + if (acl_delete_entry(nacl, acl_entry) != 0) + warn("acl_delete_entry() failed"); + continue; + } + if (acl_delete_flag_np(acl_flagset, + ACL_ENTRY_FILE_INHERIT | + ACL_ENTRY_DIRECTORY_INHERIT | + ACL_ENTRY_NO_PROPAGATE_INHERIT) != 0) + warn("acl_delete_flag_np() failed"); + } + + return (nacl); +} + +static int +handle_file(FTS *ftsp, FTSENT *file) +{ + acl_t acl, nacl; + acl_entry_t unused_entry; + int local_error, ret; + struct sf_entry *entry; + bool follow_symlink; + + local_error = 0; + switch (file->fts_info) { + case FTS_D: + /* Do not recurse if -R not specified. */ + if (!R_flag) + fts_set(ftsp, file, FTS_SKIP); + break; + case FTS_DP: + /* Skip the second visit to a directory. */ + return (0); + case FTS_DNR: + case FTS_ERR: + warnx("%s: %s", file->fts_path, strerror(file->fts_errno)); + return (0); + default: + break; + } + + if (acl_type == ACL_TYPE_DEFAULT && file->fts_info != FTS_D) { + warnx("%s: default ACL may only be set on a directory", + file->fts_path); + return (1); + } + + follow_symlink = (!R_flag && !h_flag) || (R_flag && L_flag) || + (R_flag && H_flag && file->fts_level == FTS_ROOTLEVEL); + + if (follow_symlink) + ret = pathconf(file->fts_accpath, _PC_ACL_NFS4); + else + ret = lpathconf(file->fts_accpath, _PC_ACL_NFS4); + if (ret > 0) { + if (acl_type == ACL_TYPE_DEFAULT) { + warnx("%s: there are no default entries in NFSv4 ACLs", + file->fts_path); + return (1); + } + acl_type = ACL_TYPE_NFS4; + } else if (ret == 0) { + if (acl_type == ACL_TYPE_NFS4) + acl_type = ACL_TYPE_ACCESS; + } else if (ret < 0 && errno != EINVAL && errno != ENOENT) { + warn("%s: pathconf(_PC_ACL_NFS4) failed", + file->fts_path); + } + + if (follow_symlink) + acl = acl_get_file(file->fts_accpath, acl_type); + else + acl = acl_get_link_np(file->fts_accpath, acl_type); + if (acl == NULL) { + if (follow_symlink) + warn("%s: acl_get_file() failed", file->fts_path); + else + warn("%s: acl_get_link_np() failed", file->fts_path); + return (1); + } + + /* Cycle through each option. */ + TAILQ_FOREACH(entry, &entrylist, next) { + nacl = entry->acl; + switch (entry->op) { + case OP_ADD_ACL: + if (R_flag && file->fts_info != FTS_D && + acl_type == ACL_TYPE_NFS4) + nacl = clear_inheritance_flags(nacl); + local_error += add_acl(nacl, entry->entry_number, &acl, + file->fts_path); + break; + case OP_MERGE_ACL: + if (R_flag && file->fts_info != FTS_D && + acl_type == ACL_TYPE_NFS4) + nacl = clear_inheritance_flags(nacl); + local_error += merge_acl(nacl, &acl, file->fts_path); + need_mask = true; + break; + case OP_REMOVE_EXT: + /* + * Don't try to call remove_ext() for empty + * default ACL. + */ + if (acl_type == ACL_TYPE_DEFAULT && + acl_get_entry(acl, ACL_FIRST_ENTRY, + &unused_entry) == 0) { + local_error += remove_default(&acl, + file->fts_path); + break; + } + remove_ext(&acl, file->fts_path); + need_mask = false; + break; + case OP_REMOVE_DEF: + if (acl_type == ACL_TYPE_NFS4) { + warnx("%s: there are no default entries in " + "NFSv4 ACLs; cannot remove", + file->fts_path); + local_error++; + break; + } + if (acl_delete_def_file(file->fts_accpath) == -1) { + warn("%s: acl_delete_def_file() failed", + file->fts_path); + local_error++; + } + if (acl_type == ACL_TYPE_DEFAULT) + local_error += remove_default(&acl, + file->fts_path); + need_mask = false; + break; + case OP_REMOVE_ACL: + local_error += remove_acl(nacl, &acl, file->fts_path); + need_mask = true; + break; + case OP_REMOVE_BY_NUMBER: + local_error += remove_by_number(entry->entry_number, + &acl, file->fts_path); + need_mask = true; + break; + } + + if (nacl != entry->acl) { + acl_free(nacl); + nacl = NULL; + } + if (local_error) + break; + } + + ret = 0; + + /* + * Don't try to set an empty default ACL; it will always fail. + * Use acl_delete_def_file(3) instead. + */ + if (acl_type == ACL_TYPE_DEFAULT && + acl_get_entry(acl, ACL_FIRST_ENTRY, &unused_entry) == 0) { + if (acl_delete_def_file(file->fts_accpath) == -1) { + warn("%s: acl_delete_def_file() failed", + file->fts_path); + ret = 1; + } + goto out; + } + + /* Don't bother setting the ACL if something is broken. */ + if (local_error) { + ret = 1; + } else if (acl_type != ACL_TYPE_NFS4 && need_mask && + set_acl_mask(&acl, file->fts_path) == -1) { + warnx("%s: failed to set ACL mask", file->fts_path); + ret = 1; + } else if (follow_symlink) { + if (acl_set_file(file->fts_accpath, acl_type, acl) == -1) { + warn("%s: acl_set_file() failed", file->fts_path); + ret = 1; + } + } else { + if (acl_set_link_np(file->fts_accpath, acl_type, acl) == -1) { + warn("%s: acl_set_link_np() failed", file->fts_path); + ret = 1; + } + } + +out: + acl_free(acl); + return (ret); +} + +int +main(int argc, char *argv[]) +{ + int carried_error, ch, entry_number, fts_options; + FTS *ftsp; + FTSENT *file; + char **files_list; + struct sf_entry *entry; + char *end; + + acl_type = ACL_TYPE_ACCESS; + carried_error = fts_options = 0; + have_mask = have_stdin = n_flag = false; + + TAILQ_INIT(&entrylist); + + while ((ch = getopt(argc, argv, "HLM:PRX:a:bdhkm:nx:")) != -1) + switch(ch) { + case 'H': + H_flag = true; + L_flag = false; + break; + case 'L': + L_flag = true; + H_flag = false; + break; + case 'M': + entry = zmalloc(sizeof(struct sf_entry)); + entry->acl = get_acl_from_file(optarg); + if (entry->acl == NULL) + err(1, "%s: get_acl_from_file() failed", + optarg); + entry->op = OP_MERGE_ACL; + TAILQ_INSERT_TAIL(&entrylist, entry, next); + break; + case 'P': + H_flag = L_flag = false; + break; + case 'R': + R_flag = true; + break; + case 'X': + entry = zmalloc(sizeof(struct sf_entry)); + entry->acl = get_acl_from_file(optarg); + 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; + TAILQ_INSERT_TAIL(&entrylist, entry, next); + break; + case 'd': + acl_type = ACL_TYPE_DEFAULT; + break; + case 'h': + h_flag = 1; + break; + case 'k': + entry = zmalloc(sizeof(struct sf_entry)); + entry->op = OP_REMOVE_DEF; + TAILQ_INSERT_TAIL(&entrylist, entry, next); + break; + case 'm': + entry = zmalloc(sizeof(struct sf_entry)); + entry->acl = acl_from_text(optarg); + if (entry->acl == NULL) + err(1, "%s", optarg); + entry->op = OP_MERGE_ACL; + TAILQ_INSERT_TAIL(&entrylist, entry, next); + break; + case 'n': + n_flag = true; + break; + case 'x': + entry = zmalloc(sizeof(struct sf_entry)); + 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: + usage(); + break; + } + argc -= optind; + argv += optind; + + if (!n_flag && TAILQ_EMPTY(&entrylist)) + usage(); + + /* Take list of files from stdin. */ + if (argc == 0 || strcmp(argv[0], "-") == 0) { + files_list = stdin_files(); + } else + files_list = argv; + + if (R_flag) { + if (h_flag) + errx(1, "the -R and -h options may not be " + "specified together."); + if (L_flag) { + fts_options = FTS_LOGICAL; + } else { + fts_options = FTS_PHYSICAL; + + if (H_flag) { + fts_options |= FTS_COMFOLLOW; + } + } + } else if (h_flag) { + fts_options = FTS_PHYSICAL; + } else { + fts_options = FTS_LOGICAL; + } + + /* Open all files. */ + if ((ftsp = fts_open(files_list, fts_options | FTS_NOSTAT, 0)) == NULL) + err(1, "fts_open"); + while ((file = fts_read(ftsp)) != NULL) + carried_error += handle_file(ftsp, file); + + return (carried_error); +} diff --git a/bin/setfacl/setfacl.h b/bin/setfacl/setfacl.h new file mode 100644 index 000000000000..5ea794d44c74 --- /dev/null +++ b/bin/setfacl/setfacl.h @@ -0,0 +1,62 @@ +/*- + * 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 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$ + */ + +#ifndef _SETFACL_H +#define _SETFACL_H + +#include <stdbool.h> + +#include <sys/types.h> +#include <sys/acl.h> +#include <sys/queue.h> + +/* files.c */ +acl_t get_acl_from_file(const char *filename); +/* merge.c */ +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, 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, const char *filename); +/* util.c */ +void *zmalloc(size_t size); +void *zrealloc(void *ptr, size_t size); +const char *brand_name(int brand); +int branding_mismatch(int brand1, int brand2); + +extern bool have_mask; +extern bool have_stdin; +extern bool n_flag; + +#endif /* _SETFACL_H */ diff --git a/bin/setfacl/util.c b/bin/setfacl/util.c new file mode 100644 index 000000000000..969ce3286852 --- /dev/null +++ b/bin/setfacl/util.c @@ -0,0 +1,79 @@ +/*- + * 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 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <err.h> +#include <stdlib.h> +#include <string.h> + +#include "setfacl.h" + +void * +zmalloc(size_t size) +{ + void *ptr; + + ptr = calloc(1, size); + if (ptr == NULL) + err(1, "calloc() failed"); + return (ptr); +} + +void * +zrealloc(void *ptr, size_t size) +{ + void *newptr; + + newptr = realloc(ptr, size); + if (newptr == NULL) + err(1, "realloc() failed"); + return (newptr); +} + +const char * +brand_name(int brand) +{ + switch (brand) { + case ACL_BRAND_NFS4: + return "NFSv4"; + case ACL_BRAND_POSIX: + return "POSIX.1e"; + default: + return "unknown"; + } +} + +int +branding_mismatch(int brand1, int brand2) +{ + if (brand1 == ACL_BRAND_UNKNOWN || brand2 == ACL_BRAND_UNKNOWN) + return (0); + if (brand1 != brand2) + return (1); + return (0); +} |