Add SELinux context store/restore/list support.

* gnulib.modules: Add selinux-at.
* src/Makefile.am (tar_LDADD): Add LIB_SELINUX.

* src/create.c (start_header, dump_file0): Handle selinux contexts.
* src/extract.c (delayed_set_stat) <cntx_name>: New member.
(delayed_link) <cntx_name>: New member.
(set_stat, delay_set_stat)
(apply_nonancestor_delayed_set_stat): Handle selinux contexts.
* src/tar.c: New options: "--selinux", "--no-selinux".
(tar_stat_destroy): Free cntx_name.
* src/tar.h (tar_stat_info) <cntx_name>: New member.
* src/xattrs.c (xattrs_selinux_get)
(xattrs_selinux_set): New functions.
(xattrs_print_char): Honor selinux_context_option.
(xattrs_print): Print selinux context.
* src/xheader.c: Handle new keyword "RHT.security.selinux".
* tests/Makefile.am: Add new tests.
* tests/testsuite.at: Likewise.
* tests/selacl01.at: New test.
* tests/selnx01.at: New test.
This commit is contained in:
Pavel Raiskup
2012-11-18 22:28:01 +02:00
committed by Sergey Poznyakoff
parent d36f5a3cc3
commit 085cace180
12 changed files with 309 additions and 2 deletions

View File

@@ -50,6 +50,7 @@ root-uid
rpmatch
safe-read
savedir
selinux-at
setenv
snprintf
stat-time

View File

@@ -49,4 +49,4 @@ INCLUDES = -I$(top_srcdir)/gnu -I../ -I../gnu -I$(top_srcdir)/lib -I../lib
LDADD = ../lib/libtar.a ../gnu/libgnu.a $(LIBINTL) $(LIBICONV)
tar_LDADD = $(LIBS) $(LDADD) $(LIB_CLOCK_GETTIME) $(LIB_EACCESS)
tar_LDADD = $(LIBS) $(LDADD) $(LIB_CLOCK_GETTIME) $(LIB_EACCESS) $(LIB_SELINUX)

View File

