From c1fb4bcb2ad215c11efa2de8b5f41e4d427c283a Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Thu, 4 Jan 2007 16:33:43 +0100 Subject: [PATCH 01/30] New program: chcon * gl/modules/selinux-at: New module. Check for libselinux and set LIB_SELINUX here, unconditionally, rather than depending on the configure-time --enable-selinux option. * gl/modules/selinux-h: New module. * bootstrap.conf (gnulib_modules): Add selinux-at. * gl/lib/selinux-at.c, gl/lib/selinux-at.h: New files. * gl/lib/se-selinux_.h: New file. * gl/lib/se-context_.h: New file. * gl/m4/selinux-selinux-h.m4: New file. * gl/m4/selinux-context-h.m4: New file. * src/Makefile.am (bin_PROGRAMS): Add chcon. (chcon_LDADD): Define. * README: Add chcon to the list of programs. * src/chcon.c: Rewrite the original (Red Hat) chcon to use fts. --- ChangeLog | 16 + README | 16 +- bootstrap.conf | 4 +- gl/lib/se-context_.h | 31 ++ gl/lib/se-selinux_.h | 54 ++++ gl/lib/selinux-at.c | 94 ++++++ gl/lib/selinux-at.h | 24 ++ gl/m4/selinux-context-h.m4 | 18 ++ gl/m4/selinux-selinux-h.m4 | 18 ++ gl/modules/selinux-at | 32 ++ gl/modules/selinux-h | 54 ++++ src/Makefile.am | 3 +- src/chcon.c | 589 +++++++++++++++++++++++++++++++++++++ 13 files changed, 943 insertions(+), 10 deletions(-) create mode 100644 gl/lib/se-context_.h create mode 100644 gl/lib/se-selinux_.h create mode 100644 gl/lib/selinux-at.c create mode 100644 gl/lib/selinux-at.h create mode 100644 gl/m4/selinux-context-h.m4 create mode 100644 gl/m4/selinux-selinux-h.m4 create mode 100644 gl/modules/selinux-at create mode 100644 gl/modules/selinux-h create mode 100644 src/chcon.c diff --git a/ChangeLog b/ChangeLog index 2b8a753955..22927a05c5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -789,6 +789,22 @@ 2007-01-04 Jim Meyering + New program: chcon + * gl/modules/selinux-at: New module. Check for libselinux and set + LIB_SELINUX here, unconditionally, rather than depending on + the configure-time --enable-selinux option. + * gl/modules/selinux-h: New module. + * bootstrap.conf (gnulib_modules): Add selinux-at. + * gl/lib/selinux-at.c, gl/lib/selinux-at.h: New files. + * gl/lib/se-selinux_.h: New file. + * gl/lib/se-context_.h: New file. + * gl/m4/selinux-selinux-h.m4: New file. + * gl/m4/selinux-context-h.m4: New file. + * src/Makefile.am (bin_PROGRAMS): Add chcon. + (chcon_LDADD): Define. + * README: Add chcon to the list of programs. + * src/chcon.c: Rewrite the original (Red Hat) chcon to use fts. + * Makefile.cfg (local-checks-to-skip): Skip strftime-check, in case you don't have convenient access to glibc info documentation. diff --git a/README b/README index 236b2d51ab..1dd0bbccb9 100644 --- a/README +++ b/README @@ -7,14 +7,14 @@ arbitrary limits. The programs that can be built with this package are: - [ base64 basename cat chgrp chmod chown chroot cksum comm cp csplit cut date - dd df dir dircolors dirname du echo env expand expr factor false fmt fold - ginstall groups head hostid hostname id join kill link ln logname ls - md5sum mkdir mkfifo mknod mv nice nl nohup od paste pathchk pinky pr - printenv printf ptx pwd readlink rm rmdir seq sha1sum sha224sum sha256sum - sha384sum sha512sum shred shuf sleep sort split stat stty su sum sync tac - tail tee test touch tr true tsort tty uname unexpand uniq unlink uptime - users vdir wc who whoami yes + [ base64 basename cat chcon chgrp chmod chown chroot cksum comm cp + csplit cut date dd df dir dircolors dirname du echo env expand expr + factor false fmt fold ginstall groups head hostid hostname id join + kill link ln logname ls md5sum mkdir mkfifo mknod mv nice nl nohup od + paste pathchk pinky pr printenv printf ptx pwd readlink rm rmdir seq + sha1sum sha224sum sha256sum sha384sum sha512sum shred shuf sleep sort + split stat stty su sum sync tac tail tee test touch tr true tsort tty + uname unexpand uniq unlink uptime users vdir wc who whoami yes See the file NEWS for a list of major changes in the current release. diff --git a/bootstrap.conf b/bootstrap.conf index 30433216d2..d539c37e28 100644 --- a/bootstrap.conf +++ b/bootstrap.conf @@ -60,7 +60,9 @@ gnulib_modules=" root-dev-ino rpmatch safe-read same - save-cwd savedir savewd settime sha1 sig2str ssize_t stat-macros + save-cwd savedir savewd + selinux-at + settime sha1 sig2str ssize_t stat-macros stat-time stdbool stdlib-safer stpcpy strftime strpbrk strtoimax strtoumax strverscmp sys_stat timespec tzset unicodeio unistd-safer unlink-busy unlinkdir unlocked-io diff --git a/gl/lib/se-context_.h b/gl/lib/se-context_.h new file mode 100644 index 0000000000..26e1709f10 --- /dev/null +++ b/gl/lib/se-context_.h @@ -0,0 +1,31 @@ +#ifndef SELINUX_CONTEXT_H +# define SELINUX_CONTEXT_H + +# include +/* Some systems don't have ENOSYS. */ +# ifndef ENOSYS +# ifdef ENOTSUP +# define ENOSYS ENOTSUP +# else +/* Some systems don't have ENOTSUP either. */ +# define ENOSYS EINVAL +# endif +# endif + +typedef int context_t; +static inline context_t context_new (char const *s) + { errno = ENOTSUP; return 0; } +static inline char *context_str (context_t con) + { errno = ENOTSUP; return (void *) 0; } +static inline void context_free (context_t c) {} + +static inline int context_user_set (context_t sc, char const *s) + { errno = ENOTSUP; return -1; } +static inline int context_role_set (context_t sc, char const *s) + { errno = ENOTSUP; return -1; } +static inline int context_range_set (context_t sc, char const *s) + { errno = ENOTSUP; return -1; } +static inline int context_type_set (context_t sc, char const *s) + { errno = ENOTSUP; return -1; } + +#endif diff --git a/gl/lib/se-selinux_.h b/gl/lib/se-selinux_.h new file mode 100644 index 0000000000..b08c7eee42 --- /dev/null +++ b/gl/lib/se-selinux_.h @@ -0,0 +1,54 @@ +#ifndef SELINUX_SELINUX_H +# define SELINUX_SELINUX_H + +# include +# include +/* Some systems don't have ENOSYS. */ +# ifndef ENOSYS +# ifdef ENOTSUP +# define ENOSYS ENOTSUP +# else +/* Some systems don't have ENOTSUP either. */ +# define ENOSYS EINVAL +# endif +# endif + +typedef unsigned short security_class_t; +# define security_context_t char* +# define is_selinux_enabled() 0 + +static inline int getcon (security_context_t *con) { errno = ENOTSUP; return -1; } +static inline void freecon (security_context_t con) {} + + +static inline int getfscreatecon (security_context_t *con) + { errno = ENOTSUP; return -1; } +static inline int setfscreatecon (security_context_t con) + { errno = ENOTSUP; return -1; } +static inline int matchpathcon (char const *s, mode_t m, + security_context_t *con) + { errno = ENOTSUP; return -1; } + +static inline int getfilecon (char const *s, security_context_t *con) + { errno = ENOTSUP; return -1; } +static inline int lgetfilecon (char const *s, security_context_t *con) + { errno = ENOTSUP; return -1; } +static inline int setfilecon (char const *s, security_context_t con) + { errno = ENOTSUP; return -1; } +static inline int lsetfilecon (char const *s, security_context_t con) + { errno = ENOTSUP; return -1; } +static inline int fsetfilecon (int fd, security_context_t con) + { errno = ENOTSUP; return -1; } + +static inline int security_check_context (security_context_t con) + { errno = ENOTSUP; return -1; } +static inline int security_check_context_raw (security_context_t con) + { errno = ENOTSUP; return -1; } +static inline int setexeccon (security_context_t con) + { errno = ENOTSUP; return -1; } +static inline int security_compute_create (security_context_t scon, + security_context_t tcon, + security_class_t tclass, + security_context_t *newcon) + { errno = ENOTSUP; return -1; } +#endif diff --git a/gl/lib/selinux-at.c b/gl/lib/selinux-at.c new file mode 100644 index 0000000000..ebc41ee7ac --- /dev/null +++ b/gl/lib/selinux-at.c @@ -0,0 +1,94 @@ +/* openat-style fd-relative functions for SE Linux + Copyright (C) 2007 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 2, 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, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* written by Jim Meyering */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "selinux-at.h" +#include "openat.h" + +#include +#include +#include +#include + +#include "dirname.h" /* solely for definition of IS_ABSOLUTE_FILE_NAME */ +#include "save-cwd.h" + +#include "gettext.h" +#define _(msgid) gettext (msgid) + +#include "openat-priv.h" + +#define AT_FUNC_NAME getfileconat +#define AT_FUNC_F1 getfilecon +#define AT_FUNC_F2 getfilecon +#define AT_FUNC_USE_F1_COND 1 +#define AT_FUNC_POST_FILE_PARAM_DECLS , security_context_t *con +#define AT_FUNC_POST_FILE_ARGS , con +#include "at-func.c" +#undef AT_FUNC_NAME +#undef AT_FUNC_F1 +#undef AT_FUNC_F2 +#undef AT_FUNC_USE_F1_COND +#undef AT_FUNC_POST_FILE_PARAM_DECLS +#undef AT_FUNC_POST_FILE_ARGS + +#define AT_FUNC_NAME lgetfileconat +#define AT_FUNC_F1 lgetfilecon +#define AT_FUNC_F2 lgetfilecon +#define AT_FUNC_USE_F1_COND 1 +#define AT_FUNC_POST_FILE_PARAM_DECLS , security_context_t *con +#define AT_FUNC_POST_FILE_ARGS , con +#include "at-func.c" +#undef AT_FUNC_NAME +#undef AT_FUNC_F1 +#undef AT_FUNC_F2 +#undef AT_FUNC_USE_F1_COND +#undef AT_FUNC_POST_FILE_PARAM_DECLS +#undef AT_FUNC_POST_FILE_ARGS + +#define AT_FUNC_NAME setfileconat +#define AT_FUNC_F1 setfilecon +#define AT_FUNC_F2 setfilecon +#define AT_FUNC_USE_F1_COND 1 +#define AT_FUNC_POST_FILE_PARAM_DECLS , security_context_t con +#define AT_FUNC_POST_FILE_ARGS , con +#include "at-func.c" +#undef AT_FUNC_NAME +#undef AT_FUNC_F1 +#undef AT_FUNC_F2 +#undef AT_FUNC_USE_F1_COND +#undef AT_FUNC_POST_FILE_PARAM_DECLS +#undef AT_FUNC_POST_FILE_ARGS + +#define AT_FUNC_NAME lsetfileconat +#define AT_FUNC_F1 lsetfilecon +#define AT_FUNC_F2 lsetfilecon +#define AT_FUNC_USE_F1_COND 1 +#define AT_FUNC_POST_FILE_PARAM_DECLS , security_context_t con +#define AT_FUNC_POST_FILE_ARGS , con +#include "at-func.c" +#undef AT_FUNC_NAME +#undef AT_FUNC_F1 +#undef AT_FUNC_F2 +#undef AT_FUNC_USE_F1_COND +#undef AT_FUNC_POST_FILE_PARAM_DECLS +#undef AT_FUNC_POST_FILE_ARGS diff --git a/gl/lib/selinux-at.h b/gl/lib/selinux-at.h new file mode 100644 index 0000000000..f12022c517 --- /dev/null +++ b/gl/lib/selinux-at.h @@ -0,0 +1,24 @@ +/* Prototypes for openat-style fd-relative SELinux functions + Copyright (C) 2007 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 2, 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, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include +#include + +int getfileconat (int fd, char const *file, security_context_t *con); +int lgetfileconat (int fd, char const *file, security_context_t *con); +int setfileconat (int fd, char const *file, security_context_t con); +int lsetfileconat (int fd, char const *file, security_context_t con); diff --git a/gl/m4/selinux-context-h.m4 b/gl/m4/selinux-context-h.m4 new file mode 100644 index 0000000000..4011dde2af --- /dev/null +++ b/gl/m4/selinux-context-h.m4 @@ -0,0 +1,18 @@ +# serial 1 -*- Autoconf -*- +# Copyright (C) 2006 Free Software Foundation, Inc. +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# From Jim Meyering +# Provide , if necessary. + +AC_DEFUN([gl_HEADERS_SELINUX_CONTEXT_H], +[ + AC_LIBSOURCES([se-context_.h]) + # Check for , + AC_CHECK_HEADERS([selinux/context.h], + [SELINUX_CONTEXT_H=], + [SELINUX_CONTEXT_H=selinux/context.h]) + AC_SUBST([SELINUX_CONTEXT_H]) +]) diff --git a/gl/m4/selinux-selinux-h.m4 b/gl/m4/selinux-selinux-h.m4 new file mode 100644 index 0000000000..13ce2ac9ad --- /dev/null +++ b/gl/m4/selinux-selinux-h.m4 @@ -0,0 +1,18 @@ +# serial 1 -*- Autoconf -*- +# Copyright (C) 2006 Free Software Foundation, Inc. +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# From Jim Meyering +# Provide , if necessary. + +AC_DEFUN([gl_HEADERS_SELINUX_SELINUX_H], +[ + AC_LIBSOURCES([se-selinux_.h]) + # Check for , + AC_CHECK_HEADERS([selinux/selinux.h], + [SELINUX_SELINUX_H=], + [SELINUX_SELINUX_H=selinux/selinux.h]) + AC_SUBST([SELINUX_SELINUX_H]) +]) diff --git a/gl/modules/selinux-at b/gl/modules/selinux-at new file mode 100644 index 0000000000..7599083979 --- /dev/null +++ b/gl/modules/selinux-at @@ -0,0 +1,32 @@ +Description: +openat-style fd-relative functions for SE Linux + +Files: +lib/selinux-at.c +lib/selinux-at.h + +Depends-on: +selinux-h + +configure.ac: +# FIXME: put this in an .m4 file? +# For runcon. +AC_CHECK_HEADERS([selinux/flask.h]) +AC_LIBOBJ([selinux-at]) +ac_save_LIBS="$LIBS" + AC_SEARCH_LIBS(setfilecon, selinux, + [test "$ac_cv_search_setfilecon" = "none required" || + LIB_SELINUX=$ac_cv_search_setfilecon]) + AC_SUBST(LIB_SELINUX) +LIBS="$ac_save_LIBS" + +Makefile.am: + +Include: +selinux-at.h + +License: +LGPL + +Maintainer: +Jim Meyering diff --git a/gl/modules/selinux-h b/gl/modules/selinux-h new file mode 100644 index 0000000000..915b9d2765 --- /dev/null +++ b/gl/modules/selinux-h @@ -0,0 +1,54 @@ +Description: +SELinux-related headers for systems that lack them. + +Files: +lib/se-context_.h +lib/se-selinux_.h +m4/selinux-context-h.m4 +m4/selinux-selinux-h.m4 + +Depends-on: + +configure.ac: +gl_HEADERS_SELINUX_SELINUX_H +gl_HEADERS_SELINUX_CONTEXT_H + +Makefile.am: +BUILT_SOURCES += $(SELINUX_SELINUX_H) +selinux/selinux.h: se-selinux_.h + mkdir -p selinux + cp $(srcdir)/se-selinux_.h $@-t + chmod a-x $@-t + mv $@-t $@ +MOSTLYCLEANFILES += selinux/selinux.h selinux/selinux.h-t + +BUILT_SOURCES += $(SELINUX_CONTEXT_H) +selinux/context.h: se-context_.h + mkdir -p selinux + cp $(srcdir)/se-context_.h $@-t + chmod a-x $@-t + mv $@-t $@ +MOSTLYCLEANFILES += selinux/context.h selinux/context.h-t +MOSTLYCLEANDIRS += selinux + +Include: +#include +#include + +License: +LGPL + +Maintainer: +Jim Meyering + +# lib/selinux-at.c +# +# # For runcon. +# AC_CHECK_HEADERS([selinux/flask.h]) +# +# ac_save_LIBS="$LIBS" +# AC_SEARCH_LIBS(setfilecon, selinux, +# [test "$ac_cv_search_setfilecon" = "none required" || +# LIB_SELINUX=$ac_cv_search_setfilecon]) +# AC_SUBST(LIB_SELINUX) +# LIBS="$ac_save_LIBS" diff --git a/src/Makefile.am b/src/Makefile.am index 7aa504b365..8b6fb4c360 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -19,7 +19,7 @@ EXTRA_PROGRAMS = chroot df hostid nice pinky stty su uname uptime users who bin_SCRIPTS = groups -bin_PROGRAMS = [ chgrp chown chmod cp dd dircolors du \ +bin_PROGRAMS = [ chcon chgrp chown chmod cp dd dircolors du \ ginstall link ln dir vdir ls mkdir \ mkfifo mknod mv nohup readlink rm rmdir shred stat sync touch unlink \ cat cksum comm csplit cut expand fmt fold head join md5sum \ @@ -60,6 +60,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/lib LDADD = ../lib/libcoreutils.a $(LIBINTL) ../lib/libcoreutils.a # for eaccess in lib/euidaccess.c. +chcon_LDADD = $(LDADD) $(LIB_SELINUX) cp_LDADD = $(LDADD) $(LIB_EACCESS) ginstall_LDADD = $(LDADD) $(LIB_EACCESS) mv_LDADD = $(LDADD) $(LIB_EACCESS) diff --git a/src/chcon.c b/src/chcon.c new file mode 100644 index 0000000000..66fda1d961 --- /dev/null +++ b/src/chcon.c @@ -0,0 +1,589 @@ +/* chcon -- change security context of files + Copyright (C) 2005-2007 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 2, 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, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include +#include +#include +#include + +#include "system.h" +#include "dev-ino.h" +#include "dirname.h" +#include "error.h" +#include "openat.h" +#include "quote.h" +#include "quotearg.h" +#include "root-dev-ino.h" +#include "selinux-at.h" +#include "xfts.h" + +/* The official name of this program (e.g., no `g' prefix). */ +#define PROGRAM_NAME "chcon" + +#define AUTHORS "Russell Coker", "Jim Meyering" + +enum Change_status +{ + CH_NOT_APPLIED, + CH_SUCCEEDED, + CH_FAILED, + CH_NO_CHANGE_REQUESTED +}; + +enum Verbosity +{ + /* Print a message for each file that is processed. */ + V_high, + + /* Print a message for each file whose attributes we change. */ + V_changes_only, + + /* Do not be verbose. This is the default. */ + V_off +}; + +/* The name the program was run with. */ +char *program_name; + +/* If nonzero, and the systems has support for it, change the context + of symbolic links rather than any files they point to. */ +static bool affect_symlink_referent; + +/* If true, change the modes of directories recursively. */ +static bool recurse; + +/* Level of verbosity. */ +static bool verbose; + +/* Pointer to the device and inode numbers of `/', when --recursive. + Otherwise NULL. */ +static struct dev_ino *root_dev_ino; + +/* The name of the context file is being given. */ +static char const *specified_context; + +/* Specific components of the context */ +static char const *specified_user; +static char const *specified_role; +static char const *specified_range; +static char const *specified_type; + +/* For long options that have no equivalent short option, use a + non-character as a pseudo short option, starting with CHAR_MAX + 1. */ +enum +{ + DEREFERENCE_OPTION = CHAR_MAX + 1, + NO_PRESERVE_ROOT, + PRESERVE_ROOT, + REFERENCE_FILE_OPTION +}; + +static struct option const long_options[] = +{ + {"recursive", no_argument, NULL, 'R'}, + {"dereference", no_argument, NULL, DEREFERENCE_OPTION}, + {"no-dereference", no_argument, NULL, 'h'}, + {"no-preserve-root", no_argument, NULL, NO_PRESERVE_ROOT}, + {"preserve-root", no_argument, NULL, PRESERVE_ROOT}, + {"reference", required_argument, NULL, REFERENCE_FILE_OPTION}, + {"user", required_argument, NULL, 'u'}, + {"role", required_argument, NULL, 'r'}, + {"type", required_argument, NULL, 't'}, + {"range", required_argument, NULL, 'l'}, + {"verbose", no_argument, NULL, 'v'}, + {GETOPT_HELP_OPTION_DECL}, + {GETOPT_VERSION_OPTION_DECL}, + {NULL, 0, NULL, 0} +}; + +/* Given a security context, CONTEXT, derive a context_t (*RET), + setting any portions selected via the global variables, specified_user, + specified_role, etc. */ +static int +compute_context_from_mask (security_context_t context, context_t *ret) +{ + bool ok = true; + context_t new_context = context_new (context); + if (!new_context) + { + error (0, errno, _("failed to create security context: %s"), + quotearg_colon (context)); + return 1; + } + +#define SET_COMPONENT(C, comp) \ + do \ + { \ + if (specified_ ## comp \ + && context_ ## comp ## _set ((C), specified_ ## comp)) \ + { \ + error (0, errno, \ + _("failed to set %s security context component to %s"), \ + #comp, quote (specified_ ## comp)); \ + ok = false; \ + } \ + } \ + while (0) + + SET_COMPONENT (new_context, user); + SET_COMPONENT (new_context, range); + SET_COMPONENT (new_context, role); + SET_COMPONENT (new_context, type); + + if (!ok) + { + int saved_errno = errno; + context_free (new_context); + errno = saved_errno; + return 1; + } + + *ret = new_context; + return 0; +} + +/* Change the context of FILE, using specified components. + If it is a directory and -R is given, recurse. + Return 0 if successful, 1 if errors occurred. */ + +static int +change_file_context (int fd, char const *file) +{ + security_context_t file_context = NULL; + context_t context; + security_context_t context_string; + int errors = 0; + + if (specified_context == NULL) + { + int status = (affect_symlink_referent + ? getfileconat (fd, file, &file_context) + : lgetfileconat (fd, file, &file_context)); + + if (status < 0 && errno != ENODATA) + { + error (0, errno, _("failed to get security context of %s"), + quote (file)); + return 1; + } + + /* If the file doesn't have a context, and we're not setting all of + the context components, there isn't really an obvious default. + Thus, we just give up. */ + if (file_context == NULL) + { + error (0, 0, _("can't apply partial context to unlabeled file %s"), + quote (file)); + return 1; + } + + if (compute_context_from_mask (file_context, &context)) + return 1; + } + else + { + /* FIXME: this should be done exactly once, in main. */ + context = context_new (specified_context); + if (!context) + abort (); + } + + context_string = context_str (context); + + if (file_context == NULL || ! STREQ (context_string, file_context)) + { + int fail = (affect_symlink_referent + ? setfileconat (fd, file, context_string) + : lsetfileconat (fd, file, context_string)); + + if (fail) + { + errors = 1; + error (0, errno, _("failed to change context of %s to %s"), + quote_n (0, file), quote_n (1, context_string)); + } + } + + context_free (context); + freecon (file_context); + + return errors; +} + +/* Change the context of FILE. + Return true if successful. This function is called + once for every file system object that fts encounters. */ + +static bool +process_file (FTS *fts, FTSENT *ent) +{ + char const *file_full_name = ent->fts_path; + char const *file = ent->fts_accpath; + const struct stat *file_stats = ent->fts_statp; + bool ok = true; + + switch (ent->fts_info) + { + case FTS_D: + if (recurse) + { + if (ROOT_DEV_INO_CHECK (root_dev_ino, ent->fts_statp)) + { + /* This happens e.g., with "chcon -R --preserve-root ... /" + and with "chcon -RH --preserve-root ... symlink-to-root". */ + ROOT_DEV_INO_WARN (file_full_name); + /* Tell fts not to traverse into this hierarchy. */ + fts_set (fts, ent, FTS_SKIP); + /* Ensure that we do not process "/" on the second visit. */ + ent = fts_read (fts); + return false; + } + return true; + } + break; + + case FTS_DP: + if (! recurse) + return true; + break; + + case FTS_NS: + /* For a top-level file or directory, this FTS_NS (stat failed) + indicator is determined at the time of the initial fts_open call. + With programs like chmod, chown, and chgrp, that modify + permissions, it is possible that the file in question is + accessible when control reaches this point. So, if this is + the first time we've seen the FTS_NS for this file, tell + fts_read to stat it "again". */ + if (ent->fts_level == 0 && ent->fts_number == 0) + { + ent->fts_number = 1; + fts_set (fts, ent, FTS_AGAIN); + return true; + } + error (0, ent->fts_errno, _("cannot access %s"), quote (file_full_name)); + ok = false; + break; + + case FTS_ERR: + error (0, ent->fts_errno, _("%s"), quote (file_full_name)); + ok = false; + break; + + case FTS_DNR: + error (0, ent->fts_errno, _("cannot read directory %s"), + quote (file_full_name)); + ok = false; + break; + + default: + break; + } + + if (ent->fts_info == FTS_DP + && ok && ROOT_DEV_INO_CHECK (root_dev_ino, file_stats)) + { + ROOT_DEV_INO_WARN (file_full_name); + ok = false; + } + + if (ok) + { + if (verbose) + printf (_("changing security context of %s"), + quote (file_full_name)); + + if (change_file_context (fts->fts_cwd_fd, file) != 0) + ok = false; + } + + if ( ! recurse) + fts_set (fts, ent, FTS_SKIP); + + return ok; +} + +/* Recursively operate on the specified FILES (the last entry + of which is NULL). BIT_FLAGS controls how fts works. + Return true if successful. */ + +static bool +process_files (char **files, int bit_flags) +{ + bool ok = true; + + FTS *fts = xfts_open (files, bit_flags, NULL); + + while (1) + { + FTSENT *ent; + + ent = fts_read (fts); + if (ent == NULL) + { + if (errno != 0) + { + /* FIXME: try to give a better message */ + error (0, errno, _("fts_read failed")); + ok = false; + } + break; + } + + ok &= process_file (fts, ent); + } + + /* Ignore failure, since the only way it can do so is in failing to + return to the original directory, and since we're about to exit, + that doesn't matter. */ + fts_close (fts); + + return ok; +} + +void +usage (int status) +{ + if (status != EXIT_SUCCESS) + fprintf (stderr, _("Try `%s --help' for more information.\n"), + program_name); + else + { + printf (_("\ +Usage: %s [OPTION]... CONTEXT FILE...\n\ + or: %s [OPTION]... [-u USER] [-r ROLE] [-l RANGE] [-t TYPE] FILE...\n\ + or: %s [OPTION]... --reference=RFILE FILE...\n\ +"), + program_name, program_name, program_name); + fputs (_("\ +Change the security context of each FILE to CONTEXT.\n\ +With --reference, change the security context of each FILE to that of RFILE.\n\ +\n\ + -c, --changes like verbose but report only when a change is made\n\ + -h, --no-dereference affect symbolic links instead of any referenced file\n\ + (available only on systems with lchown system call)\n\ + --reference=RFILE use RFILE's security context rather than specifying\n\ + a CONTEXT value\n\ + -R, --recursive operate on files and directories recursively\n\ + -v, --verbose output a diagnostic for every file processed\n\ +"), stdout); + fputs (_("\ + -u, --user=USER set user USER in the target security context\n\ + -r, --role=ROLE set role ROLE in the target security context\n\ + -t, --type=TYPE set type TYPE in the target security context\n\ + -l, --range=RANGE set range RANGE in the target security context\n\ +\n\ +"), stdout); + fputs (_("\ +The following options modify how a hierarchy is traversed when the -R\n\ +option is also specified. If more than one is specified, only the final\n\ +one takes effect.\n\ +\n\ + -H if a command line argument is a symbolic link\n\ + to a directory, traverse it\n\ + -L traverse every symbolic link to a directory\n\ + encountered\n\ + -P do not traverse any symbolic links (default)\n\ +\n\ +"), stdout); + fputs (HELP_OPTION_DESCRIPTION, stdout); + fputs (VERSION_OPTION_DESCRIPTION, stdout); + } + exit (status); +} + +int +main (int argc, char **argv) +{ + security_context_t ref_context = NULL; + + /* Bit flags that control how fts works. */ + int bit_flags = FTS_PHYSICAL; + + /* 1 if --dereference, 0 if --no-dereference, -1 if neither has been + specified. */ + int dereference = -1; + + bool ok; + bool preserve_root = false; + bool component_specified = false; + char *reference_file = NULL; + int optc; + + initialize_main (&argc, &argv); + program_name = argv[0]; + setlocale (LC_ALL, ""); + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); + + atexit (close_stdout); + + while ((optc = getopt_long (argc, argv, "HLPRchvu:r:t:l:", long_options, NULL)) + != -1) + { + switch (optc) + { + case 'H': /* Traverse command-line symlinks-to-directories. */ + bit_flags = FTS_COMFOLLOW | FTS_PHYSICAL; + break; + + case 'L': /* Traverse all symlinks-to-directories. */ + bit_flags = FTS_LOGICAL; + break; + + case 'P': /* Traverse no symlinks-to-directories. */ + bit_flags = FTS_PHYSICAL; + break; + + case 'h': /* --no-dereference: affect symlinks */ + dereference = 0; + break; + + case DEREFERENCE_OPTION: /* --dereference: affect the referent + of each symlink */ + dereference = 1; + break; + + case NO_PRESERVE_ROOT: + preserve_root = false; + break; + + case PRESERVE_ROOT: + preserve_root = true; + break; + + case REFERENCE_FILE_OPTION: + reference_file = optarg; + break; + + case 'R': + recurse = true; + break; + + case 'f': + /* ignore */ + break; + + case 'v': + verbose = true; + break; + + case 'u': + specified_user = optarg; + component_specified = true; + break; + + case 'r': + specified_role = optarg; + component_specified = true; + break; + + case 't': + specified_type = optarg; + component_specified = true; + break; + + case 'l': + specified_range = optarg; + component_specified = true; + break; + + case_GETOPT_HELP_CHAR; + case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); + default: + usage (EXIT_FAILURE); + } + } + + if (recurse) + { + if (bit_flags == FTS_PHYSICAL) + { + if (dereference == 1) + error (EXIT_FAILURE, 0, + _("-R --dereference requires either -H or -L")); + affect_symlink_referent = false; + } + else + { + if (dereference == 0) + error (EXIT_FAILURE, 0, _("-R -h requires -P")); + affect_symlink_referent = true; + } + } + else + { + bit_flags = FTS_PHYSICAL; + affect_symlink_referent = (dereference != 0); + } + + if (argc - optind < (reference_file || component_specified ? 1 : 2)) + { + if (argc <= optind) + error (0, 0, _("missing operand")); + else + error (0, 0, _("missing operand after %s"), quote (argv[argc - 1])); + usage (EXIT_FAILURE); + } + + if (reference_file) + { + if (getfilecon (reference_file, &ref_context) < 0) + error (EXIT_FAILURE, errno, _("failed to get security context of %s"), + quote (reference_file)); + + specified_context = ref_context; + } + else if (component_specified) + { + /* FIXME: it's already null, so this is a no-op. */ + specified_context = NULL; + } + else + { + context_t context; + specified_context = argv[optind++]; + context = context_new (specified_context); + if (!context) + error (EXIT_FAILURE, 0, _("invalid context: %s"), + quotearg_colon (specified_context)); + context_free (context); + } + + if (reference_file && component_specified) + { + error (0, 0, _("conflicting security context specifiers given")); + usage (1); + } + + if (recurse & preserve_root) + { + static struct dev_ino dev_ino_buf; + root_dev_ino = get_root_dev_ino (&dev_ino_buf); + if (root_dev_ino == NULL) + error (EXIT_FAILURE, errno, _("failed to get attributes of %s"), + quote ("/")); + } + else + { + root_dev_ino = NULL; + } + + ok = process_files (argv + optind, bit_flags | FTS_NOSTAT); + + exit (ok ? EXIT_SUCCESS : EXIT_FAILURE); +} From 561c3a2b753b3b39317b9b6320ab2930d41fdf97 Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Thu, 4 Jan 2007 16:35:31 +0100 Subject: [PATCH 02/30] .cvsignore, .gitignore: update --- .cvsignore | 1 + .gitignore | 1 + lib/.cvsignore | 5 +++++ lib/.gitignore | 5 +++++ m4/.cvsignore | 2 ++ m4/.gitignore | 2 ++ 6 files changed, 16 insertions(+) diff --git a/.cvsignore b/.cvsignore index 9e20a2569a..bf902e9136 100644 --- a/.cvsignore +++ b/.cvsignore @@ -11,3 +11,4 @@ aclocal.m4 config.status configure coreutils-* +chcon diff --git a/.gitignore b/.gitignore index 4567b1b358..213c2853dc 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,4 @@ tests/test/test-tests tests/tr/tr-tests tests/uniq/uniq-tests tests/wc/wc-tests +chcon diff --git a/lib/.cvsignore b/lib/.cvsignore index df34a8b6c5..e4e482852e 100644 --- a/lib/.cvsignore +++ b/lib/.cvsignore @@ -286,7 +286,12 @@ savedir.c savedir.h savewd.c savewd.h +se-context_.h +se-selinux_.h search.h +selinux +selinux-at.c +selinux-at.h setenv.c setenv.h settime.c diff --git a/lib/.gitignore b/lib/.gitignore index d7233fe3d7..85886debee 100644 --- a/lib/.gitignore +++ b/lib/.gitignore @@ -279,6 +279,11 @@ savedir.c savedir.h savewd.c savewd.h +se-context_.h +se-selinux_.h +selinux +selinux-at.c +selinux-at.h setenv.c setenv.h settime.c diff --git a/m4/.cvsignore b/m4/.cvsignore index faa76a48cf..8ebe6881f6 100644 --- a/m4/.cvsignore +++ b/m4/.cvsignore @@ -175,6 +175,8 @@ same.m4 save-cwd.m4 savedir.m4 savewd.m4 +selinux-context-h.m4 +selinux-selinux-h.m4 setenv.m4 settime.m4 sha1.m4 diff --git a/m4/.gitignore b/m4/.gitignore index 30fc58e571..1c2313eab6 100644 --- a/m4/.gitignore +++ b/m4/.gitignore @@ -168,6 +168,8 @@ same.m4 save-cwd.m4 savedir.m4 savewd.m4 +selinux-context-h.m4 +selinux-selinux-h.m4 setenv.m4 settime.m4 sha1.m4 From 27940f5d023b9af9fb074bbddf851396ddcda445 Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Thu, 4 Jan 2007 16:41:46 +0100 Subject: [PATCH 03/30] * man/chcon.x: New file. * man/Makefile.am: Build chcon.1. --- ChangeLog | 3 +++ man/Makefile.am | 3 ++- man/chcon.x | 4 ++++ 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 man/chcon.x diff --git a/ChangeLog b/ChangeLog index 22927a05c5..b6816681e1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -789,6 +789,9 @@ 2007-01-04 Jim Meyering + * man/chcon.x: New file. + * man/Makefile.am: Build chcon.1. + New program: chcon * gl/modules/selinux-at: New module. Check for libselinux and set LIB_SELINUX here, unconditionally, rather than depending on diff --git a/man/Makefile.am b/man/Makefile.am index 32df9d1766..8c5f610352 100644 --- a/man/Makefile.am +++ b/man/Makefile.am @@ -18,7 +18,7 @@ # 02110-1301, USA. dist_man_MANS = \ - base64.1 basename.1 cat.1 chgrp.1 chmod.1 chown.1 cksum.1 comm.1 \ + base64.1 basename.1 cat.1 chcon.1 chgrp.1 chmod.1 chown.1 cksum.1 comm.1 \ cp.1 csplit.1 cut.1 date.1 dd.1 df.1 dir.1 dircolors.1 dirname.1 du.1 \ echo.1 env.1 expand.1 expr.1 factor.1 false.1 fmt.1 fold.1 groups.1 \ head.1 hostname.1 id.1 install.1 join.1 kill.1 \ @@ -45,6 +45,7 @@ common_dep = $(top_srcdir)/configure.ac base64.1: $(common_dep) $(srcdir)/base64.x ../src/base64.c basename.1: $(common_dep) $(srcdir)/basename.x ../src/basename.c cat.1: $(common_dep) $(srcdir)/cat.x ../src/cat.c +chcon.1: $(common_dep) $(srcdir)/chcon.x ../src/chcon.c chgrp.1: $(common_dep) $(srcdir)/chgrp.x ../src/chgrp.c chmod.1: $(common_dep) $(srcdir)/chmod.x ../src/chmod.c chown.1: $(common_dep) $(srcdir)/chown.x ../src/chown.c diff --git a/man/chcon.x b/man/chcon.x new file mode 100644 index 0000000000..8c1ff6ff9c --- /dev/null +++ b/man/chcon.x @@ -0,0 +1,4 @@ +[NAME] +chcon \- change file security context +[DESCRIPTION] +.\" Add any additional description here From 1e9098dc83e580cf1640b557860600e34401c807 Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Fri, 5 Jan 2007 18:23:54 +0100 Subject: [PATCH 04/30] ls: Add support for SELinux and a slightly modified -Z option. I started with the patches from Red Hat. The entries below tell how the code evolved. * src/ls.c (print_long_format, print_file_name_and_frills): When there is no security context (due to getfilecon/lgetfilecon failing with e.g. ENOTSUP), print it as "?", not "". * src/ls.c (print_file_name_and_frills): Make -Z work without -l. (length_of_file_name_and_frills): Likewise. * src/ls.c: Remove the --lcontext and --scontext options. Change the way -Z, --context work so that it no longer implies -l. Thus, -Z -l will work like -lcontext and -Z without -l will work like --scontext. Adjust tests to reflect new 'ls -l' syntax -- affects only systems with SELinux when operating on a file with no ACL. These tests assumed that everything before the first space on each line is the 10-byte mode string. But there may also be a "+" in the 11th column, just before the space. However, note that this is not new. The same thing would have happened even without the change below, when listing a file with an ACL. * tests/chmod/equals, tests/cp/cp-parents, tests/cp/fail-perm: * tests/cp/link-preserve, tests/install/basic-1, tests/misc/mknod: * tests/mkdir/parents, tests/mkdir/special-1, tests/mv/partition-perm: Don't make compilation depend on USE_ACL. An SELinux security context counts as an "alternate access control method", so ls must output a "+" for each file with a security context. * src/ls.c [struct fileinfo] (have_acl): Declare unconditionally. (FILE_HAS_ACL): Remove macro definition. Use f->have_acl directly. (gobble_file): Record whether a file has a security context, and update the condition used to determine whether to print the "+". (gobble_file): Call getfilecon/lgetfilecon also when format == long_format, so that we get the "+". * src/ls.c (gobble_file): Add a comment explaining why (with a security context option) ls doesn't exit nonzero due to e.g., getfilecon failing with errno == ENOTSUP. * src/ls.c (gobble_file): Ignore failure of getfilecon if it's due to ENOTSUP. * src/ls.c (gobble_file): Factor out three small blocks using getfilecon and lgetfilecon. Don't ignore return value from getfilecon and lgetfilecon. * src/ls.c (print_long_format): Don't use ?: (empty 2nd arg with C ternary operator). (print_scontext_format): Likewise. (print_scontext): Declare to be "bool", not int. Adjust uses. * src/Makefile.am (dir_LDADD, ls_LDADD, vdir_LDADD): Add $(LIB_SELINUX). * tests/misc/chcon: New file. * tests/misc/chcon-fail: New file. * tests/Makefile.am (check-root): Run new, root-only misc/chcon test. * tests/misc/Makefile.am (TESTS): Add chcon and chcon-fail. * tests/misc/Makefile.am (TESTS): Add selinux. * tests/misc/selinux: New file. * tests/help-version: Skip chcon. --- ChangeLog | 19 --------- src/Makefile.am | 6 +-- src/ls.c | 90 ++++++++++++++++++++++++++++++----------- tests/Makefile.am | 4 +- tests/chmod/equals | 4 +- tests/cp/cp-parents | 6 +-- tests/cp/fail-perm | 5 +-- tests/cp/link-preserve | 2 +- tests/help-version | 6 +-- tests/install/basic-1 | 5 +-- tests/misc/Makefile.am | 5 ++- tests/misc/chcon | 73 +++++++++++++++++++++++++++++++++ tests/misc/chcon-fail | 41 +++++++++++++++++++ tests/misc/mknod | 6 +-- tests/misc/selinux | 62 ++++++++++++++++++++++++++++ tests/mkdir/parents | 8 ++-- tests/mkdir/special-1 | 8 ++-- tests/mv/partition-perm | 2 +- 18 files changed, 278 insertions(+), 74 deletions(-) create mode 100755 tests/misc/chcon create mode 100755 tests/misc/chcon-fail create mode 100755 tests/misc/selinux diff --git a/ChangeLog b/ChangeLog index b6816681e1..2b8a753955 100644 --- a/ChangeLog +++ b/ChangeLog @@ -789,25 +789,6 @@ 2007-01-04 Jim Meyering - * man/chcon.x: New file. - * man/Makefile.am: Build chcon.1. - - New program: chcon - * gl/modules/selinux-at: New module. Check for libselinux and set - LIB_SELINUX here, unconditionally, rather than depending on - the configure-time --enable-selinux option. - * gl/modules/selinux-h: New module. - * bootstrap.conf (gnulib_modules): Add selinux-at. - * gl/lib/selinux-at.c, gl/lib/selinux-at.h: New files. - * gl/lib/se-selinux_.h: New file. - * gl/lib/se-context_.h: New file. - * gl/m4/selinux-selinux-h.m4: New file. - * gl/m4/selinux-context-h.m4: New file. - * src/Makefile.am (bin_PROGRAMS): Add chcon. - (chcon_LDADD): Define. - * README: Add chcon to the list of programs. - * src/chcon.c: Rewrite the original (Red Hat) chcon to use fts. - * Makefile.cfg (local-checks-to-skip): Skip strftime-check, in case you don't have convenient access to glibc info documentation. diff --git a/src/Makefile.am b/src/Makefile.am index 8b6fb4c360..b49fc5d148 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -72,12 +72,12 @@ __LDADD = $(LDADD) $(LIB_EACCESS) # for clock_gettime and fdatasync dd_LDADD = $(LDADD) $(LIB_GETHRXTIME) $(LIB_FDATASYNC) -dir_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) -ls_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) +dir_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) $(LIB_SELINUX) +ls_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) $(LIB_SELINUX) pr_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) shred_LDADD = $(LDADD) $(LIB_GETHRXTIME) $(LIB_FDATASYNC) shuf_LDADD = $(LDADD) $(LIB_GETHRXTIME) -vdir_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) +vdir_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) $(LIB_SELINUX) ## If necessary, add -lm to resolve use of pow in lib/strtod.c. sort_LDADD = $(LDADD) $(POW_LIB) $(LIB_GETHRXTIME) diff --git a/src/ls.c b/src/ls.c index 3d48900803..66fb02a31c 100644 --- a/src/ls.c +++ b/src/ls.c @@ -61,6 +61,7 @@ #include #include #include +#include /* Use SA_NOCLDSTOP as a proxy for whether the sigaction machinery is present. */ @@ -167,24 +168,20 @@ struct fileinfo zero. */ mode_t linkmode; + /* SELinux security context. */ + security_context_t scontext; + bool stat_ok; /* For symbolic link and color printing, true if linked-to file exists, otherwise false. */ bool linkok; -#if USE_ACL - /* For long listings, true if the file has an access control list. */ + /* For long listings, true if the file has an access control list, + or an SELinux security context. */ bool have_acl; -#endif }; -#if USE_ACL -# define FILE_HAS_ACL(F) ((F)->have_acl) -#else -# define FILE_HAS_ACL(F) 0 -#endif - #define LEN_STR_PAIR(s) sizeof (s) - 1, s /* Null is a valid character in a color indicator (think about Epson @@ -320,14 +317,12 @@ static struct pending *pending_dirs; static time_t current_time = TYPE_MINIMUM (time_t); static int current_time_ns = -1; +static bool print_scontext; + /* Whether any of the files has an ACL. This affects the width of the mode column. */ -#if USE_ACL static bool any_has_acl; -#else -enum { any_has_acl = false }; -#endif /* The number of columns to use for columns containing inode numbers, block sizes, link counts, owners, groups, authors, major device @@ -336,6 +331,7 @@ enum { any_has_acl = false }; static int inode_number_width; static int block_size_width; static int nlink_width; +static int scontext_width; static int owner_width; static int group_width; static int author_width; @@ -787,6 +783,7 @@ static struct option const long_options[] = {"time-style", required_argument, NULL, TIME_STYLE_OPTION}, {"color", optional_argument, NULL, COLOR_OPTION}, {"block-size", required_argument, NULL, BLOCK_SIZE_OPTION}, + {"context", no_argument, 0, 'Z'}, {"author", no_argument, NULL, AUTHOR_OPTION}, {GETOPT_HELP_OPTION_DECL}, {GETOPT_VERSION_OPTION_DECL}, @@ -1246,6 +1243,7 @@ main (int argc, char **argv) format_needs_stat = sort_type == sort_time || sort_type == sort_size || format == long_format + || print_scontext || print_block_size; format_needs_type = (! format_needs_stat && (recursive @@ -1439,6 +1437,7 @@ decode_switches (int argc, char **argv) ignore_mode = IGNORE_DEFAULT; ignore_patterns = NULL; hide_patterns = NULL; + print_scontext = false; /* FIXME: put this in a function. */ { @@ -1514,7 +1513,7 @@ decode_switches (int argc, char **argv) } while ((c = getopt_long (argc, argv, - "abcdfghiklmnopqrstuvw:xABCDFGHI:LNQRST:UX1", + "abcdfghiklmnopqrstuvw:xABCDFGHI:LNQRST:UXZ1", long_options, NULL)) != -1) { switch (c) @@ -1813,6 +1812,10 @@ decode_switches (int argc, char **argv) file_output_block_size = output_block_size = 1; break; + case 'Z': + print_scontext = true; + break; + case_GETOPT_HELP_CHAR; case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); @@ -2516,18 +2519,22 @@ clear_files (void) struct fileinfo *f = sorted_file[i]; free (f->name); free (f->linkname); + if (f->scontext) + { + freecon (f->scontext); + f->scontext = NULL; + } } cwd_n_used = 0; -#if USE_ACL any_has_acl = false; -#endif inode_number_width = 0; block_size_width = 0; nlink_width = 0; owner_width = 0; group_width = 0; author_width = 0; + scontext_width = 0; major_device_number_width = 0; minor_device_number_width = 0; file_size_width = 0; @@ -2591,7 +2598,7 @@ gobble_file (char const *name, enum filetype type, ino_t inode, { /* Absolute name of this file. */ char *absolute_name; - + bool do_deref; int err; if (name[0] == '/' || dirname[0] == 0) @@ -2606,6 +2613,7 @@ gobble_file (char const *name, enum filetype type, ino_t inode, { case DEREF_ALWAYS: err = stat (absolute_name, &f->stat); + do_deref = true; break; case DEREF_COMMAND_LINE_ARGUMENTS: @@ -2614,6 +2622,7 @@ gobble_file (char const *name, enum filetype type, ino_t inode, { bool need_lstat; err = stat (absolute_name, &f->stat); + do_deref = true; if (dereference == DEREF_COMMAND_LINE_ARGUMENTS) break; @@ -2632,9 +2641,27 @@ gobble_file (char const *name, enum filetype type, ino_t inode, default: /* DEREF_NEVER */ err = lstat (absolute_name, &f->stat); + do_deref = false; break; } + bool file_has_security_context = false; + if (err == 0 && (format == long_format || print_scontext)) + { + int attr_len = (do_deref + ? getfilecon (absolute_name, &f->scontext) + : lgetfilecon (absolute_name, &f->scontext)); + err = (attr_len < 0); + file_has_security_context = (err == 0); + + /* When requesting security context information, don't make + ls fail just because the file (even a command line argument) + isn't on the right type of file system. I.e., a getfilecon + failure isn't in the same class as a stat failure. */ + if (err && errno == ENOTSUP) + err = 0; + } + if (err != 0) { /* Failure to stat a command line argument leads to @@ -2653,16 +2680,14 @@ gobble_file (char const *name, enum filetype type, ino_t inode, f->stat_ok = true; -#if USE_ACL if (format == long_format) { int n = file_has_acl (absolute_name, &f->stat); - f->have_acl = (0 < n); + f->have_acl = (0 < n || file_has_security_context); any_has_acl |= f->have_acl; if (n < 0) error (0, errno, "%s", quotearg_colon (absolute_name)); } -#endif if (S_ISLNK (f->stat.st_mode) && (format == long_format || check_symlink_color)) @@ -2738,6 +2763,13 @@ gobble_file (char const *name, enum filetype type, ino_t inode, author_width = len; } + if (print_scontext) + { + int len = f->scontext ? strlen (f->scontext) : 0; + if (scontext_width < len) + scontext_width = len; + } + { char buf[INT_BUFSIZE_BOUND (uintmax_t)]; int len = strlen (umaxtostr (f->stat.st_nlink, buf)); @@ -3387,7 +3419,7 @@ print_long_format (const struct fileinfo *f) struct tm *when_local; /* Compute the mode string, except remove the trailing space if no - files in this directory have ACLs. */ + file in this directory has an ACL or SELinux security context. */ if (f->stat_ok) filemodestring (&f->stat, modebuf); else @@ -3398,7 +3430,7 @@ print_long_format (const struct fileinfo *f) } if (! any_has_acl) modebuf[10] = '\0'; - else if (FILE_HAS_ACL (f)) + else if (f->have_acl) modebuf[10] = '+'; switch (time_type) @@ -3463,7 +3495,7 @@ print_long_format (const struct fileinfo *f) DIRED_INDENT (); - if (print_owner | print_group | print_author) + if (print_owner | print_group | print_author | print_scontext) { DIRED_FPUTS (buf, stdout, p - buf); @@ -3476,6 +3508,10 @@ print_long_format (const struct fileinfo *f) if (print_author) format_user (f->stat.st_author, author_width, f->stat_ok); + if (print_scontext) + format_user_or_group ((f->scontext ? f->scontext : "?"), + 0, scontext_width); + p = buf; } @@ -3812,6 +3848,10 @@ print_file_name_and_frills (const struct fileinfo *f) human_readable (ST_NBLOCKS (f->stat), buf, human_output_opts, ST_NBLOCKSIZE, output_block_size)); + if (print_scontext) + printf ("%*s ", format == with_commas ? 0 : scontext_width, + (f->scontext ? f->scontext : "?")); + print_name_with_quoting (f->name, FILE_OR_LINK_MODE (f), f->linkok, f->stat_ok, f->filetype, NULL); @@ -3975,6 +4015,9 @@ length_of_file_name_and_frills (const struct fileinfo *f) output_block_size)) : block_size_width); + if (print_scontext) + len += 1 + (format == with_commas ? strlen (f->scontext) : scontext_width); + quote_name (NULL, f->name, filename_quoting_options, &name_width); len += name_width; @@ -4403,6 +4446,7 @@ Mandatory arguments to long options are mandatory for short options too.\n\ -w, --width=COLS assume screen width instead of current value\n\ -x list entries by lines instead of by columns\n\ -X sort alphabetically by entry extension\n\ + -Z, --context print any SELinux security context of each file\n\ -1 list one file per line\n\ "), stdout); fputs (HELP_OPTION_DESCRIPTION, stdout); diff --git a/tests/Makefile.am b/tests/Makefile.am index aaf0a29082..b7e30cc66e 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -30,7 +30,7 @@ SUBDIRS = \ tsort unexpand uniq wc ## N O T E :: Please do not add new directories. -all_t = t1 t2 t3 t4 t5 t6 t7 +all_t = t1 t2 t3 t4 t5 t6 t7 t8 .PHONY: check-root $(all_t) check-root: $(all_t) @@ -48,6 +48,8 @@ t6: cd rm && $(MAKE) check TESTS=one-file-system t7: cd ls && $(MAKE) check TESTS=nameless-uid +t8: + cd misc && $(MAKE) check TESTS=chcon check-recursive: root-hint diff --git a/tests/chmod/equals b/tests/chmod/equals index dfaad17e32..bed16fb3ea 100755 --- a/tests/chmod/equals +++ b/tests/chmod/equals @@ -51,7 +51,7 @@ for src in u g o; do for dest in u g o; do test $dest = $src && continue chmod a=,$src=rwx,$dest=$src,$src= f || fail=1 - set _ `ls -l f`; shift; actual_perms=$1 + actual_perms=`ls -l f|cut -b-10` expected_perms=`eval 'echo \$expected_'$dest` test "$actual_perms" = "$expected_perms" || fail=1 done @@ -59,7 +59,7 @@ done umask 027 chmod a=,u=rwx,=u f || fail=1 -set _ `ls -l f`; shift; actual_perms=$1 +actual_perms=`ls -l f|cut -b-10` test "$actual_perms" = "-rwxr-x---" || fail=1 (exit $fail); exit $fail diff --git a/tests/cp/cp-parents b/tests/cp/cp-parents index 6c123d225b..384f6c9265 100755 --- a/tests/cp/cp-parents +++ b/tests/cp/cp-parents @@ -75,8 +75,8 @@ test -d d/f && fail=1 # Check that re_protect works. chmod go=w d/a cp -a --parents d/a/b/c e || fail=1 -p=`ls -ld e/d|sed 's/ .*//'`; case $p in drwxr-xr-x);; *) fail=1;; esac -p=`ls -ld e/d/a|sed 's/ .*//'`; case $p in drwx-w--w-);; *) fail=1;; esac -p=`ls -ld e/d/a/b/c|sed 's/ .*//'`; case $p in drwxr-xr-x);; *) fail=1;; esac +p=`ls -ld e/d|cut -b-10`; case $p in drwxr-xr-x);; *) fail=1;; esac +p=`ls -ld e/d/a|cut -b-10`; case $p in drwx-w--w-);; *) fail=1;; esac +p=`ls -ld e/d/a/b/c|cut -b-10`; case $p in drwxr-xr-x);; *) fail=1;; esac (exit $fail); exit $fail diff --git a/tests/cp/fail-perm b/tests/cp/fail-perm index 5fff090cdb..a7486b6afd 100755 --- a/tests/cp/fail-perm +++ b/tests/cp/fail-perm @@ -52,9 +52,8 @@ cp -pR D DD > /dev/null 2>&1 && fail=1 # Permissions on DD must be `dr-x------' -set X `ls -ld DD` -shift -test "$1" = dr-x------ || fail=1 +mode=`ls -ld DD|cut -b-10` +test "$mode" = dr-x------ || fail=1 chmod 0 D ln -s D/D symlink diff --git a/tests/cp/link-preserve b/tests/cp/link-preserve index 8fec65dea6..e0138e4b69 100755 --- a/tests/cp/link-preserve +++ b/tests/cp/link-preserve @@ -98,7 +98,7 @@ rm -rf a b c d touch a; chmod 731 a umask 077 cp -a --no-preserve=mode a b -set _ `ls -l b`; shift; mode=$1 +mode=`ls -l b|cut -b-10` test "$mode" = "-rwx------" || fail=1 umask 022 # -------------------------------------- diff --git a/tests/help-version b/tests/help-version index c17cc493b5..d3c9bfb187 100755 --- a/tests/help-version +++ b/tests/help-version @@ -2,8 +2,7 @@ # Make sure all these programs work properly # when invoked with --help or --version. -# Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software -# Foundation, Inc. +# Copyright (C) 2000-2007 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 @@ -72,6 +71,7 @@ for lang in C fr da; do # Skip `test'; it doesn't accept --help or --version. test $i = test && continue; + test $i = chcon && continue; # false fails even when invoked with --help or --version. if test $i = false; then @@ -197,7 +197,7 @@ lbracket_args=": ]" for i in $all_programs; do # Skip these. - case $i in chroot|stty|tty|false) continue;; esac + case $i in chroot|stty|tty|false|chcon) continue;; esac rm -rf $tmp_in $tmp_in2 $tmp_dir $tmp_out echo > $tmp_in diff --git a/tests/install/basic-1 b/tests/install/basic-1 index 78a8375520..5609b44382 100755 --- a/tests/install/basic-1 +++ b/tests/install/basic-1 @@ -98,9 +98,8 @@ ginstall -s -c -m 555 $dd $dir || fail=1 test -f $dd || fail=1 # Make sure that the destination file has the requested permissions. -set X `ls -l $dir/$dd` -shift -test "$1" = -r-xr-xr-x || fail=1 +mode=`ls -l $dir/$dd|cut -b-10` +test "$mode" = -r-xr-xr-x || fail=1 # These failed in coreutils CVS from 2004-06-25 to 2004-08-11. ginstall -d . || fail=1 diff --git a/tests/misc/Makefile.am b/tests/misc/Makefile.am index 4d6a754a8c..e4a15cf9e8 100644 --- a/tests/misc/Makefile.am +++ b/tests/misc/Makefile.am @@ -1,6 +1,6 @@ # Make miscellaneous coreutils tests. -*-Makefile-*- -# Copyright (C) 200-2007 Free Software Foundation, Inc. +# Copyright (C) 2001-2007 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 @@ -43,6 +43,9 @@ TESTS = \ pr \ df-P \ pwd-unreadable-parent \ + chcon \ + chcon-fail \ + selinux \ cut \ wc-files0-from \ wc-files0 \ diff --git a/tests/misc/chcon b/tests/misc/chcon new file mode 100755 index 0000000000..88c009bafd --- /dev/null +++ b/tests/misc/chcon @@ -0,0 +1,73 @@ +#!/bin/sh +# exercise chcon + +if test "$VERBOSE" = yes; then + set -x + chcon --version +fi + +. $srcdir/../lang-default +PRIV_CHECK_ARG=require-root . $srcdir/../priv-check + +pwd=`pwd` +t0=`echo "$0"|sed 's,.*/,,'`.tmp; tmp=$t0/$$ +trap 'status=$?; cd $pwd; chmod -R u+rwx $t0; rm -rf $t0 && exit $status' 0 +trap '(exit $?); exit $?' 1 2 13 15 + +framework_failure=0 +mkdir -p $tmp || framework_failure=1 +cd $tmp || framework_failure=1 +mkdir -p d/sub/s2 || framework_failure=1 +touch f g d/sub/1 d/sub/2 || framework_failure=1 + +if test $framework_failure = 1; then + echo "$0: failure in testing framework" 1>&2 + (exit 1); exit 1 +fi + +fail=0 + +# Set to a specified context. +u1=root +r1=object_r +t1=tmp_t +ctx=$u1:$r1:$t1 +chcon $ctx f || fail=1 +stat --printf='f|%C\n' f > out || fail=1 + +# Use --reference. +chcon --ref=f g || fail=1 +stat --printf='g|%C\n' g >> out || fail=1 + +# Change the individual parts of the context, one by one. +u2=user_u +r2=object_r +t2=file_t +l2=SystemLow-SystemHigh +for i in --user=$u2 --role=$r2 --type=$t2 --range=$l2; do + chcon $i f || fail=1 + stat --printf="f|$i|"'%C\n' f >> out || fail=1 +done + +# Same, but change back using the short-named options. +for i in -u$u1 -r$r1 -t$t1; do + chcon $i f || fail=1 + stat --printf="f|$i|"'%C\n' f >> out || fail=1 +done + +cat < exp || fail=1 +f|$ctx +g|$ctx +f|--user=$u2|$u2:$r1:$t1 +f|--role=$r2|$u2:$r2:$t1 +f|--type=$t2|$u2:$r2:$t2 +f|--range=$l2|$u2:$r2:$t2:$l2 +f|-uroot|root:object_r:file_t:SystemLow-SystemHigh +f|-robject_r|root:object_r:file_t:SystemLow-SystemHigh +f|-ttmp_t|root:object_r:tmp_t:SystemLow-SystemHigh +EOF + +cmp out exp || fail=1 +test $fail = 1 && diff out exp 2> /dev/null + +(exit $fail); exit $fail diff --git a/tests/misc/chcon-fail b/tests/misc/chcon-fail new file mode 100755 index 0000000000..d639e86b56 --- /dev/null +++ b/tests/misc/chcon-fail @@ -0,0 +1,41 @@ +#!/bin/sh +# Ensure that chcon fails when it should. +# These tests don't use any actual SE Linux syscalls. + +if test "$VERBOSE" = yes; then + set -x + chcon --version +fi + +. $srcdir/../lang-default + +pwd=`pwd` +t0=`echo "$0"|sed 's,.*/,,'`.tmp; tmp=$t0/$$ +trap 'status=$?; cd $pwd; chmod -R u+rwx $t0; rm -rf $t0 && exit $status' 0 +trap '(exit $?); exit $?' 1 2 13 15 + +framework_failure=0 +mkdir -p $tmp || framework_failure=1 +cd $tmp || framework_failure=1 + +if test $framework_failure = 1; then + echo "$0: failure in testing framework" 1>&2 + (exit 1); exit 1 +fi + +fail=0 + +# neither context nor file +chcon 2> /dev/null && fail=1 + +# No file +chcon CON 2> /dev/null && fail=1 + +# No file +touch f +chcon --reference=f 2> /dev/null && fail=1 + +# No file +chcon -u anyone 2> /dev/null && fail=1 + +(exit $fail); exit $fail diff --git a/tests/misc/mknod b/tests/misc/mknod index 12cb0a6230..d7fb22eb07 100755 --- a/tests/misc/mknod +++ b/tests/misc/mknod @@ -42,15 +42,15 @@ fail=0 umask 777 mknod -m 734 f1 p || fail=1 -set _ `ls -dgo f1`; shift; mode=$1 +mode=`ls -dgo f1|cut -b-10` test $mode = prwx-wxr-- || fail=1 mkfifo -m 734 f2 || fail=1 -set _ `ls -dgo f2`; shift; mode=$1 +mode=`ls -dgo f2|cut -b-10` test $mode = prwx-wxr-- || fail=1 mkdir -m 734 f3 || fail=1 -set _ `ls -dgo f3`; shift; mode=$1 +mode=`ls -dgo f3|cut -b-10` test $mode = drwx-wxr-- || test $mode = drwx-wsr-- || fail=1 (exit $fail); exit $fail diff --git a/tests/misc/selinux b/tests/misc/selinux new file mode 100755 index 0000000000..1207161fd8 --- /dev/null +++ b/tests/misc/selinux @@ -0,0 +1,62 @@ +#!/bin/sh +# Test SELinux-related options. + +if test "$VERBOSE" = yes; then + set -x + ls --version +fi + +. $srcdir/../envvar-check +. $srcdir/../lang-default +PRIV_CHECK_ARG=require-non-root . $srcdir/../priv-check + +test "`ls -Zd .`" = '? .' && + { + echo "$0: skipping this test; this system lacks SELinux support" 1>&2 + exit 77 + } + +pwd=`pwd` +t0=`echo "$0"|sed 's,.*/,,'`.tmp; tmp=$t0/$$ +trap 'status=$?; cd $pwd; chmod -R u+rwx $t0; rm -rf $t0 && exit $status' 0 +trap '(exit $?); exit $?' 1 2 13 15 + +framework_failure=0 +mkdir -p $tmp || framework_failure=1 +cd $tmp || framework_failure=1 + +# Create a regular file, dir, fifo. +touch f || framework_failure=1 +mkdir d s1 s2 || framework_failure=1 +mkfifo p || framework_failure=1 + +if test $framework_failure = 1; then + echo "$0: failure in testing framework" 1>&2 + (exit 1); exit 1 +fi + +fail=0 + +ctx=root:object_r:tmp_t +# FIXME, what if $ctx is no different from the default. Not likely. +# give each a different context, via chcon +chcon $ctx f d p || fail=1 +# inspect that context with both ls -Z and stat. +for i in d f p; do + c=`ls -dogZ $i|cut -d' ' -f3`; test x$c = x$ctx || fail=1 + c=`stat --printf %C $i`; test x$c = x$ctx || fail=1 +done + +# Copy each to a new directory and ensure that context is preserved. +cp -r --preserve=all d f p s1 || fail=1 +for i in d f p; do + c=`stat --printf %C s1/$i`; test x$c = x$ctx || fail=1 +done + +# Now, move each to a new directory and ensure that context is preserved. +mv d f p s2 || fail=1 +for i in d f p; do + c=`stat --printf %C s2/$i`; test x$c = x$ctx || fail=1 +done + +(exit $fail); exit $fail diff --git a/tests/mkdir/parents b/tests/mkdir/parents index f0ead6ee87..d956e39d7e 100755 --- a/tests/mkdir/parents +++ b/tests/mkdir/parents @@ -65,12 +65,12 @@ d_mode_arg=`"$abs_srcdir/../rwx-to-mode" $d_mode_str` mkdir -p -m $d_mode_arg a/b/c/d # Make sure the permissions of `a' haven't been changed. -p=`ls -ld a|sed 's/ .*//'`; case $p in $mode_str);; *) fail=1;; esac +p=`ls -ld a|cut -b-10`; case $p in $mode_str);; *) fail=1;; esac # `b's and `c's should reflect the umask -p=`ls -ld a/b|sed 's/ .*//'`; case $p in drwx------);; *) fail=1;; esac -p=`ls -ld a/b/c|sed 's/ .*//'`; case $p in drwx------);; *) fail=1;; esac +p=`ls -ld a/b|cut -b-10`; case $p in drwx------);; *) fail=1;; esac +p=`ls -ld a/b/c|cut -b-10`; case $p in drwx------);; *) fail=1;; esac # `d's perms are determined by the -m argument. -p=`ls -ld a/b/c/d|sed 's/ .*//'`; case $p in $d_mode_str);; *) fail=1;; esac +p=`ls -ld a/b/c/d|cut -b-10`; case $p in $d_mode_str);; *) fail=1;; esac (exit $fail); exit $fail diff --git a/tests/mkdir/special-1 b/tests/mkdir/special-1 index a5b65043bf..7f8dc7405d 100755 --- a/tests/mkdir/special-1 +++ b/tests/mkdir/special-1 @@ -33,8 +33,8 @@ output_mode_string=drwxr-x-wT mkdir -m$set_mode_string $tmp || fail=1 test -d $tmp || fail=1 -set -- `ls -ld $tmp` -case "$1" in +mode=`ls -ld $tmp|cut -b-10` +case "$mode" in $output_mode_string) ;; *) fail=1 ;; esac @@ -49,8 +49,8 @@ mkdir -m$set_mode_string $tmp2 2> /dev/null && fail=1 mkdir --parents -m$set_mode_string $tmp2 || fail=1 test -d $tmp2 || fail=1 -set -- `ls -ld $tmp2` -case "$1" in +mode=`ls -ld $tmp2|cut -b-10` +case "$mode" in $output_mode_string) ;; *) fail=1 ;; esac diff --git a/tests/mv/partition-perm b/tests/mv/partition-perm index 969dc74cd7..129146a80a 100755 --- a/tests/mv/partition-perm +++ b/tests/mv/partition-perm @@ -49,7 +49,7 @@ test -f file && fail=1 test -f $other_partition_tmpdir/file || fail=1 # This would have failed with the mv from fileutils-4.0i. -set _ `ls -l $other_partition_tmpdir/file`; shift; mode=$1 +mode=`ls -l $other_partition_tmpdir/file|cut -b-10` test "$mode" = "-rwxrwxrwx" || fail=1 exit $fail From 32386059981a826de6827307b46c958c50a6077c Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Sat, 6 Jan 2007 18:38:51 +0100 Subject: [PATCH 05/30] add chcon --- .cvsignore | 2 +- .gitignore | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.cvsignore b/.cvsignore index bf902e9136..577887b0c9 100644 --- a/.cvsignore +++ b/.cvsignore @@ -8,7 +8,7 @@ Makefile Makefile.in THANKS-to-translators aclocal.m4 +chcon config.status configure coreutils-* -chcon diff --git a/.gitignore b/.gitignore index 213c2853dc..0937c2b117 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ Makefile.in THANKS-to-translators aclocal.m4 autom4te.cache +chcon config.cache config.h config.hin @@ -37,4 +38,3 @@ tests/test/test-tests tests/tr/tr-tests tests/uniq/uniq-tests tests/wc/wc-tests -chcon From 2912097e82c7ba3532a2a275c83995ef9b38b23e Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Sun, 7 Jan 2007 09:15:42 +0100 Subject: [PATCH 06/30] stat: Add support for SELinux in the form of a %C format directive. * src/stat.c (follow_links): Make this variable file-global. (out_file_context): New function. (print_statfs): Honor %C. (print_stat): Honor %C. (do_stat): Remove follow_links parameter. (usage): Document the two %C directives. (main): Accept -Z (though it's a no-op). * src/Makefile.am (stat_LDADD): Define. --- src/Makefile.am | 2 ++ src/stat.c | 47 +++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index b49fc5d148..53ead236de 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -112,6 +112,8 @@ cp_LDADD += $(LIB_ACL) mv_LDADD += $(LIB_ACL) ginstall_LDADD += $(LIB_ACL) +stat_LDADD = $(LDADD) $(LIB_SELINUX) + $(PROGRAMS): ../lib/libcoreutils.a SUFFIXES = .sh diff --git a/src/stat.c b/src/stat.c index ca84236775..e3b4f09724 100644 --- a/src/stat.c +++ b/src/stat.c @@ -54,6 +54,7 @@ #elif HAVE_OS_H /* BeOS */ # include #endif +#include #include "system.h" @@ -158,6 +159,7 @@ enum }; static struct option const long_options[] = { + {"context", no_argument, 0, 'Z'}, {"dereference", no_argument, NULL, 'L'}, {"file-system", no_argument, NULL, 'f'}, {"filesystem", no_argument, NULL, 'f'}, /* obsolete and undocumented alias */ @@ -171,6 +173,9 @@ static struct option const long_options[] = { char *program_name; +/* Whether to follow symbolic links; True for --dereference (-L). */ +static bool follow_links; + /* Whether to interpret backslash-escape sequences. True for --printf=FMT, not for --format=FMT (-c). */ static bool interpret_backslash_escapes; @@ -394,6 +399,26 @@ out_uint_x (char *pformat, size_t prefix_len, uintmax_t arg) printf (pformat, arg); } +/* Very specialized function (modifies FORMAT), just so as to avoid + duplicating this code between both print_statfs and print_stat. */ +static void +out_file_context (char const *filename, char *pformat, size_t prefix_len) +{ + char *scontext; + if ((follow_links + ? getfilecon (filename, &scontext) + : lgetfilecon (filename, &scontext)) < 0) + { + error (0, errno, _("failed to get security context of %s"), + quote (filename)); + scontext = NULL; + } + strcpy (pformat + prefix_len, "s"); + printf (pformat, (scontext ? scontext : "?")); + if (scontext) + freecon (scontext); +} + /* print statfs info */ static void print_statfs (char *pformat, size_t prefix_len, char m, char const *filename, @@ -472,7 +497,9 @@ print_statfs (char *pformat, size_t prefix_len, char m, char const *filename, case 'd': out_int (pformat, prefix_len, statfsbuf->f_ffree); break; - + case 'C': + out_file_context (filename, pformat, prefix_len); + break; default: fputc ('?', stdout); break; @@ -595,6 +622,9 @@ print_stat (char *pformat, size_t prefix_len, char m, else out_uint (pformat, prefix_len, statbuf->st_ctime); break; + case 'C': + out_file_context (filename, pformat, prefix_len); + break; default: fputc ('?', stdout); break; @@ -774,8 +804,7 @@ do_statfs (char const *filename, bool terse, char const *format) /* stat the file and print what we find */ static bool -do_stat (char const *filename, bool follow_links, bool terse, - char const *format) +do_stat (char const *filename, bool terse, char const *format) { struct stat statbuf; @@ -853,6 +882,7 @@ The valid format sequences for files (without --file-system):\n\ %A Access rights in human readable form\n\ %b Number of blocks allocated (see %B)\n\ %B The size in bytes of each block reported by %b\n\ + %C SELinux security context string\n\ "), stdout); fputs (_("\ %d Device number in decimal\n\ @@ -892,6 +922,7 @@ Valid format sequences for file systems:\n\ %c Total file nodes in file system\n\ %d Free file nodes in file system\n\ %f Free blocks in file system\n\ + %C SELinux security context string\n\ "), stdout); fputs (_("\ %i File System ID in hex\n\ @@ -913,7 +944,6 @@ main (int argc, char *argv[]) { int c; int i; - bool follow_links = false; bool fs = false; bool terse = false; char *format = NULL; @@ -927,7 +957,7 @@ main (int argc, char *argv[]) atexit (close_stdout); - while ((c = getopt_long (argc, argv, "c:fLt", long_options, NULL)) != -1) + while ((c = getopt_long (argc, argv, "c:fLtZ", long_options, NULL)) != -1) { switch (c) { @@ -955,6 +985,11 @@ main (int argc, char *argv[]) terse = true; break; + case 'Z': /* FIXME: remove in 2008, warn in 2007 */ + /* Ignored, for compatibility with distributions + that implemented this before upstream. */ + break; + case_GETOPT_HELP_CHAR; case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); @@ -973,7 +1008,7 @@ main (int argc, char *argv[]) for (i = optind; i < argc; i++) ok &= (fs ? do_statfs (argv[i], terse, format) - : do_stat (argv[i], follow_links, terse, format)); + : do_stat (argv[i], terse, format)); exit (ok ? EXIT_SUCCESS : EXIT_FAILURE); } From 2aab34d486bff1adafd1730a433acb7a7351834c Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Sat, 13 Jan 2007 12:12:03 +0100 Subject: [PATCH 07/30] Keep SELinux-related ChangLog entries here. --- ChangeLog-selinux | 92 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 ChangeLog-selinux diff --git a/ChangeLog-selinux b/ChangeLog-selinux new file mode 100644 index 0000000000..cb5f4b3984 --- /dev/null +++ b/ChangeLog-selinux @@ -0,0 +1,92 @@ +2007-01-06 Jim Meyering + + stat: Add support for SELinux in the form of a %C format directive. + * src/stat.c (follow_links): Make this variable file-global. + (out_file_context): New function. + (print_statfs): Honor %C. + (print_stat): Honor %C. + (do_stat): Remove follow_links parameter. + (usage): Document the two %C directives. + (main): Accept -Z (though it's a no-op). + * src/Makefile.am (stat_LDADD): Define. + + ls: Add support for SELinux and a slightly modified -Z option. + I started with the patches from Red Hat. + The entries below tell how the code evolved. + + * src/ls.c (print_long_format, print_file_name_and_frills): When + there is no security context (due to getfilecon/lgetfilecon failing + with e.g. ENOTSUP), print it as "?", not "". + * src/ls.c (print_file_name_and_frills): Make -Z work without -l. + (length_of_file_name_and_frills): Likewise. + + * src/ls.c: Remove the --lcontext and --scontext options. + Change the way -Z, --context work so that it no longer implies -l. + Thus, -Z -l will work like -lcontext and -Z without -l will work + like --scontext. + + Adjust tests to reflect new 'ls -l' syntax -- affects only + systems with SELinux when operating on a file with no ACL. + These tests assumed that everything before the first space on + each line is the 10-byte mode string. But there may also be a "+" + in the 11th column, just before the space. However, note that this + is not new. The same thing would have happened even without the + change below, when listing a file with an ACL. + * tests/chmod/equals, tests/cp/cp-parents, tests/cp/fail-perm: + * tests/cp/link-preserve, tests/install/basic-1, tests/misc/mknod: + * tests/mkdir/parents, tests/mkdir/special-1, tests/mv/partition-perm: + + Don't make compilation depend on USE_ACL. An SELinux security + context counts as an "alternate access control method", so ls + must output a "+" for each file with a security context. + * src/ls.c [struct fileinfo] (have_acl): Declare unconditionally. + (FILE_HAS_ACL): Remove macro definition. Use f->have_acl directly. + (gobble_file): Record whether a file has a security context, and + update the condition used to determine whether to print the "+". + (gobble_file): Call getfilecon/lgetfilecon also when + format == long_format, so that we get the "+". + + * src/ls.c (gobble_file): Add a comment explaining why (with a + security context option) ls doesn't exit nonzero due to e.g., + getfilecon failing with errno == ENOTSUP. + + * src/ls.c (gobble_file): Ignore failure of getfilecon if it's due + to ENOTSUP. + + * src/ls.c (gobble_file): Factor out three small blocks using + getfilecon and lgetfilecon. + Don't ignore return value from getfilecon and lgetfilecon. + + * src/ls.c (print_long_format): Don't use ?: (empty 2nd arg with C + ternary operator). + (print_scontext_format): Likewise. + (print_scontext): Declare to be "bool", not int. Adjust uses. + + * src/Makefile.am (dir_LDADD, ls_LDADD, vdir_LDADD): Add $(LIB_SELINUX). + + * tests/misc/chcon: New file. + * tests/misc/chcon-fail: New file. + * tests/Makefile.am (check-root): Run new, root-only misc/chcon test. + * tests/misc/Makefile.am (TESTS): Add chcon and chcon-fail. + + * tests/misc/Makefile.am (TESTS): Add selinux. + * tests/misc/selinux: New file. + * tests/help-version: Skip chcon. + * man/chcon.x: New file. + * man/Makefile.am: Build chcon.1. + + New program: chcon + * gl/modules/selinux-at: New module. Check for libselinux and set + LIB_SELINUX here, unconditionally, rather than depending on + the configure-time --enable-selinux option. + * gl/modules/selinux-h: New module. + * bootstrap.conf (gnulib_modules): Add selinux-at. + * gl/lib/selinux-at.c, gl/lib/selinux-at.h: New files. + * gl/lib/se-selinux_.h: New file. + * gl/lib/se-context_.h: New file. + * gl/m4/selinux-selinux-h.m4: New file. + * gl/m4/selinux-context-h.m4: New file. + * src/Makefile.am (bin_PROGRAMS): Add chcon. + (chcon_LDADD): Define. + * README: Add chcon to the list of programs. + * src/chcon.c: Rewrite the original (Red Hat) chcon to use fts. From 0278a4d7d31f31e8eaddbd3653aae524c54ac5bd Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Sat, 13 Jan 2007 16:15:41 +0100 Subject: [PATCH 08/30] id: Add SELinux support: -Z option. * src/id.c (main): Apply patches from Fedora, with these changes: Remove #ifdef WITH_SELINUX. Use error (EXIT_FAILURE, not fprintf+exit(1). * src/Makefile.am (id_LDADD): Define, so as to add $(LIB_SELINUX). --- ChangeLog-selinux | 8 ++++++ src/Makefile.am | 1 + src/id.c | 63 +++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 62 insertions(+), 10 deletions(-) diff --git a/ChangeLog-selinux b/ChangeLog-selinux index cb5f4b3984..4993bd88e6 100644 --- a/ChangeLog-selinux +++ b/ChangeLog-selinux @@ -1,3 +1,11 @@ +2007-01-13 Jim Meyering + + id: Add SELinux support: -Z option. + * src/id.c (main): Apply patches from Fedora, with these changes: + Remove #ifdef WITH_SELINUX. + Use error (EXIT_FAILURE, not fprintf+exit(1). + * src/Makefile.am (id_LDADD): Define, so as to add $(LIB_SELINUX). + 2007-01-06 Jim Meyering stat: Add support for SELinux in the form of a %C format directive. diff --git a/src/Makefile.am b/src/Makefile.am index 53ead236de..a8ce95cd9d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -73,6 +73,7 @@ __LDADD = $(LDADD) $(LIB_EACCESS) # for clock_gettime and fdatasync dd_LDADD = $(LDADD) $(LIB_GETHRXTIME) $(LIB_FDATASYNC) dir_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) $(LIB_SELINUX) +id_LDADD = $(LDADD) $(LIB_SELINUX) ls_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) $(LIB_SELINUX) pr_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) shred_LDADD = $(LDADD) $(LIB_GETHRXTIME) $(LIB_FDATASYNC) diff --git a/src/id.c b/src/id.c index 7abb3e50ca..5adf0102e9 100644 --- a/src/id.c +++ b/src/id.c @@ -1,5 +1,5 @@ /* id -- print real and effective UIDs and GIDs - Copyright (C) 1989-2005 Free Software Foundation, Inc. + Copyright (C) 1989-2007 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 @@ -25,6 +25,7 @@ #include #include #include +#include #include "system.h" #include "error.h" @@ -37,6 +38,9 @@ int getugroups (); +/* If nonzero, output only the SELinux context. -Z */ +static int just_context = 0; + static void print_user (uid_t uid); static void print_group (gid_t gid); static void print_group_list (const char *username); @@ -55,8 +59,13 @@ static gid_t rgid, egid; /* True unless errors have been encountered. */ static bool ok = true; +/* The SELinux context. Start with a known invalid value so print_full_info + knows when `context' has not been set to a meaningful value. */ +static security_context_t context = NULL; + static struct option const longopts[] = { + {"context", no_argument, NULL, 'Z'}, {"group", no_argument, NULL, 'g'}, {"groups", no_argument, NULL, 'G'}, {"name", no_argument, NULL, 'n'}, @@ -80,6 +89,7 @@ usage (int status) Print information for USERNAME, or the current user.\n\ \n\ -a ignore, for compatibility with other versions\n\ + -Z, --context print only the security context of the current user\n\ -g, --group print only the effective group ID\n\ -G, --groups print all group IDs\n\ -n, --name print a name instead of a number, for -ugG\n\ @@ -101,6 +111,7 @@ int main (int argc, char **argv) { int optc; + int selinux_enabled = (is_selinux_enabled () > 0); /* If true, output the list of all group IDs. -G */ bool just_group_list = false; @@ -119,13 +130,22 @@ main (int argc, char **argv) atexit (close_stdout); - while ((optc = getopt_long (argc, argv, "agnruG", longopts, NULL)) != -1) + while ((optc = getopt_long (argc, argv, "agnruGZ", longopts, NULL)) != -1) { switch (optc) { case 'a': /* Ignore -a, for compatibility with SVR4. */ break; + + case 'Z': + /* politely decline if we're not on a selinux-enabled kernel. */ + if (!selinux_enabled) + error (EXIT_FAILURE, 0, _("--context (-Z) can be used only on " + "an SELinux-enabled kernel")); + just_context = 1; + break; + case 'g': just_group = true; break; @@ -148,19 +168,38 @@ main (int argc, char **argv) } } - if (just_user + just_group + just_group_list > 1) - error (EXIT_FAILURE, 0, _("cannot print only user and only group")); - - if (just_user + just_group + just_group_list == 0 && (use_real | use_name)) - error (EXIT_FAILURE, 0, - _("cannot print only names or real IDs in default format")); - - if (argc - optind > 1) + if (1 < argc - optind) { error (0, 0, _("extra operand %s"), quote (argv[optind + 1])); usage (EXIT_FAILURE); } + if (argc - optind == 1 && just_context) + error (EXIT_FAILURE, 0, + _("cannot print security context when user specified")); + + if (just_context && !selinux_enabled) + error (EXIT_FAILURE, 0, _("\ +cannot display context when selinux not enabled or when displaying the id\n\ +of a different user")); + + /* If we are on a selinux-enabled kernel, get our context. + Otherwise, leave the context variable alone - it has + been initialized known invalid value; if we see this invalid + value later, we will know we are on a non-selinux kernel. */ + if (selinux_enabled) + { + if (getcon (&context) && just_context) + error (EXIT_FAILURE, 0, _("can't get process context")); + } + + if (just_user + just_group + just_group_list + just_context > 1) + error (EXIT_FAILURE, 0, _("cannot print \"only\" of more than one choice")); + + if (just_user + just_group + just_group_list == 0 && (use_real | use_name)) + error (EXIT_FAILURE, 0, + _("cannot print only names or real IDs in default format")); + if (argc - optind == 1) { struct passwd *pwd = getpwnam (argv[optind]); @@ -183,6 +222,8 @@ main (int argc, char **argv) print_group (use_real ? rgid : egid); else if (just_group_list) print_group_list (argv[optind]); + else if (just_context) + fputs (context, stdout); else print_full_info (argv[optind]); putchar ('\n'); @@ -385,4 +426,6 @@ print_full_info (const char *username) free (groups); } #endif /* HAVE_GETGROUPS */ + if (context != NULL) + printf (" context=%s", context); } From 10ca93af8f4af7a576f439bf256f281b7a761279 Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Sat, 13 Jan 2007 16:30:04 +0100 Subject: [PATCH 09/30] * POTFILES.in: Add src/chcon.c. --- po/ChangeLog | 4 ++++ po/POTFILES.in | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/po/ChangeLog b/po/ChangeLog index 80fa52a9cf..ad54a0907f 100644 --- a/po/ChangeLog +++ b/po/ChangeLog @@ -1,3 +1,7 @@ +2007-01-13 Jim Meyering + + * POTFILES.in: Add src/chcon.c. + 2006-10-19 Jim Meyering * POTFILES.in: Also include lib/regcomp.c, since it too uses gettext. diff --git a/po/POTFILES.in b/po/POTFILES.in index 3746acc61d..8592947f4a 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1,5 +1,5 @@ # List of files which contain translatable strings. -# Copyright (C) 1996-2006 Free Software Foundation, Inc. +# Copyright (C) 1996-2007 Free Software Foundation, Inc. # These are nominally temporary... lib/acl.c @@ -33,6 +33,7 @@ lib/xstrtol.h src/base64.c src/basename.c src/cat.c +src/chcon.c src/chgrp.c src/chmod.c src/chown-core.c From 5f997760c926a9d0d17bee405abea8406fc40e8b Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Sat, 13 Jan 2007 16:43:07 +0100 Subject: [PATCH 10/30] * src/id.c (main): Tweak id -Z diagnostic. --- ChangeLog-selinux | 2 ++ src/id.c | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ChangeLog-selinux b/ChangeLog-selinux index 4993bd88e6..f8f92c3f4a 100644 --- a/ChangeLog-selinux +++ b/ChangeLog-selinux @@ -1,5 +1,7 @@ 2007-01-13 Jim Meyering + * src/id.c (main): Tweak id -Z diagnostic. + id: Add SELinux support: -Z option. * src/id.c (main): Apply patches from Fedora, with these changes: Remove #ifdef WITH_SELINUX. diff --git a/src/id.c b/src/id.c index 5adf0102e9..cced6ecb07 100644 --- a/src/id.c +++ b/src/id.c @@ -141,8 +141,8 @@ main (int argc, char **argv) case 'Z': /* politely decline if we're not on a selinux-enabled kernel. */ if (!selinux_enabled) - error (EXIT_FAILURE, 0, _("--context (-Z) can be used only on " - "an SELinux-enabled kernel")); + error (EXIT_FAILURE, 0, + _("--context (-Z) works only on an SELinux-enabled kernel")); just_context = 1; break; From 2b5e4cb837509ebc4b9e40284b98799673e92bab Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Sat, 13 Jan 2007 17:13:49 +0100 Subject: [PATCH 11/30] * src/c99-to-c89.diff: Handle a new c99'ism in ls.c. --- ChangeLog-selinux | 2 ++ src/c99-to-c89.diff | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/ChangeLog-selinux b/ChangeLog-selinux index f8f92c3f4a..0d3b8e3b14 100644 --- a/ChangeLog-selinux +++ b/ChangeLog-selinux @@ -1,5 +1,7 @@ 2007-01-13 Jim Meyering + * src/c99-to-c89.diff: Handle a new c99'ism in ls.c. + * src/id.c (main): Tweak id -Z diagnostic. id: Add SELinux support: -Z option. diff --git a/src/c99-to-c89.diff b/src/c99-to-c89.diff index 5545f6a11e..62c7726bf9 100644 --- a/src/c99-to-c89.diff +++ b/src/c99-to-c89.diff @@ -116,3 +116,23 @@ diff -u -p -r1.130 shred.c if (errnum == EIO && 0 <= size && (soff | SECTOR_MASK) < lim) { size_t soff1 = (soff | SECTOR_MASK) + 1; +diff --git a/src/ls.c b/src/ls.c +index b1c0f9a..7d51a61 100644 +--- a/src/ls.c ++++ b/src/ls.c +@@ -2565,6 +2565,7 @@ gobble_file (char const *name, enum filetype type, ino_t inode, + ))))) + + { ++ bool file_has_security_context = false; + /* Absolute name of this file. */ + char *absolute_name; + bool do_deref; +@@ -2614,7 +2615,6 @@ gobble_file (char const *name, enum filetype type, ino_t inode, + break; + } + +- bool file_has_security_context = false; + if (err == 0 && (format == long_format || print_scontext)) + { + int attr_len = (do_deref From 7782da7bf50ba52ac906d9fc754ada495e6172f8 Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Sat, 13 Jan 2007 17:17:19 +0100 Subject: [PATCH 12/30] * gl/lib/selinux-at.c: Remove a use of HAVE_CONFIG_H. --- ChangeLog-selinux | 2 ++ gl/lib/selinux-at.c | 4 +--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ChangeLog-selinux b/ChangeLog-selinux index 0d3b8e3b14..a207774fde 100644 --- a/ChangeLog-selinux +++ b/ChangeLog-selinux @@ -1,5 +1,7 @@ 2007-01-13 Jim Meyering + * gl/lib/selinux-at.c: Remove a use of HAVE_CONFIG_H. + * src/c99-to-c89.diff: Handle a new c99'ism in ls.c. * src/id.c (main): Tweak id -Z diagnostic. diff --git a/gl/lib/selinux-at.c b/gl/lib/selinux-at.c index ebc41ee7ac..9dc0b1c983 100644 --- a/gl/lib/selinux-at.c +++ b/gl/lib/selinux-at.c @@ -17,9 +17,7 @@ /* written by Jim Meyering */ -#ifdef HAVE_CONFIG_H -# include -#endif +#include #include "selinux-at.h" #include "openat.h" From b23de8683468e423df5b33ca3c19d360b9cb447e Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Sat, 13 Jan 2007 17:19:16 +0100 Subject: [PATCH 13/30] * src/chcon.c: Don't include "dirname.h". system.h already includes it. --- ChangeLog-selinux | 2 ++ src/chcon.c | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ChangeLog-selinux b/ChangeLog-selinux index a207774fde..7fc63a130e 100644 --- a/ChangeLog-selinux +++ b/ChangeLog-selinux @@ -1,5 +1,7 @@ 2007-01-13 Jim Meyering + * src/chcon.c: Don't include "dirname.h". system.h already includes it. + * gl/lib/selinux-at.c: Remove a use of HAVE_CONFIG_H. * src/c99-to-c89.diff: Handle a new c99'ism in ls.c. diff --git a/src/chcon.c b/src/chcon.c index 66fda1d961..6eee44bb6d 100644 --- a/src/chcon.c +++ b/src/chcon.c @@ -22,7 +22,6 @@ #include "system.h" #include "dev-ino.h" -#include "dirname.h" #include "error.h" #include "openat.h" #include "quote.h" From 4269a52c1a630284652f7cde9c0873fbe01b3b52 Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Sat, 13 Jan 2007 17:22:57 +0100 Subject: [PATCH 14/30] * src/c99-to-c89.diff: Remove trailing blanks. --- ChangeLog-selinux | 2 ++ src/c99-to-c89.diff | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/ChangeLog-selinux b/ChangeLog-selinux index 7fc63a130e..0424a17e5a 100644 --- a/ChangeLog-selinux +++ b/ChangeLog-selinux @@ -1,5 +1,7 @@ 2007-01-13 Jim Meyering + * src/c99-to-c89.diff: Remove trailing blanks. + * src/chcon.c: Don't include "dirname.h". system.h already includes it. * gl/lib/selinux-at.c: Remove a use of HAVE_CONFIG_H. diff --git a/src/c99-to-c89.diff b/src/c99-to-c89.diff index 62c7726bf9..808ae351fa 100644 --- a/src/c99-to-c89.diff +++ b/src/c99-to-c89.diff @@ -120,18 +120,18 @@ diff --git a/src/ls.c b/src/ls.c index b1c0f9a..7d51a61 100644 --- a/src/ls.c +++ b/src/ls.c -@@ -2565,6 +2565,7 @@ gobble_file (char const *name, enum filetype type, ino_t inode, +@@ -2565,1 +2565,1 @@ gobble_file (char const *name, enum filetype type, ino_t inode, ))))) - + { + bool file_has_security_context = false; /* Absolute name of this file. */ char *absolute_name; bool do_deref; -@@ -2614,7 +2615,6 @@ gobble_file (char const *name, enum filetype type, ino_t inode, +@@ -2614,2 +2615,2 @@ gobble_file (char const *name, enum filetype type, ino_t inode, break; } - + - bool file_has_security_context = false; if (err == 0 && (format == long_format || print_scontext)) { From 1b569150c54e4780862bc613583d342fa0fa8ea0 Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Sat, 13 Jan 2007 17:49:17 +0100 Subject: [PATCH 15/30] * AUTHORS: Add chcon. --- AUTHORS | 1 + ChangeLog-selinux | 2 ++ 2 files changed, 3 insertions(+) diff --git a/AUTHORS b/AUTHORS index 5200633a8f..11e02c5a7f 100644 --- a/AUTHORS +++ b/AUTHORS @@ -4,6 +4,7 @@ each followed by the name(s) of its author(s). base64: Simon Josefsson basename: FIXME unknown cat: Torbjorn Granlund, Richard M. Stallman +chcon: Russell Coker, Jim Meyering chgrp: David MacKenzie, Jim Meyering chmod: David MacKenzie, Jim Meyering chown: David MacKenzie, Jim Meyering diff --git a/ChangeLog-selinux b/ChangeLog-selinux index 0424a17e5a..881e376b7c 100644 --- a/ChangeLog-selinux +++ b/ChangeLog-selinux @@ -1,5 +1,7 @@ 2007-01-13 Jim Meyering + * AUTHORS: Add chcon. + * src/c99-to-c89.diff: Remove trailing blanks. * src/chcon.c: Don't include "dirname.h". system.h already includes it. From ecf70125f94220d8b9d7f6fe2c45ef62cc5c21ca Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Sat, 13 Jan 2007 18:12:01 +0100 Subject: [PATCH 16/30] * src/c99-to-c89.diff: Adjust offsets. --- ChangeLog-selinux | 2 ++ src/c99-to-c89.diff | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ChangeLog-selinux b/ChangeLog-selinux index 881e376b7c..79a2304b8b 100644 --- a/ChangeLog-selinux +++ b/ChangeLog-selinux @@ -1,5 +1,7 @@ 2007-01-13 Jim Meyering + * src/c99-to-c89.diff: Adjust offsets. + * AUTHORS: Add chcon. * src/c99-to-c89.diff: Remove trailing blanks. diff --git a/src/c99-to-c89.diff b/src/c99-to-c89.diff index 808ae351fa..83c00d05c0 100644 --- a/src/c99-to-c89.diff +++ b/src/c99-to-c89.diff @@ -120,7 +120,7 @@ diff --git a/src/ls.c b/src/ls.c index b1c0f9a..7d51a61 100644 --- a/src/ls.c +++ b/src/ls.c -@@ -2565,1 +2565,1 @@ gobble_file (char const *name, enum filetype type, ino_t inode, +@@ -2565,6 +2565,7 @@ gobble_file (char const *name, enum filetype type, ino_t inode, ))))) { @@ -128,7 +128,7 @@ index b1c0f9a..7d51a61 100644 /* Absolute name of this file. */ char *absolute_name; bool do_deref; -@@ -2614,2 +2615,2 @@ gobble_file (char const *name, enum filetype type, ino_t inode, +@@ -2614,7 +2615,6 @@ gobble_file (char const *name, enum filetype type, ino_t inode, break; } From 82d98da7431236987ccb0c5af034c2f12a44e1f3 Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Sat, 13 Jan 2007 18:22:46 +0100 Subject: [PATCH 17/30] * src/ls.c (gobble_file): Don't call getfilecon unless print_scontext. Upon failed getfilecon, accept not just ENOTSUP, but also ENODATA. --- ChangeLog-selinux | 3 +++ src/ls.c | 7 ++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/ChangeLog-selinux b/ChangeLog-selinux index 79a2304b8b..2ef22cef25 100644 --- a/ChangeLog-selinux +++ b/ChangeLog-selinux @@ -1,5 +1,8 @@ 2007-01-13 Jim Meyering + * src/ls.c (gobble_file): Don't call getfilecon unless print_scontext. + Upon failed getfilecon, accept not just ENOTSUP, but also ENODATA. + * src/c99-to-c89.diff: Adjust offsets. * AUTHORS: Add chcon. diff --git a/src/ls.c b/src/ls.c index 66fb02a31c..d421ce246b 100644 --- a/src/ls.c +++ b/src/ls.c @@ -2596,6 +2596,8 @@ gobble_file (char const *name, enum filetype type, ino_t inode, ))))) { + /* FIXME-c99: move this decl "down", once ls.c stabilizes. */ + bool file_has_security_context = false; /* Absolute name of this file. */ char *absolute_name; bool do_deref; @@ -2645,8 +2647,7 @@ gobble_file (char const *name, enum filetype type, ino_t inode, break; } - bool file_has_security_context = false; - if (err == 0 && (format == long_format || print_scontext)) + if (err == 0 && print_scontext) { int attr_len = (do_deref ? getfilecon (absolute_name, &f->scontext) @@ -2658,7 +2659,7 @@ gobble_file (char const *name, enum filetype type, ino_t inode, ls fail just because the file (even a command line argument) isn't on the right type of file system. I.e., a getfilecon failure isn't in the same class as a stat failure. */ - if (err && errno == ENOTSUP) + if (err && (errno == ENOTSUP || errno == ENODATA)) err = 0; } From b64a11dc769efc3af5b50b7d83a48b3e7cecca6f Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Sat, 13 Jan 2007 18:24:06 +0100 Subject: [PATCH 18/30] * src/chcon.c (usage): Split a string literal that was longer than 509. --- ChangeLog-selinux | 2 ++ src/chcon.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/ChangeLog-selinux b/ChangeLog-selinux index 2ef22cef25..fbae67e555 100644 --- a/ChangeLog-selinux +++ b/ChangeLog-selinux @@ -1,5 +1,7 @@ 2007-01-13 Jim Meyering + * src/chcon.c (usage): Split a string literal that was longer than 509. + * src/ls.c (gobble_file): Don't call getfilecon unless print_scontext. Upon failed getfilecon, accept not just ENOTSUP, but also ENODATA. diff --git a/src/chcon.c b/src/chcon.c index 6eee44bb6d..af5b1ad222 100644 --- a/src/chcon.c +++ b/src/chcon.c @@ -375,6 +375,8 @@ With --reference, change the security context of each FILE to that of RFILE.\n\ -c, --changes like verbose but report only when a change is made\n\ -h, --no-dereference affect symbolic links instead of any referenced file\n\ (available only on systems with lchown system call)\n\ +"), stdout); + fputs (_("\ --reference=RFILE use RFILE's security context rather than specifying\n\ a CONTEXT value\n\ -R, --recursive operate on files and directories recursively\n\ From 3765e5a26e339d4fffd8870c33ffbf113e8e8a33 Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Sat, 13 Jan 2007 19:27:41 +0100 Subject: [PATCH 19/30] * src/c99-to-c89.diff: Remove the ls.c patch, now that I've temporarily removed the offending c99'ism. --- ChangeLog-selinux | 3 +++ src/c99-to-c89.diff | 20 -------------------- 2 files changed, 3 insertions(+), 20 deletions(-) diff --git a/ChangeLog-selinux b/ChangeLog-selinux index fbae67e555..d89fb5e26e 100644 --- a/ChangeLog-selinux +++ b/ChangeLog-selinux @@ -1,5 +1,8 @@ 2007-01-13 Jim Meyering + * src/c99-to-c89.diff: Remove the ls.c patch, now that I've + temporarily removed the offending c99'ism. + * src/chcon.c (usage): Split a string literal that was longer than 509. * src/ls.c (gobble_file): Don't call getfilecon unless print_scontext. diff --git a/src/c99-to-c89.diff b/src/c99-to-c89.diff index 83c00d05c0..5545f6a11e 100644 --- a/src/c99-to-c89.diff +++ b/src/c99-to-c89.diff @@ -116,23 +116,3 @@ diff -u -p -r1.130 shred.c if (errnum == EIO && 0 <= size && (soff | SECTOR_MASK) < lim) { size_t soff1 = (soff | SECTOR_MASK) + 1; -diff --git a/src/ls.c b/src/ls.c -index b1c0f9a..7d51a61 100644 ---- a/src/ls.c -+++ b/src/ls.c -@@ -2565,6 +2565,7 @@ gobble_file (char const *name, enum filetype type, ino_t inode, - ))))) - - { -+ bool file_has_security_context = false; - /* Absolute name of this file. */ - char *absolute_name; - bool do_deref; -@@ -2614,7 +2615,6 @@ gobble_file (char const *name, enum filetype type, ino_t inode, - break; - } - -- bool file_has_security_context = false; - if (err == 0 && (format == long_format || print_scontext)) - { - int attr_len = (do_deref From 09ef5a443f3234b9a9da4c6ad884a978301cef3e Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Sat, 13 Jan 2007 19:29:21 +0100 Subject: [PATCH 20/30] * tests/misc/selinux [VERBOSE]: Print version info for each of the tested tools, not just ls. --- ChangeLog-selinux | 3 +++ tests/misc/selinux | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/ChangeLog-selinux b/ChangeLog-selinux index d89fb5e26e..05200e3648 100644 --- a/ChangeLog-selinux +++ b/ChangeLog-selinux @@ -1,5 +1,8 @@ 2007-01-13 Jim Meyering + * tests/misc/selinux [VERBOSE]: Print version info for each + of the tested tools, not just ls. + * src/c99-to-c89.diff: Remove the ls.c patch, now that I've temporarily removed the offending c99'ism. diff --git a/tests/misc/selinux b/tests/misc/selinux index 1207161fd8..b8499842b9 100755 --- a/tests/misc/selinux +++ b/tests/misc/selinux @@ -3,7 +3,11 @@ if test "$VERBOSE" = yes; then set -x + chcon --version + cp --version ls --version + mv --version + stat --version fi . $srcdir/../envvar-check From 795c052e334b63e9ed9c5f07051a598b2d3f7b3d Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Sat, 20 Jan 2007 16:10:43 +0100 Subject: [PATCH 21/30] cp, mv, install: add SELinux support, but unlike with the Red Hat patch, mv and cp do not provide the "-Z context" option. * src/copy.c: Include . (restore_default_fscreatecon): New function. (copy_reg): Make cp --preserve=context work for existing destination. (copy_internal): Likewise for new destinations. * src/copy.h (cp_options) [preserve_security_context]: New member. * src/cp.c: Include . (selinux_enabled): New global. (usage): Mention new --preserve=context option. (PRESERVE_CONTEXT): Define/use. (decode_preserve_arg): Handle PRESERVE_CONTEXT. (main): Remove an obsolete comment. If --preserve=context is specified on a system without SELinux enabled, give a diagnostic and fail. * src/mv.c: Include . Set x->preserve_security_context if SELinux is enabled. * src/install.c: Accept new "-Z, --context=C" option. Accept --preserve-context option (but not -P option). Accept alternate spelling: --preserve_context, for now. Include and "quotearg.h". (selinux_enabled, use_default_selinux_context): New globals. (PRESERVE_CONTEXT_OPTION): Define. (cp_option_init): Default: do not preserve security context. (setdefaultfilecon): New function. (main): Honor new options. * src/Makefile.am (mv_LDADD, cp_LDADD, ginstall_LDADD): Add $(LIB_SELINUX). --- ChangeLog-selinux | 31 +++++++++++++- src/Makefile.am | 6 +-- src/copy.c | 79 ++++++++++++++++++++++++++++++++++- src/copy.h | 5 ++- src/cp.c | 30 +++++++++++--- src/install.c | 103 ++++++++++++++++++++++++++++++++++++++++++++-- src/mv.c | 4 ++ 7 files changed, 243 insertions(+), 15 deletions(-) diff --git a/ChangeLog-selinux b/ChangeLog-selinux index 05200e3648..de9cc2faea 100644 --- a/ChangeLog-selinux +++ b/ChangeLog-selinux @@ -1,4 +1,33 @@ -2007-01-13 Jim Meyering +2007-01-20 Jim Meyering + + cp, mv, install: add SELinux support, but unlike with the Red Hat + patch, mv and cp do not provide the "-Z context" option. + * src/copy.c: Include . + (restore_default_fscreatecon): New function. + (copy_reg): Make cp --preserve=context work for existing destination. + (copy_internal): Likewise for new destinations. + * src/copy.h (cp_options) [preserve_security_context]: New member. + * src/cp.c: Include . + (selinux_enabled): New global. + (usage): Mention new --preserve=context option. + (PRESERVE_CONTEXT): Define/use. + (decode_preserve_arg): Handle PRESERVE_CONTEXT. + (main): Remove an obsolete comment. + If --preserve=context is specified on a system without SELinux + enabled, give a diagnostic and fail. + * src/mv.c: Include . + Set x->preserve_security_context if SELinux is enabled. + * src/install.c: Accept new "-Z, --context=C" option. + Accept --preserve-context option (but not -P option). + Accept alternate spelling: --preserve_context, for now. + Include and "quotearg.h". + (selinux_enabled, use_default_selinux_context): New globals. + (PRESERVE_CONTEXT_OPTION): Define. + (cp_option_init): Default: do not preserve security context. + (setdefaultfilecon): New function. + (main): Honor new options. + * src/Makefile.am (mv_LDADD, cp_LDADD, ginstall_LDADD): + Add $(LIB_SELINUX). * tests/misc/selinux [VERBOSE]: Print version info for each of the tested tools, not just ls. diff --git a/src/Makefile.am b/src/Makefile.am index a8ce95cd9d..7ad9815e97 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -61,9 +61,9 @@ LDADD = ../lib/libcoreutils.a $(LIBINTL) ../lib/libcoreutils.a # for eaccess in lib/euidaccess.c. chcon_LDADD = $(LDADD) $(LIB_SELINUX) -cp_LDADD = $(LDADD) $(LIB_EACCESS) -ginstall_LDADD = $(LDADD) $(LIB_EACCESS) -mv_LDADD = $(LDADD) $(LIB_EACCESS) +cp_LDADD = $(LDADD) $(LIB_EACCESS) $(LIB_SELINUX) +ginstall_LDADD = $(LDADD) $(LIB_EACCESS) $(LIB_SELINUX) +mv_LDADD = $(LDADD) $(LIB_EACCESS) $(LIB_SELINUX) pathchk_LDADD = $(LDADD) $(LIB_EACCESS) rm_LDADD = $(LDADD) $(LIB_EACCESS) test_LDADD = $(LDADD) $(LIB_EACCESS) diff --git a/src/copy.c b/src/copy.c index 4bdb75cbb9..5283a6472b 100644 --- a/src/copy.c +++ b/src/copy.c @@ -21,6 +21,7 @@ #include #include #include +#include #if HAVE_HURD_H # include @@ -302,6 +303,36 @@ copy_reg (char const *src_name, char const *dst_name, { dest_desc = open (dst_name, O_WRONLY | O_TRUNC | O_BINARY); + /* When using cp --preserve=context to copy to an existing destination, + use the default context rather than that of the source. Why? + 1) the src context may prohibit writing, and + 2) because it's more consistent to use the same context + that is used when the destination file doesn't already exist. */ + if (x->preserve_security_context && 0 <= dest_desc) + { + security_context_t con; + if (getfscreatecon (&con) < 0) + { + error (0, errno, _("failed to get file system create context")); + return_val = false; + goto close_src_desc; + } + + if (con) + { + if (fsetfilecon (dest_desc, con) < 0) + { + error (0, errno, + _("failed to set the security context of %s to %s"), + quote_n (0, dst_name), quote_n (1, con)); + return_val = false; + freecon (con); + goto close_src_desc; + } + freecon(con); + } + } + if (dest_desc < 0 && x->unlink_dest_after_failed_open) { if (unlink (dst_name) != 0) @@ -995,6 +1026,15 @@ emit_verbose (char const *src, char const *dst, char const *backup_dst_name) putchar ('\n'); } +/* A wrapper around "setfscreatecon (NULL)" that exits upon failure. */ +static void +restore_default_fscreatecon_or_die (void) +{ + if (setfscreatecon (NULL) != 0) + error (EXIT_FAILURE, errno, + _("failed to restore the default file creation context")); +} + /* Copy the file SRC_NAME to the file DST_NAME. The files may be of any type. NEW_DST should be true if the file DST_NAME cannot exist because its parent directory was just created; NEW_DST should @@ -1343,7 +1383,7 @@ copy_internal (char const *src_name, char const *dst_name, if (x->move_mode && src_sb.st_nlink == 1) { - earlier_file = src_to_dest_lookup (src_sb.st_ino, src_sb.st_dev); + earlier_file = src_to_dest_lookup (src_sb.st_ino, src_sb.st_dev); } else if ((x->preserve_links && (1 < src_sb.st_nlink @@ -1533,6 +1573,37 @@ copy_internal (char const *src_name, char const *dst_name, delayed_ok = true; + if (x->preserve_security_context) + { + security_context_t con; + + if (0 <= lgetfilecon (src_name, &con)) + { + if (setfscreatecon (con) < 0) + { + error (0, errno, + _("failed to set default file creation context to %s"), + quote (con)); + if (x->require_preserve) + { + freecon (con); + return false; + } + } + freecon (con); + } + else + { + if (errno != ENOTSUP && errno != ENODATA) + { + error (0, errno, + _("failed to get security context of %s"), + quote (src_name)); + return false; + } + } + } + /* In certain modes (cp's --symbolic-link), and for certain file types (symlinks and hard links) it doesn't make sense to preserve metadata, or it's possible to preserve only some of it. @@ -1762,6 +1833,9 @@ copy_internal (char const *src_name, char const *dst_name, } } + if (x->preserve_security_context) + restore_default_fscreatecon_or_die (); + /* There's no need to preserve timestamps or permissions. */ preserve_metadata = false; @@ -1895,6 +1969,9 @@ copy_internal (char const *src_name, char const *dst_name, un_backup: + if (x->preserve_security_context) + restore_default_fscreatecon_or_die (); + /* We have failed to create the destination file. If we've just added a dev/ino entry via the remember_copied call above (i.e., unless we've just failed to create a hard link), diff --git a/src/copy.h b/src/copy.h index c815baf64f..eab6c8678c 100644 --- a/src/copy.h +++ b/src/copy.h @@ -1,5 +1,5 @@ /* core functions for copying files and directories - Copyright (C) 89, 90, 91, 1995-2005 Free Software Foundation. + Copyright (C) 89, 90, 91, 1995-2007 Free Software Foundation. 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 @@ -127,6 +127,9 @@ struct cp_options bool preserve_ownership; bool preserve_mode; bool preserve_timestamps; + /* If true, attempt to preserve the SELinux security context, too. + Set this only if the kernel is SELinux enabled. */ + bool preserve_security_context; /* Enabled for mv, and for cp by the --preserve=links option. If true, attempt to preserve in the destination files any diff --git a/src/cp.c b/src/cp.c index 5759e0d07f..c5d3d32622 100644 --- a/src/cp.c +++ b/src/cp.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "system.h" #include "argmatch.h" @@ -85,6 +86,9 @@ enum /* The invocation name of this program. */ char *program_name; +/* True if the kernel is SELinux enabled. */ +static bool selinux_enabled; + /* If true, the command "cp x/e_file e_dir" uses "e_dir/x/e_file" as its destination instead of the usual "e_dir/e_file." */ static bool parents_option = false; @@ -191,7 +195,7 @@ Mandatory arguments to long options are mandatory for short options too.\n\ -p same as --preserve=mode,ownership,timestamps\n\ --preserve[=ATTR_LIST] preserve the specified attributes (default:\n\ mode,ownership,timestamps), if possible\n\ - additional attributes: links, all\n\ + additional attributes: context, links, all\n\ "), stdout); fputs (_("\ --no-preserve=ATTR_LIST don't preserve the specified attributes\n\ @@ -749,6 +753,7 @@ cp_option_init (struct cp_options *x) x->preserve_links = false; x->preserve_mode = false; x->preserve_timestamps = false; + x->preserve_security_context = false; x->require_preserve = false; x->recursive = false; @@ -777,18 +782,19 @@ decode_preserve_arg (char const *arg, struct cp_options *x, bool on_off) PRESERVE_TIMESTAMPS, PRESERVE_OWNERSHIP, PRESERVE_LINK, + PRESERVE_CONTEXT, PRESERVE_ALL }; static enum File_attribute const preserve_vals[] = { PRESERVE_MODE, PRESERVE_TIMESTAMPS, - PRESERVE_OWNERSHIP, PRESERVE_LINK, PRESERVE_ALL + PRESERVE_OWNERSHIP, PRESERVE_LINK, PRESERVE_CONTEXT, PRESERVE_ALL }; /* Valid arguments to the `--preserve' option. */ static char const* const preserve_args[] = { "mode", "timestamps", - "ownership", "links", "all", NULL + "ownership", "links", "context", "all", NULL }; ARGMATCH_VERIFY (preserve_args, preserve_vals); @@ -824,11 +830,17 @@ decode_preserve_arg (char const *arg, struct cp_options *x, bool on_off) x->preserve_links = on_off; break; + case PRESERVE_CONTEXT: + x->preserve_security_context = on_off; + break; + case PRESERVE_ALL: x->preserve_mode = on_off; x->preserve_timestamps = on_off; x->preserve_ownership = on_off; x->preserve_links = on_off; + if (selinux_enabled) + x->preserve_security_context = on_off; break; default: @@ -862,6 +874,7 @@ main (int argc, char **argv) atexit (close_stdout); + selinux_enabled = (0 < is_selinux_enabled ()); cp_option_init (&x); /* FIXME: consider not calling getenv for SIMPLE_BACKUP_SUFFIX unless @@ -1048,9 +1061,6 @@ main (int argc, char **argv) x.dereference = DEREF_ALWAYS; } - /* The key difference between -d (--no-dereference) and not is the version - of `stat' to call. */ - if (x.recursive) x.copy_as_regular = copy_contents; @@ -1059,6 +1069,14 @@ main (int argc, char **argv) if (x.unlink_dest_after_failed_open & (x.hard_link | x.symbolic_link)) x.unlink_dest_before_opening = true; + if (x.preserve_security_context) + { + if (!selinux_enabled) + error (EXIT_FAILURE, 0, + _("cannot preserve security context " + "without an SELinux-enabled kernel")); + } + /* Allocate space for remembering copied and created files. */ hash_init (); diff --git a/src/install.c b/src/install.c index 04577518ce..1613144bd3 100644 --- a/src/install.c +++ b/src/install.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "system.h" #include "backupfile.h" @@ -35,6 +36,7 @@ #include "mkdir-p.h" #include "modechange.h" #include "quote.h" +#include "quotearg.h" #include "savewd.h" #include "stat-time.h" #include "utimens.h" @@ -49,6 +51,9 @@ # include #endif +static int selinux_enabled = 0; +static bool use_default_selinux_context = true; + #if ! HAVE_ENDGRENT # define endgrent() ((void) 0) #endif @@ -121,15 +126,28 @@ static bool strip_files; /* If true, install a directory instead of a regular file. */ static bool dir_arg; +/* For long options that have no equivalent short option, use a + non-character as a pseudo short option, starting with CHAR_MAX + 1. */ +enum +{ + PRESERVE_CONTEXT_OPTION = CHAR_MAX + 1 +}; + static struct option const long_options[] = { {"backup", optional_argument, NULL, 'b'}, + {GETOPT_SELINUX_CONTEXT_OPTION_DECL}, {"directory", no_argument, NULL, 'd'}, {"group", required_argument, NULL, 'g'}, {"mode", required_argument, NULL, 'm'}, {"no-target-directory", no_argument, NULL, 'T'}, {"owner", required_argument, NULL, 'o'}, {"preserve-timestamps", no_argument, NULL, 'p'}, + {"preserve-context", no_argument, NULL, PRESERVE_CONTEXT_OPTION}, + /* Continue silent support for --preserve_context until Jan 2008. FIXME-obs + After that, FIXME-obs: warn in, say, late 2008, and disable altogether + a year or two later. */ + {"preserve_context", no_argument, NULL, PRESERVE_CONTEXT_OPTION}, {"strip", no_argument, NULL, 's'}, {"suffix", required_argument, NULL, 'S'}, {"target-directory", required_argument, NULL, 't'}, @@ -169,11 +187,47 @@ cp_option_init (struct cp_options *x) x->stdin_tty = false; x->update = false; + x->preserve_security_context = false; x->verbose = false; x->dest_info = NULL; x->src_info = NULL; } +/* Modify file context to match the specified policy. + If an error occurs the file will remain with the default directory + context. */ +static void +setdefaultfilecon (char const *file) +{ + struct stat st; + security_context_t scontext = NULL; + if (selinux_enabled != 1) + { + /* Indicate no context found. */ + return; + } + if (lstat (file, &st) != 0) + return; + + /* If there's an error determining the context, or it has none, + return to allow default context */ + if ((matchpathcon (file, st.st_mode, &scontext) != 0) || + (strcmp (scontext, "<>") == 0)) + { + if (scontext != NULL) + freecon (scontext); + return; + } + + if (lsetfilecon (file, scontext) < 0 && errno != ENOTSUP) + error (0, errno, + _("warning: %s: failed to change context to %s"), + quotearg_colon (file), scontext); + + freecon (scontext); + return; +} + /* FILE is the last operand of this command. Return true if FILE is a directory. But report an error there is a problem accessing FILE, or if FILE does not exist but would have to refer to an existing @@ -222,6 +276,9 @@ main (int argc, char **argv) bool no_target_directory = false; int n_files; char **file; + security_context_t scontext = NULL; + /* set iff kernel has extra selinux system calls */ + selinux_enabled = (0 < is_selinux_enabled ()); initialize_main (&argc, &argv); program_name = argv[0]; @@ -243,7 +300,7 @@ main (int argc, char **argv) we'll actually use backup_suffix_string. */ backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX"); - while ((optc = getopt_long (argc, argv, "bcsDdg:m:o:pt:TvS:", long_options, + while ((optc = getopt_long (argc, argv, "bcsDdg:m:o:pt:TvS:Z:", long_options, NULL)) != -1) { switch (optc) @@ -305,6 +362,27 @@ main (int argc, char **argv) case 'T': no_target_directory = true; break; + + case PRESERVE_CONTEXT_OPTION: + if ( ! selinux_enabled) + { + error (0, 0, _("Warning: ignoring --preserve-context; " + "this kernel is not SELinux-enabled.")); + break; + } + x.preserve_security_context = true; + use_default_selinux_context = false; + break; + case 'Z': + if ( ! selinux_enabled) + { + error (0, 0, _("Warning: ignoring --context (-Z); " + "this kernel is not SELinux-enabled.")); + break; + } + scontext = optarg; + use_default_selinux_context = false; + break; case_GETOPT_HELP_CHAR; case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); default: @@ -320,6 +398,11 @@ main (int argc, char **argv) error (EXIT_FAILURE, 0, _("target directory not allowed when installing a directory")); + if (x.preserve_security_context && scontext != NULL) + error (EXIT_FAILURE, 0, + _("cannot force target context to %s and preserve it"), + quote (scontext)); + if (backup_suffix_string) simple_backup_suffix = xstrdup (backup_suffix_string); @@ -328,6 +411,11 @@ main (int argc, char **argv) version_control_string) : no_backups); + if (scontext && setfscreatecon (scontext) < 0) + error (EXIT_FAILURE, errno, + _("failed to set default file creation context to %s"), + quote (scontext)); + n_files = argc - optind; file = argv + optind; @@ -503,6 +591,7 @@ copy_file (const char *from, const char *to, const struct cp_options *x) static bool change_attributes (char const *name) { + bool ok = false; /* chown must precede chmod because on some systems, chown clears the set[ug]id bits for non-superusers, resulting in incorrect permissions. @@ -521,9 +610,12 @@ change_attributes (char const *name) else if (chmod (name, mode) != 0) error (0, errno, _("cannot change permissions of %s"), quote (name)); else - return true; + ok = true; + + if (use_default_selinux_context) + setdefaultfilecon (name); - return false; + return ok; } /* Set the timestamps of file TO to match those of file FROM. @@ -687,6 +779,11 @@ Mandatory arguments to long options are mandatory for short options too.\n\ -T, --no-target-directory treat DEST as a normal file\n\ -v, --verbose print the name of each directory as it is created\n\ "), stdout); + fputs (_("\ + --preserve-context preserve SELinux security context\n\ + -Z, --context=CONTEXT set SELinux security context of files and directories\n\ +"), stdout); + fputs (HELP_OPTION_DESCRIPTION, stdout); fputs (VERSION_OPTION_DESCRIPTION, stdout); fputs (_("\ diff --git a/src/mv.c b/src/mv.c index 1d1dddab89..6caf6423dc 100644 --- a/src/mv.c +++ b/src/mv.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "system.h" #include "argmatch.h" @@ -113,6 +114,8 @@ rm_option_init (struct rm_options *x) static void cp_option_init (struct cp_options *x) { + bool selinux_enabled = (0 < is_selinux_enabled ()); + x->copy_as_regular = false; /* FIXME: maybe make this an option */ x->dereference = DEREF_NEVER; x->unlink_dest_before_opening = false; @@ -126,6 +129,7 @@ cp_option_init (struct cp_options *x) x->preserve_links = true; x->preserve_mode = true; x->preserve_timestamps = true; + x->preserve_security_context = selinux_enabled; x->require_preserve = false; /* FIXME: maybe make this an option */ x->recursive = true; x->sparse_mode = SPARSE_AUTO; /* FIXME: maybe make this an option */ From 65ba507e17baefb7808b24dd7627814a0ea7902b Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Sat, 20 Jan 2007 16:33:00 +0100 Subject: [PATCH 22/30] * src/system.h (GETOPT_SELINUX_CONTEXT_OPTION_DECL): Define. --- ChangeLog-selinux | 1 + src/system.h | 2 ++ 2 files changed, 3 insertions(+) diff --git a/ChangeLog-selinux b/ChangeLog-selinux index de9cc2faea..f1eba9708c 100644 --- a/ChangeLog-selinux +++ b/ChangeLog-selinux @@ -28,6 +28,7 @@ (main): Honor new options. * src/Makefile.am (mv_LDADD, cp_LDADD, ginstall_LDADD): Add $(LIB_SELINUX). + * src/system.h (GETOPT_SELINUX_CONTEXT_OPTION_DECL): Define. * tests/misc/selinux [VERBOSE]: Print version info for each of the tested tools, not just ls. diff --git a/src/system.h b/src/system.h index 763909bac0..9d23ffd6c6 100644 --- a/src/system.h +++ b/src/system.h @@ -399,6 +399,8 @@ enum "help", no_argument, NULL, GETOPT_HELP_CHAR #define GETOPT_VERSION_OPTION_DECL \ "version", no_argument, NULL, GETOPT_VERSION_CHAR +#define GETOPT_SELINUX_CONTEXT_OPTION_DECL \ + "context", required_argument, NULL, 'Z' #define case_GETOPT_HELP_CHAR \ case GETOPT_HELP_CHAR: \ From beb12e09f3c93a7175d37bc1894bc047f26cc660 Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Wed, 31 Jan 2007 23:01:50 +0100 Subject: [PATCH 23/30] * tests/cp/cp-a-selinux: New file. Test for the bug reported in * tests/cp/Makefile.am (TESTS): Add cp-a-selinux. * tests/selinux: New file. * tests/Makefile.am (EXTRA_DIST): Add selinux. * tests/misc/selinux: Source the new script, rather than open coding it. Change how "cp -a" and "cp --preserve=context" work with SELinux. Now, cp -a attempts to preserve context, but failure to do so does not change cp's exit status. However "cp --preserve=context" is similar, but failure *does* cause cp to exit with nonzero status. * src/copy.h (struct cp_options) [require_preserve_context]: New member. * src/copy.c (copy_reg, copy_internal): Implement the above. * src/mv.c (cp_option_init): Initialize the new member. * src/install.c (cp_option_init): Likewise. * src/cp.c (cp_option_init): Likewise. (decode_preserve_arg): Set it or reset it. FIXME: add an on-writable-NFS-only test --- ChangeLog-selinux | 23 +++++++++++ src/copy.c | 21 ++++++---- src/copy.h | 22 ++++++++--- src/cp.c | 2 + src/install.c | 1 + src/mv.c | 1 + tests/Makefile.am | 2 +- tests/cp/Makefile.am | 1 + tests/cp/cp-a-selinux | 90 +++++++++++++++++++++++++++++++++++++++++++ tests/misc/selinux | 7 +--- tests/selinux | 24 ++++++++++++ 11 files changed, 175 insertions(+), 19 deletions(-) create mode 100755 tests/cp/cp-a-selinux create mode 100644 tests/selinux diff --git a/ChangeLog-selinux b/ChangeLog-selinux index f1eba9708c..736fd9ca16 100644 --- a/ChangeLog-selinux +++ b/ChangeLog-selinux @@ -1,3 +1,26 @@ +2007-01-31 Jim Meyering + + * tests/cp/cp-a-selinux: New file. Test for the bug reported in + . + * tests/cp/Makefile.am (TESTS): Add cp-a-selinux. + + * tests/selinux: New file. + * tests/Makefile.am (EXTRA_DIST): Add selinux. + * tests/misc/selinux: Source the new script, rather than open coding it. + + Change how "cp -a" and "cp --preserve=context" work with SELinux. + Now, cp -a attempts to preserve context, but failure to do so does + not change cp's exit status. However "cp --preserve=context" is + similar, but failure *does* cause cp to exit with nonzero status. + * src/copy.h (struct cp_options) [require_preserve_context]: New member. + * src/copy.c (copy_reg, copy_internal): Implement the above. + * src/mv.c (cp_option_init): Initialize the new member. + * src/install.c (cp_option_init): Likewise. + * src/cp.c (cp_option_init): Likewise. + (decode_preserve_arg): Set it or reset it. + + FIXME: add an on-writable-NFS-only test + 2007-01-20 Jim Meyering cp, mv, install: add SELinux support, but unlike with the Red Hat diff --git a/src/copy.c b/src/copy.c index 5283a6472b..0f50494433 100644 --- a/src/copy.c +++ b/src/copy.c @@ -314,8 +314,11 @@ copy_reg (char const *src_name, char const *dst_name, if (getfscreatecon (&con) < 0) { error (0, errno, _("failed to get file system create context")); - return_val = false; - goto close_src_desc; + if (x->require_preserve_context) + { + return_val = false; + goto close_src_desc; + } } if (con) @@ -325,9 +328,12 @@ copy_reg (char const *src_name, char const *dst_name, error (0, errno, _("failed to set the security context of %s to %s"), quote_n (0, dst_name), quote_n (1, con)); - return_val = false; - freecon (con); - goto close_src_desc; + if (x->require_preserve_context) + { + return_val = false; + freecon (con); + goto close_src_desc; + } } freecon(con); } @@ -1584,7 +1590,7 @@ copy_internal (char const *src_name, char const *dst_name, error (0, errno, _("failed to set default file creation context to %s"), quote (con)); - if (x->require_preserve) + if (x->require_preserve_context) { freecon (con); return false; @@ -1599,7 +1605,8 @@ copy_internal (char const *src_name, char const *dst_name, error (0, errno, _("failed to get security context of %s"), quote (src_name)); - return false; + if (x->require_preserve_context) + return false; } } } diff --git a/src/copy.h b/src/copy.h index eab6c8678c..0d6233f957 100644 --- a/src/copy.h +++ b/src/copy.h @@ -127,9 +127,6 @@ struct cp_options bool preserve_ownership; bool preserve_mode; bool preserve_timestamps; - /* If true, attempt to preserve the SELinux security context, too. - Set this only if the kernel is SELinux enabled. */ - bool preserve_security_context; /* Enabled for mv, and for cp by the --preserve=links option. If true, attempt to preserve in the destination files any @@ -145,10 +142,25 @@ struct cp_options /* If true and any of the above (for preserve) file attributes cannot be applied to a destination file, treat it as a failure and return - nonzero immediately. E.g. cp -p requires this be nonzero, mv requires - it be zero. */ + nonzero immediately. E.g. for cp -p this must be true, for mv it + must be false. */ bool require_preserve; + /* If true, attempt to preserve the SELinux security context, too. + Set this only if the kernel is SELinux enabled. */ + bool preserve_security_context; + + /* Useful only when preserve_security_context is true. + If true, a failed attempt to preserve a file's security context + propagates failure "out" to the caller. If false, a failure to + preserve a file's security context does not change the invoking + application's exit status. Give diagnostics for failed syscalls + regardless of this setting. For example, with "cp --preserve=context" + this flag is "true", while with "cp -a", it is false. That means + "cp -a" attempts to preserve any security context, but does not + fail if it is unable to do so. */ + bool require_preserve_context; + /* If true, copy directories recursively and copy special files as themselves rather than copying their contents. */ bool recursive; diff --git a/src/cp.c b/src/cp.c index c5d3d32622..5db65bc1b0 100644 --- a/src/cp.c +++ b/src/cp.c @@ -754,6 +754,7 @@ cp_option_init (struct cp_options *x) x->preserve_mode = false; x->preserve_timestamps = false; x->preserve_security_context = false; + x->require_preserve_context = false; x->require_preserve = false; x->recursive = false; @@ -832,6 +833,7 @@ decode_preserve_arg (char const *arg, struct cp_options *x, bool on_off) case PRESERVE_CONTEXT: x->preserve_security_context = on_off; + x->require_preserve_context = on_off; break; case PRESERVE_ALL: diff --git a/src/install.c b/src/install.c index 1613144bd3..a35ff33040 100644 --- a/src/install.c +++ b/src/install.c @@ -174,6 +174,7 @@ cp_option_init (struct cp_options *x) x->preserve_mode = false; x->preserve_timestamps = false; x->require_preserve = false; + x->require_preserve_context = false; x->recursive = false; x->sparse_mode = SPARSE_AUTO; x->symbolic_link = false; diff --git a/src/mv.c b/src/mv.c index 6caf6423dc..be64421e78 100644 --- a/src/mv.c +++ b/src/mv.c @@ -131,6 +131,7 @@ cp_option_init (struct cp_options *x) x->preserve_timestamps = true; x->preserve_security_context = selinux_enabled; x->require_preserve = false; /* FIXME: maybe make this an option */ + x->require_preserve_context = false; x->recursive = true; x->sparse_mode = SPARSE_AUTO; /* FIXME: maybe make this an option */ x->symbolic_link = false; diff --git a/tests/Makefile.am b/tests/Makefile.am index b7e30cc66e..b2cbbfc095 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -18,7 +18,7 @@ EXTRA_DIST = \ $(TESTS) Coreutils.pm Makefile.am.in README acl envvar-check \ expensive group-names input-tty lang-default mk-script \ other-fs-tmpdir priv-check \ - rwx-to-mode sample-test setgid-check sparse-file \ + rwx-to-mode sample-test selinux setgid-check sparse-file \ umask-check very-expensive ## N O T E :: Please do not add new tests/ directories. diff --git a/tests/cp/Makefile.am b/tests/cp/Makefile.am index f9925dbc43..536c0221d0 100644 --- a/tests/cp/Makefile.am +++ b/tests/cp/Makefile.am @@ -18,6 +18,7 @@ # 02110-1301, USA. TESTS = \ + cp-a-selinux \ file-perm-race parent-perm-race \ backup-dir \ src-base-dot \ diff --git a/tests/cp/cp-a-selinux b/tests/cp/cp-a-selinux new file mode 100755 index 0000000000..d28b333e0c --- /dev/null +++ b/tests/cp/cp-a-selinux @@ -0,0 +1,90 @@ +#!/bin/sh +# Ensure that cp -a and cp --preserve=context work properly. +# In particular, test on a writable NFS partition. + +# Copyright (C) 2007 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 2 of the License, 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + +if test "$VERBOSE" = yes; then + set -x + cp --version +fi + +. $srcdir/../envvar-check +. $srcdir/../lang-default +. $srcdir/../selinux +PRIV_CHECK_ARG=require-non-root . $srcdir/../priv-check + +pwd=`pwd` +t0=`echo "$0"|sed 's,.*/,,'`.tmp; tmp=$t0/$$ +trap 'status=$?; cd "$pwd" && chmod -R u+rwx $t0 && rm -rf $t0 && exit $status' 0 +trap '(exit $?); exit $?' 1 2 13 15 + +framework_failure=0 +mkdir -p $tmp || framework_failure=1 +cd $tmp || framework_failure=1 + +echo > f || framework_failure=1 +echo > g || framework_failure=1 + +if test $framework_failure = 1; then + echo "$0: failure in testing framework" 1>&2 + (exit 1); exit 1 +fi + +fail=0 + +# /bin/cp from coreutils-6.7-3.fc7 would fail this test by letting cp +# succeed (giving no diagnostics), yet leaving the destination file empty. +cp -a f g 2>err || fail=1 +test -s g || fail=1 # The destination file must not be empty. +test -s err && fail=1 # There must be no stderr output. + +rm -f g err +echo > g + +# ===================================================== +# Here, we expect cp to fail, because it (currently?) cannot +# set the SELinux security context through NFS. +cp --preserve=context f g 2> out && fail=1 + +# Here, we *do* expect the destination to be empty. +test -s g && fail=1 + +# FIXME: currently, this test must be run in an NFS mounted +# directory, and that's not checked. Move this part into a separate +# test and make that a prerequisite. +# In addition, we can add a root-only test that takes one of two +# approaches: 1) create a loopback context=... mount and run the test there. +# 2) run in a confined domain (maybe creating/loading it) that lacks the +# required permissions to the file type). + +# Currently, I get this diagnostic: +# cp: failed to set the security context of `g' to `system_u:object_r:nfs_t': \ +# Operation not supported +# but don't want to depend on ENOTSUP or that specific context triple: +sed "s/ .g' to .*//" out > k +mv k out + +cat <<\EOF > exp || fail=1 +cp: failed to set the security context of +EOF + +cmp out exp || fail=1 +test $fail = 1 && diff out exp 2> /dev/null + +(exit $fail); exit $fail diff --git a/tests/misc/selinux b/tests/misc/selinux index b8499842b9..3e2aae5e2c 100755 --- a/tests/misc/selinux +++ b/tests/misc/selinux @@ -12,14 +12,9 @@ fi . $srcdir/../envvar-check . $srcdir/../lang-default +. $srcdir/../selinux PRIV_CHECK_ARG=require-non-root . $srcdir/../priv-check -test "`ls -Zd .`" = '? .' && - { - echo "$0: skipping this test; this system lacks SELinux support" 1>&2 - exit 77 - } - pwd=`pwd` t0=`echo "$0"|sed 's,.*/,,'`.tmp; tmp=$t0/$$ trap 'status=$?; cd $pwd; chmod -R u+rwx $t0; rm -rf $t0 && exit $status' 0 diff --git a/tests/selinux b/tests/selinux new file mode 100644 index 0000000000..1c4d8a8a6b --- /dev/null +++ b/tests/selinux @@ -0,0 +1,24 @@ +# Is a test expensive? +# Copyright (C) 2007 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 2 of the License, 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + +test "`ls -Zd .`" = '? .' && + { + echo "$0: skipping this test; this system (or maybe just" 1>&2 + echo " the current file system) lacks SELinux support" 1>&2 + (exit 77); exit 77 + } From 0db199df6976327fbcff5970ba554ce4ee2f2e06 Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Wed, 31 Jan 2007 23:59:46 +0100 Subject: [PATCH 24/30] mkdir: Accept new "-Z, --context=C" option. * src/mkdir.c: Include . (main): Honor it. * src/Makefile.am (mkdir_LDADD): Use $(LIB_SELINUX). --- ChangeLog-selinux | 5 +++++ src/Makefile.am | 1 + src/mkdir.c | 18 ++++++++++++++++-- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/ChangeLog-selinux b/ChangeLog-selinux index 736fd9ca16..7860f9b926 100644 --- a/ChangeLog-selinux +++ b/ChangeLog-selinux @@ -1,5 +1,10 @@ 2007-01-31 Jim Meyering + mkdir: Accept new "-Z, --context=C" option. + * src/mkdir.c: Include . + (main): Honor it. + * src/Makefile.am (mkdir_LDADD): Use $(LIB_SELINUX). + * tests/cp/cp-a-selinux: New file. Test for the bug reported in . * tests/cp/Makefile.am (TESTS): Add cp-a-selinux. diff --git a/src/Makefile.am b/src/Makefile.am index 7ad9815e97..42f39d8416 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -63,6 +63,7 @@ LDADD = ../lib/libcoreutils.a $(LIBINTL) ../lib/libcoreutils.a chcon_LDADD = $(LDADD) $(LIB_SELINUX) cp_LDADD = $(LDADD) $(LIB_EACCESS) $(LIB_SELINUX) ginstall_LDADD = $(LDADD) $(LIB_EACCESS) $(LIB_SELINUX) +mkdir_LDADD = $(LDADD) $(LIB_SELINUX) mv_LDADD = $(LDADD) $(LIB_EACCESS) $(LIB_SELINUX) pathchk_LDADD = $(LDADD) $(LIB_EACCESS) rm_LDADD = $(LDADD) $(LIB_EACCESS) diff --git a/src/mkdir.c b/src/mkdir.c index 6fa0ac21d1..0f5af5a24e 100644 --- a/src/mkdir.c +++ b/src/mkdir.c @@ -1,5 +1,5 @@ /* mkdir -- make directories - Copyright (C) 90, 1995-2002, 2004, 2005, 2006 Free Software Foundation, Inc. + Copyright (C) 90, 1995-2002, 2004-2007 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 @@ -21,6 +21,7 @@ #include #include #include +#include #include "system.h" #include "error.h" @@ -40,6 +41,7 @@ char *program_name; static struct option const longopts[] = { + {GETOPT_SELINUX_CONTEXT_OPTION_DECL}, {"mode", required_argument, NULL, 'm'}, {"parents", no_argument, NULL, 'p'}, {"verbose", no_argument, NULL, 'v'}, @@ -68,6 +70,8 @@ Mandatory arguments to long options are mandatory for short options too.\n\ -m, --mode=MODE set file mode (as in chmod), not a=rwx - umask\n\ -p, --parents no error if existing, make parent directories as needed\n\ -v, --verbose print a message for each created directory\n\ + -Z, --context=CTX set the SELinux security context of each created\n\ + directory to CTX\n\ "), stdout); fputs (HELP_OPTION_DESCRIPTION, stdout); fputs (VERSION_OPTION_DESCRIPTION, stdout); @@ -140,7 +144,9 @@ main (int argc, char **argv) { const char *specified_mode = NULL; int optc; + security_context_t scontext = NULL; struct mkdir_options options; + options.make_ancestor_function = NULL; options.mode = S_IRWXUGO; options.mode_bits = 0; @@ -154,7 +160,7 @@ main (int argc, char **argv) atexit (close_stdout); - while ((optc = getopt_long (argc, argv, "pm:v", longopts, NULL)) != -1) + while ((optc = getopt_long (argc, argv, "pm:vZ:", longopts, NULL)) != -1) { switch (optc) { @@ -167,6 +173,9 @@ main (int argc, char **argv) case 'v': /* --verbose */ options.created_directory_format = _("created directory %s"); break; + case 'Z': + scontext = optarg; + break; case_GETOPT_HELP_CHAR; case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); default: @@ -180,6 +189,11 @@ main (int argc, char **argv) usage (EXIT_FAILURE); } + if (scontext && setfscreatecon (scontext) < 0) + error (EXIT_FAILURE, errno, + _("failed to set default file creation context to %s"), + quote (optarg)); + if (options.make_ancestor_function || specified_mode) { mode_t umask_value = umask (0); From 3f619c59cf13818604e1bf15924af7568ba21cf6 Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Thu, 1 Feb 2007 00:26:07 +0100 Subject: [PATCH 25/30] mkfifo, mknod: Accept new "-Z, --context=C" option. * src/mkfifo.c, src/mknod.c: Include . (main): Honor it. * src/Makefile.am (mkfifo_LDADD, mknod_LDADD): Use $(LIB_SELINUX). --- ChangeLog-selinux | 5 +++++ src/Makefile.am | 2 ++ src/mkfifo.c | 18 ++++++++++++++++-- src/mknod.c | 18 ++++++++++++++++-- 4 files changed, 39 insertions(+), 4 deletions(-) diff --git a/ChangeLog-selinux b/ChangeLog-selinux index 7860f9b926..7a27296d42 100644 --- a/ChangeLog-selinux +++ b/ChangeLog-selinux @@ -1,5 +1,10 @@ 2007-01-31 Jim Meyering + mkfifo, mknod: Accept new "-Z, --context=C" option. + * src/mkfifo.c, src/mknod.c: Include . + (main): Honor it. + * src/Makefile.am (mkfifo_LDADD, mknod_LDADD): Use $(LIB_SELINUX). + mkdir: Accept new "-Z, --context=C" option. * src/mkdir.c: Include . (main): Honor it. diff --git a/src/Makefile.am b/src/Makefile.am index 42f39d8416..ca9d9685c9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -64,6 +64,8 @@ chcon_LDADD = $(LDADD) $(LIB_SELINUX) cp_LDADD = $(LDADD) $(LIB_EACCESS) $(LIB_SELINUX) ginstall_LDADD = $(LDADD) $(LIB_EACCESS) $(LIB_SELINUX) mkdir_LDADD = $(LDADD) $(LIB_SELINUX) +mkfifo_LDADD = $(LDADD) $(LIB_SELINUX) +mknod_LDADD = $(LDADD) $(LIB_SELINUX) mv_LDADD = $(LDADD) $(LIB_EACCESS) $(LIB_SELINUX) pathchk_LDADD = $(LDADD) $(LIB_EACCESS) rm_LDADD = $(LDADD) $(LIB_EACCESS) diff --git a/src/mkfifo.c b/src/mkfifo.c index d329b79b8b..955db49f82 100644 --- a/src/mkfifo.c +++ b/src/mkfifo.c @@ -1,5 +1,5 @@ /* mkfifo -- make fifo's (named pipes) - Copyright (C) 90, 91, 1995-2006 Free Software Foundation, Inc. + Copyright (C) 90, 91, 1995-2007 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 @@ -21,6 +21,7 @@ #include #include #include +#include #include "system.h" #include "error.h" @@ -37,6 +38,7 @@ char *program_name; static struct option const longopts[] = { + {GETOPT_SELINUX_CONTEXT_OPTION_DECL}, {"mode", required_argument, NULL, 'm'}, {GETOPT_HELP_OPTION_DECL}, {GETOPT_VERSION_OPTION_DECL}, @@ -55,6 +57,9 @@ usage (int status) fputs (_("\ Create named pipes (FIFOs) with the given NAMEs.\n\ \n\ +"), stdout); + fputs (_("\ + -Z, --context=CTX set the SELinux security context of each NAME to CTX\n\ "), stdout); fputs (_("\ Mandatory arguments to long options are mandatory for short options too.\n\ @@ -76,6 +81,7 @@ main (int argc, char **argv) char const *specified_mode = NULL; int exit_status = EXIT_SUCCESS; int optc; + security_context_t scontext = NULL; initialize_main (&argc, &argv); program_name = argv[0]; @@ -85,13 +91,16 @@ main (int argc, char **argv) atexit (close_stdout); - while ((optc = getopt_long (argc, argv, "m:", longopts, NULL)) != -1) + while ((optc = getopt_long (argc, argv, "m:Z:", longopts, NULL)) != -1) { switch (optc) { case 'm': specified_mode = optarg; break; + case 'Z': + scontext = optarg; + break; case_GETOPT_HELP_CHAR; case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); default: @@ -105,6 +114,11 @@ main (int argc, char **argv) usage (EXIT_FAILURE); } + if (scontext && setfscreatecon (scontext) < 0) + error (EXIT_FAILURE, errno, + _("failed to set default file creation context to %s"), + quote (optarg)); + newmode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); if (specified_mode) { diff --git a/src/mknod.c b/src/mknod.c index 43a1b9dcc7..6c42e1d039 100644 --- a/src/mknod.c +++ b/src/mknod.c @@ -1,5 +1,5 @@ /* mknod -- make special files - Copyright (C) 90, 91, 1995-2006 Free Software Foundation, Inc. + Copyright (C) 90, 91, 1995-2007 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 @@ -21,6 +21,7 @@ #include #include #include +#include #include "system.h" #include "error.h" @@ -38,6 +39,7 @@ char *program_name; static struct option const longopts[] = { + {GETOPT_SELINUX_CONTEXT_OPTION_DECL}, {"mode", required_argument, NULL, 'm'}, {GETOPT_HELP_OPTION_DECL}, {GETOPT_VERSION_OPTION_DECL}, @@ -57,6 +59,9 @@ usage (int status) fputs (_("\ Create the special file NAME of the given TYPE.\n\ \n\ +"), stdout); + fputs(_("\ + -Z, --context=CTX set the SELinux security context of NAME to CTX\n\ "), stdout); fputs (_("\ Mandatory arguments to long options are mandatory for short options too.\n\ @@ -92,6 +97,7 @@ main (int argc, char **argv) int optc; int expected_operands; mode_t node_type; + security_context_t scontext = NULL; initialize_main (&argc, &argv); program_name = argv[0]; @@ -101,13 +107,16 @@ main (int argc, char **argv) atexit (close_stdout); - while ((optc = getopt_long (argc, argv, "m:", longopts, NULL)) != -1) + while ((optc = getopt_long (argc, argv, "m:Z:", longopts, NULL)) != -1) { switch (optc) { case 'm': specified_mode = optarg; break; + case 'Z': + scontext = optarg; + break; case_GETOPT_HELP_CHAR; case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); default: @@ -157,6 +166,11 @@ main (int argc, char **argv) usage (EXIT_FAILURE); } + if (scontext && setfscreatecon (scontext) < 0) + error (EXIT_FAILURE, errno, + _("failed to set default file creation context to %s"), + quote (optarg)); + /* Only check the first character, to allow mnemonic usage like `mknod /dev/rst0 character 18 0'. */ From 8daf081cd2d1b429d71058dc4c89b87ccf7cc1fe Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Thu, 1 Feb 2007 18:07:36 +0100 Subject: [PATCH 26/30] Regenerate .cvsignore and .gitignore files. --- lib/.cvsignore | 2 ++ lib/.gitignore | 2 ++ 2 files changed, 4 insertions(+) diff --git a/lib/.cvsignore b/lib/.cvsignore index e4e482852e..6b1f1cd6bf 100644 --- a/lib/.cvsignore +++ b/lib/.cvsignore @@ -286,7 +286,9 @@ savedir.c savedir.h savewd.c savewd.h +se-context.h se-context_.h +se-selinux.h se-selinux_.h search.h selinux diff --git a/lib/.gitignore b/lib/.gitignore index 85886debee..393c166729 100644 --- a/lib/.gitignore +++ b/lib/.gitignore @@ -279,7 +279,9 @@ savedir.c savedir.h savewd.c savewd.h +se-context.h se-context_.h +se-selinux.h se-selinux_.h selinux selinux-at.c From 6f5700ac8a58f60490fb6197b8dbda20b0734c0e Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Fri, 2 Feb 2007 18:58:41 +0100 Subject: [PATCH 27/30] * src/runcon.c: New program. * src/Makefile.am (bin_PROGRAMS): Add runcon. (runcon_LDADD): Define. * README: Add runcon to the list of programs. * AUTHORS: Add this: runcon: Russell Coker * tests/help-version: Add runcon as an exception. * man/Makefile.am (dist_man_MANS): Add runcon.1. (runcon.1): New dependency. * po/POTFILES.in: Add src/runcon.c. --- AUTHORS | 1 + ChangeLog-selinux | 11 ++ README | 10 +- man/Makefile.am | 4 +- man/runcon.x | 14 +++ po/ChangeLog | 4 + po/POTFILES.in | 1 + src/Makefile.am | 4 +- src/runcon.c | 249 +++++++++++++++++++++++++++++++++++++++++++++ tests/help-version | 3 +- 10 files changed, 293 insertions(+), 8 deletions(-) create mode 100644 man/runcon.x create mode 100644 src/runcon.c diff --git a/AUTHORS b/AUTHORS index 11e02c5a7f..9a8b2c9293 100644 --- a/AUTHORS +++ b/AUTHORS @@ -61,6 +61,7 @@ pwd: Jim Meyering readlink: Dmitry V. Levin rm: Paul Rubin, David MacKenzie, Richard Stallman, Jim Meyering rmdir: David MacKenzie +runcon: Russell Coker seq: Ulrich Drepper sha1sum: Ulrich Drepper, Scott Miller, David Madore sha224sum: Ulrich Drepper, Scott Miller, David Madore diff --git a/ChangeLog-selinux b/ChangeLog-selinux index 7a27296d42..63df994ec4 100644 --- a/ChangeLog-selinux +++ b/ChangeLog-selinux @@ -1,3 +1,14 @@ +2007-02-02 Jim Meyering + + * src/runcon.c: New program. + * src/Makefile.am (bin_PROGRAMS): Add runcon. + (runcon_LDADD): Define. + * README: Add runcon to the list of programs. + * AUTHORS: Add this: runcon: Russell Coker + * tests/help-version: Add runcon as an exception. + * man/Makefile.am (dist_man_MANS): Add runcon.1. + (runcon.1): New dependency. + 2007-01-31 Jim Meyering mkfifo, mknod: Accept new "-Z, --context=C" option. diff --git a/README b/README index 1dd0bbccb9..b510100757 100644 --- a/README +++ b/README @@ -10,11 +10,11 @@ The programs that can be built with this package are: [ base64 basename cat chcon chgrp chmod chown chroot cksum comm cp csplit cut date dd df dir dircolors dirname du echo env expand expr factor false fmt fold ginstall groups head hostid hostname id join - kill link ln logname ls md5sum mkdir mkfifo mknod mv nice nl nohup od - paste pathchk pinky pr printenv printf ptx pwd readlink rm rmdir seq - sha1sum sha224sum sha256sum sha384sum sha512sum shred shuf sleep sort - split stat stty su sum sync tac tail tee test touch tr true tsort tty - uname unexpand uniq unlink uptime users vdir wc who whoami yes + kill link ln logname ls md5sum mkdir mkfifo mknod mv nice nl nohup + od paste pathchk pinky pr printenv printf ptx pwd readlink rm rmdir + runcon seq sha1sum sha224sum sha256sum sha384sum sha512sum shred shuf + sleep sort split stat stty su sum sync tac tail tee test touch tr true + tsort tty uname unexpand uniq unlink uptime users vdir wc who whoami yes See the file NEWS for a list of major changes in the current release. diff --git a/man/Makefile.am b/man/Makefile.am index 8c5f610352..650306ed60 100644 --- a/man/Makefile.am +++ b/man/Makefile.am @@ -25,7 +25,8 @@ dist_man_MANS = \ link.1 ln.1 logname.1 \ ls.1 md5sum.1 mkdir.1 mkfifo.1 mknod.1 mv.1 nl.1 nohup.1 od.1 \ paste.1 pathchk.1 pr.1 printenv.1 printf.1 ptx.1 pwd.1 readlink.1 \ - rm.1 rmdir.1 seq.1 sha1sum.1 sha224sum.1 sha256sum.1 sha384sum.1 sha512sum.1 \ + rm.1 rmdir.1 runcon.1 seq.1 \ + sha1sum.1 sha224sum.1 sha256sum.1 sha384sum.1 sha512sum.1 \ shred.1 shuf.1 sleep.1 sort.1 split.1 stat.1 \ su.1 sum.1 sync.1 tac.1 tail.1 tee.1 test.1 touch.1 tr.1 true.1 tsort.1 \ tty.1 unexpand.1 uniq.1 unlink.1 vdir.1 wc.1 \ @@ -105,6 +106,7 @@ pwd.1: $(common_dep) $(srcdir)/pwd.x ../src/pwd.c readlink.1: $(common_dep) $(srcdir)/readlink.x ../src/readlink.c rm.1: $(common_dep) $(srcdir)/rm.x ../src/rm.c rmdir.1: $(common_dep) $(srcdir)/rmdir.x ../src/rmdir.c +runcon.1: $(common_dep) $(srcdir)/runcon.x ../src/runcon.c seq.1: $(common_dep) $(srcdir)/seq.x ../src/seq.c sha1sum.1: $(common_dep) $(srcdir)/sha1sum.x ../src/md5sum.c sha224sum.1: $(common_dep) $(srcdir)/sha224sum.x ../src/md5sum.c diff --git a/man/runcon.x b/man/runcon.x new file mode 100644 index 0000000000..d2df13e24a --- /dev/null +++ b/man/runcon.x @@ -0,0 +1,14 @@ +[NAME] +runcon \- run command with specified security context +[DESCRIPTION] +Run COMMAND with completely-specified CONTEXT, or with current or +transitioned security context modified by one or more of LEVEL, +ROLE, TYPE, and USER. +.PP +If none of \fI-c\fR, \fI-t\fR, \fI-u\fR, \fI-r\fR, or \fI-l\fR, is specified, +the first argument is used as the complete context. Any additional +arguments after \fICOMMAND\fR are interpreted as arguments to the +command. +.PP +Note that only carefully-chosen contexts are likely to successfully +run. diff --git a/po/ChangeLog b/po/ChangeLog index ad54a0907f..709c42eba4 100644 --- a/po/ChangeLog +++ b/po/ChangeLog @@ -1,3 +1,7 @@ +2007-02-02 Jim Meyering + + * POTFILES.in: Add src/runcon.c. + 2007-01-13 Jim Meyering * POTFILES.in: Add src/chcon.c. diff --git a/po/POTFILES.in b/po/POTFILES.in index 8592947f4a..c9679f131f 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -91,6 +91,7 @@ src/readlink.c src/remove.c src/rm.c src/rmdir.c +src/runcon.c src/seq.c src/setuidgid.c src/shred.c diff --git a/src/Makefile.am b/src/Makefile.am index ca9d9685c9..670d51b5e0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -26,7 +26,8 @@ bin_PROGRAMS = [ chcon chgrp chown chmod cp dd dircolors du \ nl od paste pr ptx sha1sum sha224sum sha256sum sha384sum sha512sum \ shuf sort split sum tac tail tr tsort unexpand uniq wc \ basename date dirname echo env expr factor false \ - hostname id kill logname pathchk printenv printf pwd seq sleep tee \ + hostname id kill logname pathchk printenv printf pwd \ + runcon seq sleep tee \ test true tty whoami yes \ base64 \ $(OPTIONAL_BIN_PROGS) $(DF_PROG) @@ -67,6 +68,7 @@ mkdir_LDADD = $(LDADD) $(LIB_SELINUX) mkfifo_LDADD = $(LDADD) $(LIB_SELINUX) mknod_LDADD = $(LDADD) $(LIB_SELINUX) mv_LDADD = $(LDADD) $(LIB_EACCESS) $(LIB_SELINUX) +runcon_LDADD = $(LDADD) $(LIB_SELINUX) pathchk_LDADD = $(LDADD) $(LIB_EACCESS) rm_LDADD = $(LDADD) $(LIB_EACCESS) test_LDADD = $(LDADD) $(LIB_EACCESS) diff --git a/src/runcon.c b/src/runcon.c new file mode 100644 index 0000000000..ac0b906614 --- /dev/null +++ b/src/runcon.c @@ -0,0 +1,249 @@ +/* + * runcon [ context | + * ( [ -c ] [ -r role ] [-t type] [ -u user ] [ -l levelrange ] ) + * command [arg1 [arg2 ...] ] + * + * attempt to run the specified command with the specified context. + * + * -r role : use the current context with the specified role + * -t type : use the current context with the specified type + * -u user : use the current context with the specified user + * -l level : use the current context with the specified level range + * -c : compute process transition context before modifying + * + * Contexts are interpreted as follows: + * + * Number of MLS + * components system? + * + * 1 - type + * 2 - role:type + * 3 Y role:type:range + * 3 N user:role:type + * 4 Y user:role:type:range + * 4 N error + */ + +#include +#include +#include +#include +#include +#ifdef HAVE_SELINUX_FLASK_H +# include +#else +# define SECCLASS_PROCESS 0 +#endif +#include +#include "system.h" +#include "error.h" +#include "quote.h" +#include "quotearg.h" + +/* The official name of this program (e.g., no `g' prefix). */ +#define PROGRAM_NAME "runcon" + +#define AUTHORS "Russell Coker" + +static struct option long_options[] = { + {"role", required_argument, NULL, 'r'}, + {"type", required_argument, NULL, 't'}, + {"user", required_argument, NULL, 'u'}, + {"range", required_argument, NULL, 'l'}, + {"compute", no_argument, NULL, 'c'}, + {GETOPT_HELP_OPTION_DECL}, + {GETOPT_VERSION_OPTION_DECL}, + {NULL, 0, NULL, 0} +}; + +/* The name the program was run with. */ +char *program_name; + +void +usage (int status) +{ + if (status != EXIT_SUCCESS) + fprintf (stderr, _("Try `%s --help' for more information.\n"), + program_name); + else + { + printf (_("\ +Usage: %s CONTEXT COMMAND [args]\n\ + or: %s [ -c ] [-u USER] [-r ROLE] [-t TYPE] [-l RANGE] COMMAND [args]\n\ +"), program_name, program_name); + fputs (_("\ +Run a program in a different security context.\n\ +With neither CONTEXT nor COMMAND, print the current security context.\n\ +\n\ + CONTEXT Complete security context\n\ + -c, --compute compute process transition context before modifying\n\ + -t, --type=TYPE type (for same role as parent)\n\ + -u, --user=USER user identity\n\ + -r, --role=ROLE role\n\ + -l, --range=RANGE levelrange\n\ +\n\ +"), stdout); + fputs (HELP_OPTION_DESCRIPTION, stdout); + fputs (VERSION_OPTION_DESCRIPTION, stdout); + } + exit (status); +} + +int +main (int argc, char **argv, char **envp) +{ + char *role = NULL; + char *range = NULL; + char *user = NULL; + char *type = NULL; + char *context = NULL; + security_context_t cur_context = NULL; + security_context_t file_context = NULL; + security_context_t new_context = NULL; + bool compute_trans = false; + + context_t con; + + initialize_main (&argc, &argv); + program_name = argv[0]; + setlocale (LC_ALL, ""); + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); + + atexit (close_stdout); + + while (1) + { + int c; + int option_index = 0; + c = getopt_long (argc, argv, "r:t:u:l:c", long_options, &option_index); + if (c == -1) + break; + switch (c) + { + case 'r': + if (role) + error (EXIT_FAILURE, 0, _("multiple roles")); + role = optarg; + break; + case 't': + if (type) + error (EXIT_FAILURE, 0, _("multiple types")); + type = optarg; + break; + case 'u': + if (user) + error (EXIT_FAILURE, 0, _("multiple users")); + user = optarg; + break; + case 'l': + if (range) + error (EXIT_FAILURE, 0, _("multiple levelranges")); + range = optarg; + break; + case 'c': + compute_trans = true; + break; + + case_GETOPT_HELP_CHAR; + case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); + default: + usage (EXIT_FAILURE); + break; + } + } + + if (argc - optind == 0) + { + if (getcon (&cur_context) < 0) + error (EXIT_FAILURE, errno, _("failed to get current context")); + fputs (cur_context, stdout); + fputc ('\n', stdout); + exit (EXIT_SUCCESS); + } + + if (!(user || role || type || range || compute_trans)) + { + if (optind >= argc) + { + error (0, 0, _("you must specify -c, -t, -u, -l, -r, or context")); + usage (1); + } + context = argv[optind++]; + } + + if (optind >= argc) + { + error (0, 0, _("no command specified")); + usage (1); + } + + if (is_selinux_enabled () != 1) + error (EXIT_FAILURE, 0, + _("runcon may be used only on a SELinux kernel.")); + + if (context) + { + con = context_new (context); + if (!con) + error (EXIT_FAILURE, errno, _("failed to create security context: %s"), + quotearg_colon (context)); + } + else + { + if (getcon (&cur_context) < 0) + error (EXIT_FAILURE, errno, _("failed to get current context")); + + /* We will generate context based on process transition */ + if (compute_trans) + { + /* Get context of file to be executed */ + if (getfilecon (argv[optind], &file_context) == -1) + error (EXIT_FAILURE, errno, + _("failed to get security context of %s"), + quote (argv[optind])); + /* compute result of process transition */ + if (security_compute_create (cur_context, file_context, + SECCLASS_PROCESS, &new_context) != 0) + error (EXIT_FAILURE, errno, + _("failed to compute a new context")); + /* free contexts */ + freecon (file_context); + freecon (cur_context); + + /* set cur_context equal to new_context */ + cur_context = new_context; + } + + con = context_new (cur_context); + if (!con) + error (EXIT_FAILURE, errno, _("failed to create security context: %s"), + quotearg_colon (cur_context)); + if (user && context_user_set (con, user)) + error (EXIT_FAILURE, errno, _("failed to set new user %s"), user); + if (type && context_type_set (con, type)) + error (EXIT_FAILURE, errno, _("failed to set new type %s"), type); + if (range && context_range_set (con, range)) + error (EXIT_FAILURE, errno, _("failed to set new range %s"), range); + if (role && context_role_set (con, role)) + error (EXIT_FAILURE, errno, _("failed to set new role %s"), role); + } + + if (security_check_context (context_str (con)) < 0) + error (EXIT_FAILURE, errno, _("invalid context: %s"), + quotearg_colon (context_str (con))); + + if (setexeccon (context_str (con)) != 0) + error (EXIT_FAILURE, errno, _("unable to set security context %s"), + quote (context_str (con))); + if (cur_context != NULL) + freecon (cur_context); + + execvp (argv[optind], argv + optind); + + { + int exit_status = (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE); + error (0, errno, "%s", argv[optind]); + exit (exit_status); + } +} diff --git a/tests/help-version b/tests/help-version index d3c9bfb187..3dea4d1262 100755 --- a/tests/help-version +++ b/tests/help-version @@ -72,6 +72,7 @@ for lang in C fr da; do # Skip `test'; it doesn't accept --help or --version. test $i = test && continue; test $i = chcon && continue; + test $i = runcon && continue; # false fails even when invoked with --help or --version. if test $i = false; then @@ -197,7 +198,7 @@ lbracket_args=": ]" for i in $all_programs; do # Skip these. - case $i in chroot|stty|tty|false|chcon) continue;; esac + case $i in chroot|stty|tty|false|chcon|runcon) continue;; esac rm -rf $tmp_in $tmp_in2 $tmp_dir $tmp_out echo > $tmp_in From 5e711ff948a975e25a5b1252005dc4fda4e7191a Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Mon, 12 Feb 2007 19:35:25 +0100 Subject: [PATCH 28/30] Arrange for "make check-root" to run the new root-only test. * tests/Makefile.am (t9): New target, to run tests/cp/cp-a-selinux. (all_t): Add t9. Use a directory on a loopback device mounted with -o context=... * tests/cp/cp-a-selinux: Since this test now runs mount and umount, it is a root-only one. --- ChangeLog | 8 ++++++++ tests/Makefile.am | 4 +++- tests/cp/cp-a-selinux | 36 ++++++++++++++++++++---------------- 3 files changed, 31 insertions(+), 17 deletions(-) diff --git a/ChangeLog b/ChangeLog index 2b8a753955..c3f095b5b5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -486,9 +486,17 @@ consistent, so this can be applied with patch -p0. Reported by Matthew Woehlke. + Arrange for "make check-root" to run the new root-only test. + * tests/Makefile.am (t9): New target, to run tests/cp/cp-a-selinux. + (all_t): Add t9. + * Makefile.maint (patch-check): Use patch with its -p2 option, since that makes this check slightly more strict. + Use a directory on a loopback device mounted with -o context=... + * tests/cp/cp-a-selinux: Since this test now runs mount and umount, + it is a root-only one. + 2007-01-29 Jim Meyering Plug a leak in ls. diff --git a/tests/Makefile.am b/tests/Makefile.am index b2cbbfc095..4371d73a96 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -30,7 +30,7 @@ SUBDIRS = \ tsort unexpand uniq wc ## N O T E :: Please do not add new directories. -all_t = t1 t2 t3 t4 t5 t6 t7 t8 +all_t = t1 t2 t3 t4 t5 t6 t7 t8 t9 .PHONY: check-root $(all_t) check-root: $(all_t) @@ -50,6 +50,8 @@ t7: cd ls && $(MAKE) check TESTS=nameless-uid t8: cd misc && $(MAKE) check TESTS=chcon +t9: + cd cp && $(MAKE) check TESTS=cp-a-selinux check-recursive: root-hint diff --git a/tests/cp/cp-a-selinux b/tests/cp/cp-a-selinux index d28b333e0c..338044f165 100755 --- a/tests/cp/cp-a-selinux +++ b/tests/cp/cp-a-selinux @@ -27,19 +27,28 @@ fi . $srcdir/../envvar-check . $srcdir/../lang-default . $srcdir/../selinux -PRIV_CHECK_ARG=require-non-root . $srcdir/../priv-check +PRIV_CHECK_ARG=require-root . $srcdir/../priv-check pwd=`pwd` t0=`echo "$0"|sed 's,.*/,,'`.tmp; tmp=$t0/$$ -trap 'status=$?; cd "$pwd" && chmod -R u+rwx $t0 && rm -rf $t0 && exit $status' 0 +trap 'status=$?; cd "$pwd"; umount $tmp/mnt; chmod -R u+rwx $t0 && rm -rf $t0 && exit $status' 0 trap '(exit $?); exit $?' 1 2 13 15 framework_failure=0 mkdir -p $tmp || framework_failure=1 cd $tmp || framework_failure=1 -echo > f || framework_failure=1 -echo > g || framework_failure=1 +# Create a file system, then mount it with the context=... option. +dd if=/dev/zero of=blob bs=8192 count=200 > /dev/null 2>&1 \ + || framework_failure=1 +mkdir mnt || framework_failure=1 +mkfs -t ext2 -F blob > /dev/null 2>&1 || framework_failure=1 +mount -oloop,context=system_u:object_r:removable_t blob mnt \ + || framework_failure=1 +cd mnt || framework_failure=1 + +echo > f || framework_failure=1 +echo > g || framework_failure=1 if test $framework_failure = 1; then echo "$0: failure in testing framework" 1>&2 @@ -58,25 +67,20 @@ rm -f g err echo > g # ===================================================== -# Here, we expect cp to fail, because it (currently?) cannot -# set the SELinux security context through NFS. +# Here, we expect cp to fail, because it cannot set the SELinux +# security context through NFS or a mount with fixed context. cp --preserve=context f g 2> out && fail=1 # Here, we *do* expect the destination to be empty. test -s g && fail=1 -# FIXME: currently, this test must be run in an NFS mounted -# directory, and that's not checked. Move this part into a separate -# test and make that a prerequisite. -# In addition, we can add a root-only test that takes one of two -# approaches: 1) create a loopback context=... mount and run the test there. -# 2) run in a confined domain (maybe creating/loading it) that lacks the -# required permissions to the file type). - -# Currently, I get this diagnostic: +# An alternative to the current approach would be to run in a confined +# domain (maybe creating/loading it) that lacks the required permissions +# to the file type. +# Note: this test could also be run by a regular (non-root) user in an +# NFS mounted directory. When doing that, I get this diagnostic: # cp: failed to set the security context of `g' to `system_u:object_r:nfs_t': \ # Operation not supported -# but don't want to depend on ENOTSUP or that specific context triple: sed "s/ .g' to .*//" out > k mv k out From ab511a2106e8395ee86997c07c13bd5fa2472c5a Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Sun, 18 Mar 2007 17:52:00 +0100 Subject: [PATCH 29/30] * src/runcon.c (main): Remove "." at end of a diagnostic. --- ChangeLog-selinux | 4 ++++ src/runcon.c | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ChangeLog-selinux b/ChangeLog-selinux index 63df994ec4..95046dc6c6 100644 --- a/ChangeLog-selinux +++ b/ChangeLog-selinux @@ -1,3 +1,7 @@ +2007-03-18 Jim Meyering + + * src/runcon.c (main): Remove "." at end of a diagnostic. + 2007-02-02 Jim Meyering * src/runcon.c: New program. diff --git a/src/runcon.c b/src/runcon.c index ac0b906614..a8ddb74742 100644 --- a/src/runcon.c +++ b/src/runcon.c @@ -180,7 +180,7 @@ main (int argc, char **argv, char **envp) if (is_selinux_enabled () != 1) error (EXIT_FAILURE, 0, - _("runcon may be used only on a SELinux kernel.")); + _("runcon may be used only on a SELinux kernel")); if (context) { From 9aee8e78ea1d7966b5d79034874d9cf831565410 Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Sun, 18 Mar 2007 18:04:30 +0100 Subject: [PATCH 30/30] * src/runcon.c (main): Don't reorder arguments. Reported by Ulrich Drepper in . * tests/misc/runcon-no-reorder: New file. Test for the above. * tests/misc/Makefile.am (TESTS): Add runcon-no-reorder. --- ChangeLog-selinux | 5 ++++ src/runcon.c | 4 +-- tests/misc/Makefile.am | 1 + tests/misc/runcon-no-reorder | 57 ++++++++++++++++++++++++++++++++++++ 4 files changed, 65 insertions(+), 2 deletions(-) create mode 100755 tests/misc/runcon-no-reorder diff --git a/ChangeLog-selinux b/ChangeLog-selinux index 95046dc6c6..3b6264c381 100644 --- a/ChangeLog-selinux +++ b/ChangeLog-selinux @@ -1,5 +1,10 @@ 2007-03-18 Jim Meyering + * src/runcon.c (main): Don't reorder arguments. Reported by + Ulrich Drepper in . + * tests/misc/runcon-no-reorder: New file. Test for the above. + * tests/misc/Makefile.am (TESTS): Add runcon-no-reorder. + * src/runcon.c (main): Remove "." at end of a diagnostic. 2007-02-02 Jim Meyering diff --git a/src/runcon.c b/src/runcon.c index a8ddb74742..1d79ef6ffa 100644 --- a/src/runcon.c +++ b/src/runcon.c @@ -114,9 +114,9 @@ main (int argc, char **argv, char **envp) while (1) { - int c; int option_index = 0; - c = getopt_long (argc, argv, "r:t:u:l:c", long_options, &option_index); + int c = getopt_long (argc, argv, "+r:t:u:l:c", long_options, + &option_index); if (c == -1) break; switch (c) diff --git a/tests/misc/Makefile.am b/tests/misc/Makefile.am index e4a15cf9e8..87a644ee0c 100644 --- a/tests/misc/Makefile.am +++ b/tests/misc/Makefile.am @@ -73,6 +73,7 @@ TESTS = \ printf \ printf-hex \ pwd-long \ + runcon-no-reorder \ sha224sum \ sha256sum \ sha384sum \ diff --git a/tests/misc/runcon-no-reorder b/tests/misc/runcon-no-reorder new file mode 100755 index 0000000000..e41fd30414 --- /dev/null +++ b/tests/misc/runcon-no-reorder @@ -0,0 +1,57 @@ +#!/bin/sh +# Ensure that runcon does not reorder its arguments. + +# Copyright (C) 2007 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 2 of the License, 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, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + +if test "$VERBOSE" = yes; then + set -x + runcon --version +fi + +. $srcdir/../lang-default + +pwd=`pwd` +t0=`echo "$0"|sed 's,.*/,,'`.tmp; tmp=$t0/$$ +trap 'status=$?; cd "$pwd" && chmod -R u+rwx $t0 && rm -rf $t0 && exit $status' 0 +trap '(exit $?); exit $?' 1 2 13 15 + +framework_failure=0 +mkdir -p $tmp || framework_failure=1 +cd $tmp || framework_failure=1 + +cat <<\EOF > exp || framework_failure=1 +runcon: runcon may be used only on a SELinux kernel +EOF + +if test $framework_failure = 1; then + echo "$0: failure in testing framework" 1>&2 + (exit 1); exit 1 +fi + +fail=0 + +# This test works even on systems without SELinux. +# On such a system it fails with the above diagnostic, which is fine. +# Before the no-reorder change, it would have failed with a diagnostic +# about -j being an invalid option. +runcon -t unconfined_t true -j 2> out && : > exp + +cmp out exp || fail=1 +test $fail = 1 && diff out exp 2> /dev/null + +(exit $fail); exit $fail