23f80af2ca
Initially, 'acl' (an 'acl_t *') is allocated, and its ACCESS_ACL and DEFAULT_ACL fields are passed to the 'libc' ACL routines for subsequent allocation. If the '-m' option (merge existing ACL with a new one) is specified, then 'set_acl_mask()' will be called and passed one of the two ACLs. This function, in turn, replaces this given ACL structure by another, freshly allocated. However, the pointer in the 'acl' variable in the caller is not updated. The caller then proceeds to free the ACL, incurring in a double free condition. Submitted by: Pedro Martelletto <pedro at ambientworks.net> Approved by: rwatson (mentor)
281 lines
7.0 KiB
C
281 lines
7.0 KiB
C
/*-
|
|
* 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 <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/param.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/acl.h>
|
|
#include <sys/queue.h>
|
|
|
|
#include <err.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#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)
|
|
{
|
|
struct sf_file *file;
|
|
|
|
if (strlen(filename) > PATH_MAX - 1) {
|
|
warn("illegal filename");
|
|
return;
|
|
}
|
|
file = zmalloc(sizeof(struct sf_file));
|
|
file->filename = filename;
|
|
TAILQ_INSERT_TAIL(&filelist, file, next);
|
|
}
|
|
|
|
static acl_t *
|
|
get_file_acls(const char *filename)
|
|
{
|
|
acl_t *acl;
|
|
struct stat sb;
|
|
|
|
if (stat(filename, &sb) == -1) {
|
|
warn("stat() of %s failed", filename);
|
|
return (NULL);
|
|
}
|
|
|
|
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 (h_flag)
|
|
acl[DEFAULT_ACL] = acl_get_link_np(filename,
|
|
ACL_TYPE_DEFAULT);
|
|
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;
|
|
|
|
return (acl);
|
|
}
|
|
|
|
static void
|
|
usage(void)
|
|
{
|
|
|
|
fprintf(stderr, "usage: setfacl [-bdhkn] [-m entries] [-M file] "
|
|
"[-x entries] [-X file] [file ...]\n");
|
|
exit(1);
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
acl_t *acl, final_acl;
|
|
char filename[PATH_MAX];
|
|
int local_error, carried_error, ch, i;
|
|
struct sf_file *file;
|
|
struct sf_entry *entry;
|
|
const char *fn_dup;
|
|
|
|
acl_type = ACL_TYPE_ACCESS;
|
|
carried_error = local_error = 0;
|
|
h_flag = have_mask = have_stdin = n_flag = need_mask = 0;
|
|
|
|
TAILQ_INIT(&entrylist);
|
|
TAILQ_INIT(&filelist);
|
|
|
|
while ((ch = getopt(argc, argv, "M:X: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");
|
|
entry->op = OP_MERGE_ACL;
|
|
TAILQ_INSERT_TAIL(&entrylist, entry, next);
|
|
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 '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++;
|
|
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;
|
|
TAILQ_INSERT_TAIL(&entrylist, entry, next);
|
|
break;
|
|
default:
|
|
usage();
|
|
break;
|
|
}
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
if (n_flag == 0 && TAILQ_EMPTY(&entrylist))
|
|
usage();
|
|
|
|
/* take list of files from stdin */
|
|
if (argc == 0 || strcmp(argv[0], "-") == 0) {
|
|
if (have_stdin)
|
|
err(1, "cannot have more than one stdin");
|
|
have_stdin = 1;
|
|
bzero(&filename, sizeof(filename));
|
|
while (fgets(filename, (int)sizeof(filename), stdin)) {
|
|
/* remove the \n */
|
|
filename[strlen(filename) - 1] = '\0';
|
|
fn_dup = strdup(filename);
|
|
if (fn_dup == NULL)
|
|
err(1, "strdup() failed");
|
|
add_filename(fn_dup);
|
|
}
|
|
} else
|
|
for (i = 0; i < argc; i++)
|
|
add_filename(argv[i]);
|
|
|
|
/* cycle through each file */
|
|
TAILQ_FOREACH(file, &filelist, next) {
|
|
/* get our initial access and default ACL's */
|
|
acl = get_file_acls(file->filename);
|
|
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) {
|
|
if (local_error)
|
|
continue;
|
|
|
|
switch(entry->op) {
|
|
case OP_MERGE_ACL:
|
|
local_error += merge_acl(entry->acl, acl);
|
|
need_mask = 1;
|
|
break;
|
|
case OP_REMOVE_EXT:
|
|
remove_ext(acl);
|
|
need_mask = 0;
|
|
break;
|
|
case OP_REMOVE_DEF:
|
|
if (acl_delete_def_file(file->filename) == -1) {
|
|
warn("acl_delete_def_file() failed");
|
|
local_error++;
|
|
}
|
|
local_error += remove_default(acl);
|
|
need_mask = 0;
|
|
break;
|
|
case OP_REMOVE_ACL:
|
|
local_error += remove_acl(entry->acl, acl);
|
|
need_mask = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* don't bother setting the ACL if something is broken */
|
|
if (local_error) {
|
|
carried_error++;
|
|
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 (need_mask && (set_acl_mask(&final_acl) == -1)) {
|
|
warnx("failed to set ACL mask on %s", file->filename);
|
|
carried_error++;
|
|
} else if (h_flag) {
|
|
if (acl_set_link_np(file->filename, acl_type,
|
|
final_acl) == -1) {
|
|
carried_error++;
|
|
warn("acl_set_link_np() failed for %s",
|
|
file->filename);
|
|
}
|
|
} else {
|
|
if (acl_set_file(file->filename, acl_type,
|
|
final_acl) == -1) {
|
|
carried_error++;
|
|
warn("acl_set_file() failed for %s",
|
|
file->filename);
|
|
}
|
|
}
|
|
|
|
acl_free(final_acl);
|
|
free(acl);
|
|
}
|
|
|
|
return (carried_error);
|
|
}
|