Add additional error handling, flags, xdev and alt_rootpath
support for setfiles(8) functionality.

Signed-off-by: Richard Haines <[email protected]>
---
 libselinux/include/selinux/restorecon.h            |  17 +++
 libselinux/man/man3/selinux_restorecon.3           |  13 +++
 .../man/man3/selinux_restorecon_set_alt_rootpath.3 |  34 ++++++
 libselinux/src/selinux_restorecon.c                | 126 +++++++++++++++++----
 libselinux/utils/selinux_restorecon.c              |  28 ++++-
 5 files changed, 197 insertions(+), 21 deletions(-)
 create mode 100644 libselinux/man/man3/selinux_restorecon_set_alt_rootpath.3

diff --git a/libselinux/include/selinux/restorecon.h 
b/libselinux/include/selinux/restorecon.h
index 0b93b0c..6b3e0f1 100644
--- a/libselinux/include/selinux/restorecon.h
+++ b/libselinux/include/selinux/restorecon.h
@@ -50,6 +50,14 @@ extern int selinux_restorecon(const char *pathname,
  * If there is a different context that matched the inode,
  * then use the first context that matched. */
 #define SELINUX_RESTORECON_ADD_ASSOC                   256
+/* Abort on errors during the file tree walk. */
+#define SELINUX_RESTORECON_ABORT_ON_ERROR              512
+/* Log any label changes to syslog. */
+#define SELINUX_RESTORECON_SYSLOG_CHANGES              1024
+/* Log what spec matched each file. */
+#define SELINUX_RESTORECON_LOG_MATCHES                 2048
+/* Ignore files that do not exist. */
+#define SELINUX_RESTORECON_IGNORE_NOENTRY              4096
 
 /**
  * selinux_restorecon_set_sehandle - Set the global fc handle.
@@ -77,6 +85,15 @@ extern struct selabel_handle 
*selinux_restorecon_default_handle(void);
  */
 extern void selinux_restorecon_set_exclude_list(const char **exclude_list);
 
+/**
+ * selinux_restorecon_set_alt_rootpath - Use alternate rootpath.
+ *                                      directories that are to be excluded
+ *                                      from relabeling.
+ * @alt_rootpath: containing the alternate rootpath to be used. The path MUST
+ *               NOT be terminated with a '/'. The path MUST NOT be only '/'.
+ */
+extern void selinux_restorecon_set_alt_rootpath(const char *alt_rootpath);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/libselinux/man/man3/selinux_restorecon.3 
b/libselinux/man/man3/selinux_restorecon.3
index bbb6721..1d79f91 100644
--- a/libselinux/man/man3/selinux_restorecon.3
+++ b/libselinux/man/man3/selinux_restorecon.3
@@ -106,6 +106,19 @@ entry from which the descent began.
 .B SELINUX_RESTORECON_ADD_ASSOC
 attempt to add an association between an inode and a context. If there is a
 different context that matched the inode, then use the first context that 
matched.
+.sp
+.B SELINUX_RESTORECON_ABORT_ON_ERROR
+abort on errors during the file tree walk.
+.sp
+.B SELINUX_RESTORECON_SYSLOG_CHANGES
+log any label changes to
+.BR syslog (3).
+.sp
+.B SELINUX_RESTORECON_LOG_MATCHES
+log what specfile context matched each file.
+.sp
+.B SELINUX_RESTORECON_IGNORE_NOENTRY
+ignore files that do not exist.
 .RE
 .sp
 The behavior regarding the checking and updating of the SHA1 digest described
diff --git a/libselinux/man/man3/selinux_restorecon_set_alt_rootpath.3 
b/libselinux/man/man3/selinux_restorecon_set_alt_rootpath.3
new file mode 100644
index 0000000..79223eb
--- /dev/null
+++ b/libselinux/man/man3/selinux_restorecon_set_alt_rootpath.3
@@ -0,0 +1,34 @@
+.TH "selinux_restorecon_set_alt_rootpath" "3" "28 April 2016" "Security 
Enhanced Linux" "SELinux API documentation"
+
+.SH "NAME"
+selinux_restorecon_set_alt_rootpath \- set an alternate rootpath.
+.
+.SH "SYNOPSIS"
+.B #include <selinux/restorecon.h>
+.sp
+.BI "void selinux_restorecon_set_alt_rootpath(const char *" alt_rootpath ");"
+.in +\w'void selinux_restorecon_set_alt_rootpath('u
+.
+.SH "DESCRIPTION"
+.BR selinux_restorecon_set_alt_rootpath ()
+passes to
+.BR selinux_restorecon (3)
+a pointer containing an alternate rootpath
+.IR alt_rootpath .
+.br
+The path MUST NOT be terminated with a trailing '/'.
+.br
+The path MUST NOT be '/' (i.e. the root path).
+.sp
+.BR selinux_restorecon_set_alt_rootpath ()
+must be called prior to
+.BR selinux_restorecon (3).
+.
+.SH "SEE ALSO"
+.BR selinux_restorecon (3),
+.br
+.BR selinux_restorecon_set_sehandle (3),
+.br
+.BR selinux_restorecon_default_handle (3),
+.br
+.BR selinux_restorecon_set_exclude_list (3)
diff --git a/libselinux/src/selinux_restorecon.c 
b/libselinux/src/selinux_restorecon.c
index 2794659..11ae8a0 100644
--- a/libselinux/src/selinux_restorecon.c
+++ b/libselinux/src/selinux_restorecon.c
@@ -22,6 +22,7 @@
 #include <sys/vfs.h>
 #include <linux/magic.h>
 #include <libgen.h>
+#include <syslog.h>
 #include <selinux/selinux.h>
 #include <selinux/context.h>
 #include <selinux/label.h>
@@ -39,6 +40,8 @@ static struct selabel_handle *fc_sehandle = NULL;
 static unsigned char *fc_digest = NULL;
 static size_t fc_digest_len = 0;
 static const char **fc_exclude_list = NULL;
+static const char *rootpath = NULL;
+static int rootpathlen;
 static size_t fc_count = 0;
 #define STAR_COUNT 1000
 
@@ -47,12 +50,16 @@ struct rest_flags {
        bool nochange;
        bool verbose;
        bool progress;
-       bool specctx;
+       bool set_specctx;
        bool add_assoc;
-       bool ignore;
+       bool ignore_digest;
        bool recurse;
        bool userealpath;
        bool xdev;
+       bool abort_on_error;
+       bool syslog_changes;
+       bool log_matches;
+       bool ignore_enoent;
 };
 
 static void restorecon_init(void)
@@ -67,13 +74,15 @@ static void restorecon_init(void)
 
 static pthread_once_t fc_once = PTHREAD_ONCE_INIT;
 
-
 static int check_excluded(const char *file)
 {
        int i;
+       size_t len;
 
        for (i = 0; fc_exclude_list[i]; i++) {
-               if (strcmp(file, fc_exclude_list[i]) == 0)
+               len = strlen(fc_exclude_list[i]);
+               /* Check if 'file' is in an excluded directory. */
+               if (strncmp(file, fc_exclude_list[i], len) == 0)
                                return 1;
        }
        return 0;
@@ -360,10 +369,29 @@ static int restorecon_sb(const char *pathname, const 
struct stat *sb,
        char *newcon = NULL;
        char *curcon = NULL;
        char *newtypecon = NULL;
-       int rc = 0;
+       int rc;
        bool updated = false;
+       const char *tmp_pathname = pathname;
+
+       if (rootpath) {
+               if (strncmp(rootpath, tmp_pathname, rootpathlen) != 0) {
+                       selinux_log(SELINUX_ERROR,
+                                   "%s is not located in alt_rootpath %s\n",
+                                   tmp_pathname, rootpath);
+                       return -1;
+               }
+               tmp_pathname += rootpathlen;
+       }
 
-       if (selabel_lookup_raw(fc_sehandle, &newcon, pathname, sb->st_mode) < 0)
+       if (rootpath != NULL && tmp_pathname[0] == '\0')
+               /* this is actually the root dir of the alt root. */
+               rc = selabel_lookup_raw(fc_sehandle, &newcon, "/",
+                                                   sb->st_mode);
+       else
+               rc = selabel_lookup_raw(fc_sehandle, &newcon, tmp_pathname,
+                                                   sb->st_mode);
+
+       if (rc < 0)
                return 0; /* no match, but not an error */
 
        if (flags->add_assoc) {
@@ -385,6 +413,10 @@ static int restorecon_sb(const char *pathname, const 
struct stat *sb,
                }
        }
 
+       if (flags->log_matches)
+               selinux_log(SELINUX_INFO, "%s matched by %s\n",
+                           pathname, newcon);
+
        if (lgetfilecon_raw(pathname, &curcon) < 0) {
                if (errno != ENODATA)
                        goto err;
@@ -401,7 +433,7 @@ static int restorecon_sb(const char *pathname, const struct 
stat *sb,
        }
 
        if (strcmp(curcon, newcon) != 0) {
-               if (!flags->specctx && curcon &&
+               if (!flags->set_specctx && curcon &&
                                    (is_context_customizable(curcon) > 0)) {
                        if (flags->verbose) {
                                selinux_log(SELINUX_INFO,
@@ -411,7 +443,7 @@ static int restorecon_sb(const char *pathname, const struct 
stat *sb,
                        }
                }
 
-               if (!flags->specctx && curcon) {
+               if (!flags->set_specctx && curcon) {
                        /* If types different then update newcon. */
                        rc = compare_types(curcon, newcon, &newtypecon);
                        if (rc)
@@ -436,6 +468,16 @@ static int restorecon_sb(const char *pathname, const 
struct stat *sb,
                                    "%s %s from %s to %s\n",
                                    updated ? "Relabeled" : "Would relabel",
                                    pathname, curcon, newcon);
+
+               if (flags->syslog_changes && !flags->nochange) {
+                       if (curcon)
+                               syslog(LOG_INFO,
+                                           "relabeling %s from %s to %s\n",
+                                           pathname, curcon, newcon);
+                       else
+                               syslog(LOG_INFO, "labeling %s to %s\n",
+                                           pathname, newcon);
+               }
        }
 
 out:
@@ -468,11 +510,11 @@ int selinux_restorecon(const char *pathname_orig,
                    SELINUX_RESTORECON_VERBOSE) ? true : false;
        flags.progress = (restorecon_flags &
                    SELINUX_RESTORECON_PROGRESS) ? true : false;
-       flags.specctx = (restorecon_flags &
+       flags.set_specctx = (restorecon_flags &
                    SELINUX_RESTORECON_SET_SPECFILE_CTX) ? true : false;
        flags.add_assoc = (restorecon_flags &
                   SELINUX_RESTORECON_ADD_ASSOC) ? true : false;
-       flags.ignore = (restorecon_flags &
+       flags.ignore_digest = (restorecon_flags &
                    SELINUX_RESTORECON_IGNORE_DIGEST) ? true : false;
        flags.recurse = (restorecon_flags &
                    SELINUX_RESTORECON_RECURSE) ? true : false;
@@ -480,6 +522,14 @@ int selinux_restorecon(const char *pathname_orig,
                   SELINUX_RESTORECON_REALPATH) ? true : false;
        flags.xdev = (restorecon_flags &
                   SELINUX_RESTORECON_XDEV) ? true : false;
+       flags.abort_on_error = (restorecon_flags &
+                  SELINUX_RESTORECON_ABORT_ON_ERROR) ? true : false;
+       flags.syslog_changes = (restorecon_flags &
+                  SELINUX_RESTORECON_SYSLOG_CHANGES) ? true : false;
+       flags.log_matches = (restorecon_flags &
+                  SELINUX_RESTORECON_LOG_MATCHES) ? true : false;
+       flags.ignore_enoent = (restorecon_flags &
+                  SELINUX_RESTORECON_IGNORE_NOENTRY) ? true : false;
 
        bool issys;
        bool setrestoreconlast = true; /* TRUE = set xattr RESTORECON_LAST
@@ -494,6 +544,7 @@ int selinux_restorecon(const char *pathname_orig,
        int error, sverrno;
        char *xattr_value = NULL;
        ssize_t size;
+       dev_t dev_num = 0;
 
        if (flags.verbose && flags.progress)
                flags.verbose = false;
@@ -545,8 +596,17 @@ int selinux_restorecon(const char *pathname_orig,
                            sizeof(SYS_PREFIX) - 1)) ? true : false;
 
        if (lstat(pathname, &sb) < 0) {
-               error = -1;
-               goto cleanup;
+               if (flags.ignore_enoent && errno == ENOENT) {
+                       free(pathdnamer);
+                       free(pathname);
+                       return 0;
+               } else {
+                       selinux_log(SELINUX_ERROR,
+                                   "lstat(%s) failed: %s\n",
+                                   pathname, strerror(errno));
+                       error = -1;
+                       goto cleanup;
+               }
        }
 
        /* Ignore restoreconlast if not a directory */
@@ -572,7 +632,7 @@ int selinux_restorecon(const char *pathname_orig,
                size = getxattr(pathname, RESTORECON_LAST, xattr_value,
                                                            fc_digest_len);
 
-               if (!flags.ignore && size == fc_digest_len &&
+               if (!flags.ignore_digest && size == fc_digest_len &&
                            memcmp(fc_digest, xattr_value, fc_digest_len)
                                                                    == 0) {
                        selinux_log(SELINUX_INFO,
@@ -589,13 +649,22 @@ int selinux_restorecon(const char *pathname_orig,
                fts_flags = FTS_PHYSICAL | FTS_NOCHDIR;
 
        fts = fts_open(paths, fts_flags, NULL);
-       if (!fts) {
-               error = -1;
-               goto cleanup;
-       }
+       if (!fts)
+               goto fts_err;
+
+       ftsent = fts_read(fts);
+       if (!ftsent)
+               goto fts_err;
+
+       /* Keep the inode of the first device. */
+       dev_num = ftsent->fts_statp->st_dev;
 
        error = 0;
-       while ((ftsent = fts_read(fts)) != NULL) {
+       do {
+               /* If the XDEV flag is set and the device is different */
+               if (flags.xdev && ftsent->fts_statp->st_dev != dev_num)
+                       continue;
+
                switch (ftsent->fts_info) {
                case FTS_DC:
                        selinux_log(SELINUX_ERROR,
@@ -644,9 +713,12 @@ int selinux_restorecon(const char *pathname_orig,
 
                        error |= restorecon_sb(ftsent->fts_path,
                                               ftsent->fts_statp, &flags);
+
+                       if (error && flags.abort_on_error)
+                               goto out;
                        break;
                }
-       }
+       } while ((ftsent = fts_read(fts)) != NULL);
 
        /* Labeling successful. Mark the top level directory as completed. */
        if (setrestoreconlast && !flags.nochange && !error) {
@@ -672,12 +744,14 @@ cleanup:
        free(pathname);
        free(xattr_value);
        return error;
+
 oom:
        sverrno = errno;
        selinux_log(SELINUX_ERROR, "%s:  Out of memory\n", __func__);
        errno = sverrno;
        error = -1;
        goto cleanup;
+
 realpatherr:
        sverrno = errno;
        selinux_log(SELINUX_ERROR,
@@ -686,6 +760,13 @@ realpatherr:
        errno = sverrno;
        error = -1;
        goto cleanup;
+
+fts_err:
+       selinux_log(SELINUX_ERROR,
+                   "fts error while labeling %s: %s\n",
+                   paths[0], strerror(errno));
+       error = -1;
+       goto cleanup;
 }
 
 /* selinux_restorecon_set_sehandle(3) is called to set the global fc handle */
@@ -757,3 +838,10 @@ void selinux_restorecon_set_exclude_list(const char 
**exclude_list)
 {
        fc_exclude_list = exclude_list;
 }
+
+/* selinux_restorecon_set_alt_rootpath(3) sets an alternate rootpath. */
+void selinux_restorecon_set_alt_rootpath(const char *alt_rootpath)
+{
+       rootpath = alt_rootpath;
+       rootpathlen = strlen(rootpath);
+}
diff --git a/libselinux/utils/selinux_restorecon.c 
b/libselinux/utils/selinux_restorecon.c
index 2552d63..da59fcd 100644
--- a/libselinux/utils/selinux_restorecon.c
+++ b/libselinux/utils/selinux_restorecon.c
@@ -37,7 +37,7 @@ static int validate_context(char **contextp)
 static void usage(const char *progname)
 {
        fprintf(stderr,
-               "\nusage: %s [-FCnRrdeia] [-v|-P] [-p policy] [-f specfile] "
+               "\nusage: %s [-FCnRrdeiIaAsl] [-v|-P] [-x alt_rootpath] [-p 
policy] [-f specfile] "
                "pathname ...\n"
                "Where:\n\t"
                "-F  Set the label to that in specfile.\n\t"
@@ -57,9 +57,14 @@ static void usage(const char *progname)
                "-e  Exclude this file/directory (add multiple -e entries).\n\t"
                "-i  Do not set SELABEL_OPT_DIGEST option when calling "
                " selabel_open(3).\n\t"
+               "-I  Ignore files that do not exist.\n\t"
                "-a  Add an association between an inode and a context.\n\t"
                "    If there is a different context that matched the 
inode,\n\t"
                "    then use the first context that matched.\n\t"
+               "-A  Abort on errors during the file tree walk.\n\t"
+               "-s  Log any label changes to syslog(3).\n\t"
+               "-l  Log what specfile context matched each file.\n\t"
+               "-x  Set alternate rootpath.\n\t"
                "-p  Optional binary policy file (also sets validate context "
                "option).\n\t"
                "-f  Optional file contexts file.\n\t"
@@ -101,6 +106,7 @@ int main(int argc, char **argv)
        int opt, i;
        unsigned int restorecon_flags = 0;
        char *path = NULL, *digest = NULL, *validate = NULL;
+       char *alt_rootpath = NULL;
        FILE *policystream;
        bool ignore_digest = false, require_selinux = true;
        bool verbose = false, progress = false;
@@ -118,7 +124,7 @@ int main(int argc, char **argv)
        exclude_list = NULL;
        exclude_count = 0;
 
-       while ((opt = getopt(argc, argv, "iFCnRvPrdae:f:p:")) > 0) {
+       while ((opt = getopt(argc, argv, "iIFCnRvPrdaAsle:f:p:x:")) > 0) {
                switch (opt) {
                case 'F':
                        restorecon_flags |=
@@ -190,9 +196,24 @@ int main(int argc, char **argv)
                case 'i':
                        ignore_digest = true;
                        break;
+               case 'I':
+                       restorecon_flags |= SELINUX_RESTORECON_IGNORE_NOENTRY;
+                       break;
                case 'a':
                        restorecon_flags |= SELINUX_RESTORECON_ADD_ASSOC;
                        break;
+               case 'A':
+                       restorecon_flags |= SELINUX_RESTORECON_ABORT_ON_ERROR;
+                       break;
+               case 's':
+                       restorecon_flags |= SELINUX_RESTORECON_SYSLOG_CHANGES;
+                       break;
+               case 'l':
+                       restorecon_flags |= SELINUX_RESTORECON_LOG_MATCHES;
+                       break;
+               case 'x':
+                       alt_rootpath = optarg;
+                       break;
                default:
                        usage(argv[0]);
                }
@@ -247,6 +268,9 @@ int main(int argc, char **argv)
                selinux_restorecon_set_exclude_list
                                                 ((const char **)exclude_list);
 
+       if (alt_rootpath)
+               selinux_restorecon_set_alt_rootpath(alt_rootpath);
+
        /* Call restorecon for each path in list */
        for (i = optind; i < argc; i++) {
                if (selinux_restorecon(argv[i], restorecon_flags) < 0) {
-- 
2.5.5

_______________________________________________
Selinux mailing list
[email protected]
To unsubscribe, send email to [email protected].
To get help, send an email containing "help" to [email protected].

Reply via email to