Add additional error handling, flags, xdev and alt_rootpath support for setfiles(8) functionality. Signed-off-by: Richard Haines <richard_c_haines@xxxxxxxxxxxxxx> --- 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 Selinux@xxxxxxxxxxxxx To unsubscribe, send email to Selinux-leave@xxxxxxxxxxxxx. To get help, send an email containing "help" to Selinux-request@xxxxxxxxxxxxx.