@@ -953,6 +953,8 @@ start_header (struct tar_stat_info *st)
if (st->acls_d_ptr)
xheader_store ("SCHILY.acl.default", st, NULL);
}
if ((selinux_context_option > 0) && st->cntx_name)
xheader_store ("RHT.security.selinux", st, NULL);
if (xattrs_option > 0)
{
size_t scan_xattr = 0;
@@ -1742,6 +1744,7 @@ dump_file0 (struct tar_stat_info *st, char const *name, char const *p)
struct stat final_stat;
xattrs_acls_get (parentfd, name, st, 0, !is_dir);
xattrs_selinux_get (parentfd, name, st, fd);
xattrs_xattrs_get (parentfd, name, st, fd);
if (is_dir)
@@ -1862,6 +1865,7 @@ dump_file0 (struct tar_stat_info *st, char const *name, char const *p)
if (NAME_FIELD_SIZE - (archive_format == OLDGNU_FORMAT) < size)
write_long_link (st);
xattrs_selinux_get (parentfd, name, st, 0);
xattrs_xattrs_get (parentfd, name, st, 0);
block_ordinal = current_block_ordinal ();
@@ -1885,18 +1889,21 @@ dump_file0 (struct tar_stat_info *st, char const *name, char const *p)
{
type = CHRTYPE;
xattrs_acls_get (parentfd, name, st, 0, true);
xattrs_selinux_get (parentfd, name, st, 0);
xattrs_xattrs_get (parentfd, name, st, 0);
}
else if (S_ISBLK (st->stat.st_mode))
{
type = BLKTYPE;
xattrs_acls_get (parentfd, name, st, 0, true);
xattrs_selinux_get (parentfd, name, st, 0);
xattrs_xattrs_get (parentfd, name, st, 0);
}
else if (S_ISFIFO (st->stat.st_mode))
{
type = FIFOTYPE;
xattrs_acls_get (parentfd, name, st, 0, true);
xattrs_selinux_get (parentfd, name, st, 0);
xattrs_xattrs_get (parentfd, name, st, 0);
}
else if (S_ISSOCK (st->stat.st_mode))

View File

@@ -100,6 +100,7 @@ struct delayed_set_stat
int change_dir;
/* extended attributes*/
char *cntx_name;
char *acls_a_ptr;
size_t acls_a_len;
char *acls_d_ptr;
@@ -146,6 +147,9 @@ struct delayed_link
hard-linked together. */
struct string_list *sources;
/* SELinux context */
char *cntx_name;
/* ACLs */
char *acls_a_ptr;
size_t acls_a_len;
@@ -386,6 +390,7 @@ set_stat (char const *file_name,
causes that linux capabilities becomes cleared. */
xattrs_xattrs_set (st, file_name, typeflag, 1);
xattrs_acls_set (st, file_name, typeflag);
xattrs_selinux_set (st, file_name, typeflag);
}
/* For each entry H in the leading prefix of entries in HEAD that do
@@ -457,6 +462,9 @@ delay_set_stat (char const *file_name, struct tar_stat_info const *st,
data->atflag = atflag;
data->after_links = 0;
data->change_dir = chdir_current;
data->cntx_name = NULL;
if (st)
assign_string (&data->cntx_name, st->cntx_name);
if (st && st->acls_a_ptr)
{
data->acls_a_ptr = xmemdup (st->acls_a_ptr, st->acls_a_len + 1);
@@ -825,6 +833,7 @@ apply_nonancestor_delayed_set_stat (char const *file_name, bool after_links)
sb.stat.st_gid = data->gid;
sb.atime = data->atime;
sb.mtime = data->mtime;
sb.cntx_name = data->cntx_name;
sb.acls_a_ptr = data->acls_a_ptr;
sb.acls_a_len = data->acls_a_len;
sb.acls_d_ptr = data->acls_d_ptr;
@@ -838,6 +847,7 @@ apply_nonancestor_delayed_set_stat (char const *file_name, bool after_links)
delayed_set_stat_head = data->next;
xheader_xattr_free (data->xattr_map, data->xattr_map_size);
free (data->cntx_name);
free (data->acls_a_ptr);
free (data->acls_d_ptr);
free (data);
@@ -1210,6 +1220,8 @@ create_placeholder_file (char *file_name, bool is_symlink, bool *interdir_made)
+ strlen (file_name) + 1);
p->sources->next = 0;
strcpy (p->sources->string, file_name);
p->cntx_name = NULL;
assign_string (&p->cntx_name, current_stat_info.cntx_name);
p->acls_a_ptr = NULL;
p->acls_a_len = 0;
p->acls_d_ptr = NULL;
@@ -1650,6 +1662,7 @@ apply_delayed_links (void)
st1.stat.st_gid = ds->gid;
st1.atime = ds->atime;
st1.mtime = ds->mtime;
st1.cntx_name = ds->cntx_name;
st1.acls_a_ptr = ds->acls_a_ptr;
st1.acls_a_len = ds->acls_a_len;
st1.acls_d_ptr = ds->acls_d_ptr;
@@ -1671,6 +1684,7 @@ apply_delayed_links (void)
}
xheader_xattr_free (ds->xattr_map, ds->xattr_map_size);
free (ds->cntx_name);
{
struct delayed_link *next = ds->next;

View File

@@ -304,6 +304,7 @@ enum
NO_SAME_OWNER_OPTION,
NO_SAME_PERMISSIONS_OPTION,
NO_SEEK_OPTION,
NO_SELINUX_CONTEXT_OPTION,
NO_UNQUOTE_OPTION,
NO_WILDCARDS_MATCH_SLASH_OPTION,
NO_WILDCARDS_OPTION,
@@ -329,6 +330,7 @@ enum
RMT_COMMAND_OPTION,
RSH_COMMAND_OPTION,
SAME_OWNER_OPTION,
SELINUX_CONTEXT_OPTION,
SHOW_DEFAULTS_OPTION,
SHOW_OMITTED_DIRS_OPTION,
SHOW_TRANSFORMED_NAMES_OPTION,
@@ -549,6 +551,10 @@ static struct argp_option options[] = {
N_("specify the include pattern for xattr keys"), GRID+1 },
{"xattrs-exclude", XATTR_EXCLUDE, N_("MASK"), 0,
N_("specify the exclude pattern for xattr keys"), GRID+1 },
{"selinux", SELINUX_CONTEXT_OPTION, 0, 0,
N_("Enable the SELinux context support"), GRID+1 },
{"no-selinux", NO_SELINUX_CONTEXT_OPTION, 0, 0,
N_("Disable the SELinux context support"), GRID+1 },
{"acls", ACLS_OPTION, 0, 0,
N_("Enable the POSIX ACLs support"), GRID+1 },
{"no-acls", NO_ACLS_OPTION, 0, 0,
@@ -2184,6 +2190,15 @@ parse_opt (int key, char *arg, struct argp_state *state)
acls_option = -1;
break;
case SELINUX_CONTEXT_OPTION:
set_archive_format ("posix");
selinux_context_option = 1;
break;
case NO_SELINUX_CONTEXT_OPTION:
selinux_context_option = -1;
break;
case XATTR_OPTION:
set_archive_format ("posix");
xattrs_option = 1;
@@ -2585,6 +2600,11 @@ decode_options (int argc, char **argv)
&& !READ_LIKE_SUBCOMMAND)
USAGE_ERROR ((0, 0, _("--acls can be used only on POSIX archives")));
if ((selinux_context_option > 0)
&& archive_format != POSIX_FORMAT
&& !READ_LIKE_SUBCOMMAND)
USAGE_ERROR ((0, 0, _("--selinux can be used only on POSIX archives")));
if ((xattrs_option > 0)
&& archive_format != POSIX_FORMAT
&& !READ_LIKE_SUBCOMMAND)
@@ -2849,6 +2869,7 @@ tar_stat_destroy (struct tar_stat_info *st)
free (st->link_name);
free (st->uname);
free (st->gname);
free (st->cntx_name);
free (st->acls_a_ptr);
free (st->acls_d_ptr);
free (st->sparse_map);

View File

@@ -297,6 +297,8 @@ struct tar_stat_info
char *uname; /* user name of owner */
char *gname; /* group name of owner */
char *cntx_name; /* SELinux context for the current archive entry. */
char *acls_a_ptr; /* Access ACLs for the current archive entry. */
size_t acls_a_len; /* Access ACLs for the current archive entry. */

View File

@@ -28,6 +28,7 @@
#include "common.h"
#include "xattr-at.h"
#include "selinux-at.h"
struct xattrs_mask_map
{
@@ -490,6 +491,64 @@ static void xattrs__fd_set (struct tar_stat_info const *st,
}
}
/* lgetfileconat is called against FILE_NAME iff the FD parameter is set to
zero, otherwise the fgetfileconat is used against correct file descriptor */
void xattrs_selinux_get (int parentfd, char const *file_name,
struct tar_stat_info *st, int fd)
{
if (selinux_context_option > 0)
{
#if HAVE_SELINUX_SELINUX_H != 1
static int done = 0;
if (!done)
WARN ((0, 0, _("SELinux support is not available")));
done = 1;
#else
int result = (fd ? fgetfilecon (fd, &st->cntx_name)
: lgetfileconat (parentfd, file_name, &st->cntx_name));
if (result == -1 && errno != ENODATA && errno != ENOTSUP)
call_arg_warn (fd ? "fgetfilecon" : "lgetfileconat", file_name);
#endif
}
}
void xattrs_selinux_set (struct tar_stat_info const *st,
char const *file_name, char typeflag)
{
if (selinux_context_option > 0)
{
#if HAVE_SELINUX_SELINUX_H != 1
static int done = 0;
if (!done)
WARN ((0, 0, _("SELinux support is not available")));
done = 1;
#else
const char *sysname = "setfilecon";
int ret;
if (!st->cntx_name)
return;
if (typeflag != SYMTYPE)
{
ret = setfileconat (chdir_fd, file_name, st->cntx_name);
sysname = "setfileconat";
}
else
{
ret = lsetfileconat (chdir_fd, file_name, st->cntx_name);
sysname = "lsetfileconat";
}
if (ret == -1)
WARNOPT (WARN_XATTR_WRITE, (0, errno,
_("%s: Cannot set SELinux context for file '%s'"), sysname,
file_name));
#endif
}
}
static bool xattrs_matches_mask (const char *kw, struct xattrs_mask_map *mm)
{
int i;
@@ -589,7 +648,7 @@ void xattrs_print_char (struct tar_stat_info const *st, char *output)
return;
}
if (xattrs_option > 0 || acls_option > 0)
if (xattrs_option > 0 || selinux_context_option > 0 || acls_option > 0)
{
/* placeholders */
*output = ' ';
@@ -606,6 +665,9 @@ void xattrs_print_char (struct tar_stat_info const *st, char *output)
break;
}
if (selinux_context_option > 0 && st->cntx_name)
*output = '.';
if (acls_option && (st->acls_a_len || st->acls_d_len))
*output = '+';
}
@@ -615,6 +677,10 @@ void xattrs_print (struct tar_stat_info const *st)
if (verbose_option < 3)
return;
/* selinux */
if (selinux_context_option && st->cntx_name)
fprintf (stdlis, " s: %s\n", st->cntx_name);
/* acls */
if (acls_option && (st->acls_a_len || st->acls_d_len))
{

View File

@@ -469,6 +469,7 @@ void xheader_xattr_init (struct tar_stat_info *st)
st->acls_a_len = 0;
st->acls_d_ptr = NULL;
st->acls_d_len = 0;
st->cntx_name = NULL;
}
void xheader_xattr_free (struct xattr_array *xattr_map, size_t xattr_map_size)
@@ -1554,6 +1555,20 @@ volume_filename_decoder (struct tar_stat_info *st,
decode_string (&continued_file_name, arg);
}
static void
xattr_selinux_coder (struct tar_stat_info const *st, char const *keyword,
struct xheader *xhdr, void const *data)
{
code_string (st->cntx_name, keyword, xhdr);
}
static void
xattr_selinux_decoder (struct tar_stat_info *st,
char const *keyword, char const *arg, size_t size)
{
decode_string (&st->cntx_name, arg);
}
static void
xattr_acls_a_coder (struct tar_stat_info const *st , char const *keyword,
struct xheader *xhdr, void const *data)
@@ -1703,6 +1718,11 @@ struct xhdr_tab const xhdr_tab[] = {
{ "GNU.volume.offset", volume_offset_coder, volume_offset_decoder,
XHDR_PROTECTED | XHDR_GLOBAL, false },
/* We get the SELinux value from filecon, so add a namespace for SELinux
instead of storing it in SCHILY.xattr.* (which would be RAW). */
{ "RHT.security.selinux",
xattr_selinux_coder, xattr_selinux_decoder, 0, false },
/* ACLs, use the star format... */
{ "SCHILY.acl.access",
xattr_acls_a_coder, xattr_acls_a_decoder, 0, false },

View File

@@ -180,6 +180,8 @@ TESTSUITE_AT = \
xattr04.at\
acls01.at\
acls02.at\
selnx01.at\
selacl01.at\
capabs_raw01.at
TESTSUITE = $(srcdir)/testsuite

63
tests/selacl01.at Normal file
View File

@@ -0,0 +1,63 @@
# Process this file with autom4te to create testsuite. -*- Autotest -*-
#
# Test suite for GNU tar.
# Copyright (C) 2011 Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Test description:
#
# This is basic test for support of extended attributes.
AT_SETUP([acls/selinux: special files & fifos])
AT_KEYWORDS([xattrs selinux acls selacls01])
AT_TAR_CHECK([
AT_PRIVILEGED_PREREQ
AT_XATTRS_UTILS_PREREQ
AT_SELINUX_PREREQ
AT_ACLS_PREREQ
mkdir dir
mkfifo dir/fifo
MAJOR=$( stat /dev/urandom --printf="%t" )
MINOR=$( stat /dev/urandom --printf="%T" )
mknod dir/chartype c $MAJOR $MINOR
# setup attributes
chcon -h --user=system_u dir/fifo
chcon -h --user=system_u dir/chartype
setfacl -m u:$UID:--- dir/fifo
setfacl -m u:$UID:rwx dir/chartype
getfacl dir/fifo >> before
getfattr -msecurity.selinux dir/chartype >> before
tar --xattrs --selinux --acls -cf archive.tar dir
mv dir olddir
tar --xattrs --selinux --acls -xf archive.tar
getfacl dir/fifo >> after
getfattr -msecurity.selinux dir/chartype >> after
diff before after
echo separator
],
[0],
[separator
])
AT_CLEANUP

95
tests/selnx01.at Normal file
View File

@@ -0,0 +1,95 @@
# Process this file with autom4te to create testsuite. -*- Autotest -*-
#
# Test suite for GNU tar.
# Copyright (C) 2012 Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Test description:
#
# This is basic test for selinux support (store & restore).
AT_SETUP([selinux: basic store/restore])
AT_KEYWORDS([xattrs selinux selnx01])
AT_TAR_CHECK([
AT_XATTRS_UTILS_PREREQ
AT_SELINUX_PREREQ
mkdir dir
genfile --file dir/file
ln -s file dir/link
getfattr -h -d -msecurity.selinux dir dir/file dir/link > start
chcon -h --user=system_u dir
chcon -h --user=unconfined_u dir/file
chcon -h --user=system_u dir/link
# archive whole directory including selinux contexts
tar --selinux -cf archive.tar dir
# clear the directory
rm -rf dir
# ================================================
# check if selinux contexts are correctly restored
tar --selinux -xf archive.tar
# archive for later debugging
cp archive.tar archive_origin.tar
# check if selinux contexts were restored
getfattr -h -d dir dir/file dir/link -msecurity.selinux | \
grep -v -e '^#' -e ^$ | cut -d: -f1
# ===========================================================================
# check if selinux contexts are not restored when --selinux option is missing
getfattr -h -d -msecurity.selinux dir dir/file dir/link > with_selinux
rm -rf dir
tar -xf archive.tar
getfattr -h -d -msecurity.selinux dir dir/file dir/link > without_selinux
diff with_selinux without_selinux > diff_with_without
if test "$?" -eq "0"; then
echo "selinux contexts probably restored while --selinux is off"
fi
# =================================================================
# check if selinux is not archived when --selinux option is missing
tar -cf archive.tar dir
# clear the directory
rm -rf dir
# restore (with --selinux)
tar --selinux -xf archive.tar dir
getfattr -h -d -msecurity.selinux dir dir/file dir/link > final
diff start final > final_diff
if test "$?" -ne "0"; then
echo "bad result"
fi
],
[0],
[security.selinux="system_u
security.selinux="unconfined_u
security.selinux="system_u
])
AT_CLEANUP

View File

@@ -130,6 +130,11 @@ m4_define([AT_XATTRS_UTILS_PREREQ],[
AT_CHECK_UTIL(setfattr -n user.test -v test $file,0)
AT_CHECK_UTIL(getfattr $file,0)
])
m4_define([AT_SELINUX_UTILS_PREREQ],[
file=$( mktemp -p . )
AT_CHECK_UTIL(chcon -h --user=unconfined_u $file,0)
rm -rf $file
])
m4_define([AT_ACLS_UTILS_PREREQ],[
file=$( mktemp -p . )
AT_CHECK_UTIL(setfacl -m u:$UID:rwx $file,0)
@@ -152,6 +157,14 @@ m4_define([AT_XATTRS_PREREQ],[
AT_SKIP_TEST
fi
])
m4_define([AT_SELINUX_PREREQ],[
AT_XATTRS_UTILS_PREREQ
file=$( mktemp -p . )
err=$( tar --selinux -cf /dev/null $file 2>&1 >/dev/null | wc -l )
if test "$err" != "0"; then
AT_SKIP_TEST
fi
])
m4_define([AT_ACLS_PREREQ],[
AT_XATTRS_UTILS_PREREQ
file=$( mktemp -p . )
@@ -328,6 +341,9 @@ m4_include([xattr04.at])
m4_include([acls01.at])
m4_include([acls02.at])
m4_include([selnx01.at])
m4_include([selacl01.at])
m4_include([capabs_raw01.at])
m4_include([star/gtarfail.at])