mirror of
git://git.sv.gnu.org/coreutils.git
synced 2026-02-10 01:12:02 +02:00
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.
This commit is contained in:
16
ChangeLog
16
ChangeLog
@@ -789,6 +789,22 @@
|
||||
|
||||
2007-01-04 Jim Meyering <jim@meyering.net>
|
||||
|
||||
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.
|
||||
|
||||
|
||||
16
README
16
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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
31
gl/lib/se-context_.h
Normal file
31
gl/lib/se-context_.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#ifndef SELINUX_CONTEXT_H
|
||||
# define SELINUX_CONTEXT_H
|
||||
|
||||
# include <errno.h>
|
||||
/* 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
|
||||
54
gl/lib/se-selinux_.h
Normal file
54
gl/lib/se-selinux_.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#ifndef SELINUX_SELINUX_H
|
||||
# define SELINUX_SELINUX_H
|
||||
|
||||
# include <sys/types.h>
|
||||
# include <errno.h>
|
||||
/* 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
|
||||
94
gl/lib/selinux-at.c
Normal file
94
gl/lib/selinux-at.c
Normal file
@@ -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 <config.h>
|
||||
#endif
|
||||
|
||||
#include "selinux-at.h"
|
||||
#include "openat.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#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
|
||||
24
gl/lib/selinux-at.h
Normal file
24
gl/lib/selinux-at.h
Normal file
@@ -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 <selinux/selinux.h>
|
||||
#include <selinux/context.h>
|
||||
|
||||
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);
|
||||
18
gl/m4/selinux-context-h.m4
Normal file
18
gl/m4/selinux-context-h.m4
Normal file
@@ -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 <selinux/context.h>, if necessary.
|
||||
|
||||
AC_DEFUN([gl_HEADERS_SELINUX_CONTEXT_H],
|
||||
[
|
||||
AC_LIBSOURCES([se-context_.h])
|
||||
# Check for <selinux/context.h>,
|
||||
AC_CHECK_HEADERS([selinux/context.h],
|
||||
[SELINUX_CONTEXT_H=],
|
||||
[SELINUX_CONTEXT_H=selinux/context.h])
|
||||
AC_SUBST([SELINUX_CONTEXT_H])
|
||||
])
|
||||
18
gl/m4/selinux-selinux-h.m4
Normal file
18
gl/m4/selinux-selinux-h.m4
Normal file
@@ -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 <selinux/selinux.h>, if necessary.
|
||||
|
||||
AC_DEFUN([gl_HEADERS_SELINUX_SELINUX_H],
|
||||
[
|
||||
AC_LIBSOURCES([se-selinux_.h])
|
||||
# Check for <selinux/selinux.h>,
|
||||
AC_CHECK_HEADERS([selinux/selinux.h],
|
||||
[SELINUX_SELINUX_H=],
|
||||
[SELINUX_SELINUX_H=selinux/selinux.h])
|
||||
AC_SUBST([SELINUX_SELINUX_H])
|
||||
])
|
||||
32
gl/modules/selinux-at
Normal file
32
gl/modules/selinux-at
Normal file
@@ -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
|
||||
54
gl/modules/selinux-h
Normal file
54
gl/modules/selinux-h
Normal file
@@ -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 <selinux/selinux.h>
|
||||
#include <selinux/context.h>
|
||||
|
||||
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"
|
||||
@@ -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)
|
||||
|
||||
589
src/chcon.c
Normal file
589
src/chcon.c
Normal file
@@ -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 <config.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
Reference in New Issue
Block a user