All of lore.kernel.org
 help / color / mirror / Atom feed
From: Richard Haines <richard_c_haines@btinternet.com>
To: selinux@tycho.nsa.gov
Subject: [PATCH 2/3] libselinux: Add setfiles support to selinux_restorecon(3)
Date: Tue, 10 May 2016 16:23:18 +0100	[thread overview]
Message-ID: <1462893798-9559-1-git-send-email-richard_c_haines@btinternet.com> (raw)

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

Signed-off-by: Richard Haines <richard_c_haines@btinternet.com>
---
 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

             reply	other threads:[~2016-05-10 15:23 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-05-10 15:23 Richard Haines [this message]
2016-05-20 17:03 ` [PATCH 2/3] libselinux: Add setfiles support to selinux_restorecon(3) Stephen Smalley
2016-05-31 15:21   ` Richard Haines

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1462893798-9559-1-git-send-email-richard_c_haines@btinternet.com \
    --to=richard_c_haines@btinternet.com \
    --cc=selinux@tycho.nsa.gov \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.