* 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.
rpmatch
safe-read
savedir
+selinux-at
setenv
snprintf
stat-time
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)
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;
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)
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 ();
{
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))
int change_dir;
/* extended attributes*/
+ char *cntx_name;
char *acls_a_ptr;
size_t acls_a_len;
char *acls_d_ptr;
hard-linked together. */
struct string_list *sources;
+ /* SELinux context */
+ char *cntx_name;
+
/* ACLs */
char *acls_a_ptr;
size_t acls_a_len;
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
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);
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;
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);
+ 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;
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;
}
xheader_xattr_free (ds->xattr_map, ds->xattr_map_size);
+ free (ds->cntx_name);
{
struct delayed_link *next = ds->next;
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,
RMT_COMMAND_OPTION,
RSH_COMMAND_OPTION,
SAME_OWNER_OPTION,
+ SELINUX_CONTEXT_OPTION,
SHOW_DEFAULTS_OPTION,
SHOW_OMITTED_DIRS_OPTION,
SHOW_TRANSFORMED_NAMES_OPTION,
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,
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;
&& !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)
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);
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. */
#include "common.h"
#include "xattr-at.h"
+#include "selinux-at.h"
struct xattrs_mask_map
{
}
}
+/* 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;
return;
}
- if (xattrs_option > 0 || acls_option > 0)
+ if (xattrs_option > 0 || selinux_context_option > 0 || acls_option > 0)
{
/* placeholders */
*output = ' ';
break;
}
+ if (selinux_context_option > 0 && st->cntx_name)
+ *output = '.';
+
if (acls_option && (st->acls_a_len || st->acls_d_len))
*output = '+';
}
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))
{
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)
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)
{ "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 },
xattr04.at\
acls01.at\
acls02.at\
+ selnx01.at\
+ selacl01.at\
capabs_raw01.at
TESTSUITE = $(srcdir)/testsuite
--- /dev/null
+# 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
--- /dev/null
+# 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
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)
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 . )
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])