aboutsummaryrefslogtreecommitdiff
path: root/src/rc
diff options
context:
space:
mode:
authorJason Zaman <jason@perfinion.com>2014-10-30 10:22:02 +0800
committerWilliam Hubbs <w.d.hubbs@gmail.com>2014-11-03 09:31:25 -0600
commit1932360adca3f9fe9b47bcfad7b8bd5efbd33bee (patch)
tree48a6bcb311746736976c0650cf49ac567aaf42ae /src/rc
parentbe952bebb3647069fb93b9791ee3439698f697ca (diff)
Integrate the functionality from runscript_selinux.so
runscript used to dlopen() runscript_selinux.so. This adds equivalent functionality directly in to runscript instead. It authenticates with either PAM or shadow and optionally has a dep on audit. X-Gentoo-Bug: 517450 X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=517450
Diffstat (limited to 'src/rc')
-rw-r--r--src/rc/rc-selinux.c292
-rw-r--r--src/rc/rc-selinux.h2
-rw-r--r--src/rc/runscript.c2
3 files changed, 265 insertions, 31 deletions
diff --git a/src/rc/rc-selinux.c b/src/rc/rc-selinux.c
index 518b25df..7124e83e 100644
--- a/src/rc/rc-selinux.c
+++ b/src/rc/rc-selinux.c
@@ -1,7 +1,7 @@
/*
- rc-selinux.c
- SELinux helpers to get and set contexts.
-*/
+ * rc-selinux.c
+ * SELinux helpers to get and set contexts.
+ */
/*
* Copyright (c) 2014 Jason Zaman <jason@perfinion.com>
@@ -31,11 +31,18 @@
#include <stddef.h>
#include <errno.h>
#include <dlfcn.h>
-
-#include <sys/stat.h>
+#include <ctype.h>
+#include <limits.h>
+#include <pwd.h>
+#include <unistd.h>
#include <selinux/selinux.h>
#include <selinux/label.h>
+#include <selinux/get_default_type.h>
+#include <selinux/context.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
#include "einfo.h"
#include "queue.h"
@@ -44,11 +51,28 @@
#include "rc-plugin.h"
#include "rc-selinux.h"
-#define SELINUX_LIB RC_LIBDIR "/runscript_selinux.so"
+/* the context files for selinux */
+#define RUN_INIT_FILE "run_init_type"
+#define INITRC_FILE "initrc_context"
-static void (*selinux_run_init_old) (void);
-static void (*selinux_run_init_new) (int argc, char **argv);
+#ifdef HAVE_AUDIT
+#include <libaudit.h>
+#endif
+/* PAM or shadow for authentication */
+#ifdef HAVE_PAM
+# define PAM_SERVICE_NAME "run_init" /* the name of this program for PAM */
+# include <security/pam_appl.h>
+# include <security/pam_misc.h>
+#else
+# define PASSWORD_PROMPT "Password:"
+# include <crypt.h>
+# include <shadow.h>
+# include <string.h>
+#endif
+
+
+/* The handle for the fcontext lookups */
static struct selabel_handle *hnd = NULL;
int selinux_util_label(const char *path)
@@ -133,33 +157,243 @@ int selinux_util_close(void)
return 0;
}
-void selinux_setup(int argc, char **argv)
+/*
+ * This will check the users password and return 0 on success or -1 on fail
+ *
+ * We ask for the password to make sure it is intended vs run by malicious software.
+ * Actual authorization is covered by the policy itself.
+ */
+static int check_password(char *username)
{
- void *lib_handle = NULL;
+ int ret = 1;
+#ifdef HAVE_PAM
+ pam_handle_t *pamh;
+ int pam_err = 0;
+ const struct pam_conv pconv = {
+ misc_conv,
+ NULL
+ };
- if (!exists(SELINUX_LIB))
- return;
+ pam_err = pam_start(PAM_SERVICE_NAME, username, &pconv, &pamh);
+ if (pam_err != PAM_SUCCESS) {
+ ret = -1;
+ goto outpam;
+ }
- lib_handle = dlopen(SELINUX_LIB, RTLD_NOW | RTLD_GLOBAL);
- if (!lib_handle) {
- eerror("dlopen: %s", dlerror());
- return;
+ pam_err = pam_authenticate(pamh, PAM_DISALLOW_NULL_AUTHTOK);
+ if (pam_err != PAM_SUCCESS) {
+ ret = -1;
+ goto outpam;
+ }
+
+ ret = 0;
+outpam:
+ pam_end(pamh, pam_err);
+ pamh = NULL;
+
+#else /* authenticating via /etc/shadow instead */
+ struct spwd *spw;
+ char *password;
+ char *attempt;
+
+ spw = getspnam(username);
+ if (!spw) {
+ eerror("Failed to read shadow entry");
+ ret = -1;
+ goto outshadow;
+ }
+
+ attempt = getpass(PASSWORD_PROMPT);
+ if (!attempt) {
+ ret = -1;
+ goto outshadow;
+ }
+
+ if (*spw->sp_pwdp == '\0' && *attempt == '\0') {
+ ret = -1;
+ goto outshadow;
+ }
+
+ /* salt must be at least two characters long */
+ if (!(spw->sp_pwdp[0] && spw->sp_pwdp[1])) {
+ ret = -1;
+ goto outshadow;
}
- selinux_run_init_old = (void (*)(void))
- dlfunc(lib_handle, "selinux_runscript");
- selinux_run_init_new = (void (*)(int, char **))
- dlfunc(lib_handle, "selinux_runscript2");
+ /* encrypt the password attempt */
+ password = crypt(attempt, spw->sp_pwdp);
- /* Use new run_init if it exists, else fall back to old */
- if (selinux_run_init_new)
- selinux_run_init_new(argc, argv);
- else if (selinux_run_init_old)
- selinux_run_init_old();
+ if (password && strcmp(password, spw->sp_pwdp) == 0)
+ ret = 0;
else
- /* This shouldnt happen... probably corrupt lib */
- eerrorx
- ("run_init is missing from runscript_selinux.so!");
+ ret = -1;
+outshadow:
+#endif
+ return ret;
+}
+
+/* Authenticates the user, returns 0 on success, 1 on fail */
+static int check_auth()
+{
+ struct passwd *pw;
+ uid_t uid;
+
+#ifdef HAVE_AUDIT
+ uid = audit_getloginuid();
+ if (uid == (uid_t) -1)
+ uid = getuid();
+#else
+ uid = getuid();
+#endif
+
+ pw = getpwuid(uid);
+ if (!pw) {
+ eerror("cannot find your entry in the passwd file.");
+ return (-1);
+ }
+
+ printf("Authenticating %s.\n", pw->pw_name);
+
+ /* do the actual check */
+ if (check_password(pw->pw_name) == 0) {
+ return 0;
+ }
+
+ eerrorx("Authentication failed for %s", pw->pw_name);
+ return 1;
+}
+
+/*
+ * Read the context from the given context file. context must be free'd by the user.
+ */
+static int read_context_file(const char *filename, char **context)
+{
+ int ret = -1;
+ FILE *fp;
+ char filepath[PATH_MAX];
+ char *line = NULL;
+ char *p;
+ char *p2;
+ size_t len = 0;
+ ssize_t read;
+
+ memset(filepath, '\0', PATH_MAX);
+ snprintf(filepath, PATH_MAX - 1, "%s/%s", selinux_contexts_path(), filename);
+
+ fp = fopen(filepath, "r");
+ if (fp == NULL) {
+ eerror("Failed to open context file: %s", filename);
+ return -1;
+ }
+
+ while ((read = getline(&line, &len, fp)) != -1) {
+ /* cut off spaces before the string */
+ p = line;
+ while (isspace(*p) && *p != '\0')
+ p++;
+
+ /* empty string, skip */
+ if (*p == '\0')
+ continue;
+
+ /* cut off spaces after the string */
+ p2 = p;
+ while (!isspace(*p2) && *p2 != '\0')
+ p2++;
+ *p2 = '\0';
+
+ *context = xstrdup(p);
+ ret = 0;
+ break;
+ }
+
+ free(line);
+ fclose(fp);
+ return ret;
+}
+
+void selinux_setup(char **argv)
+{
+ char *new_context = NULL;
+ char *curr_context = NULL;
+ context_t curr_con;
+ char *curr_t = NULL;
+ char *run_init_t = NULL;
+
+ /* Return, if selinux is disabled. */
+ if (is_selinux_enabled() < 1) {
+ return;
+ }
+
+ if (read_context_file(RUN_INIT_FILE, &run_init_t) != 0) {
+ /* assume a reasonable default, rather than bailing out */
+ run_init_t = xstrdup("run_init_t");
+ ewarn("Assuming SELinux run_init type is %s", run_init_t);
+ }
+
+ /* Get our current context. */
+ if (getcon(&curr_context) < 0) {
+ if (errno == ENOENT) {
+ /* should only hit this if proc is not mounted. this
+ * happens on Gentoo right after init starts, when
+ * the init script processing starts.
+ */
+ goto out;
+ } else {
+ perror("getcon");
+ exit(1);
+ }
+ }
+
+ /* extract the type from the context */
+ curr_con = context_new(curr_context);
+ curr_t = xstrdup(context_type_get(curr_con));
+ /* dont need them anymore so free() now */
+ context_free(curr_con);
+ free(curr_context);
+
+ /* if we are not in the run_init domain, we should not do anything */
+ if (strncmp(run_init_t, curr_t, strlen(run_init_t)) != 0) {
+ goto out;
+ }
+
+ free(curr_t);
+ free(run_init_t);
+
+ if (check_auth() != 0) {
+ eerrorx("Authentication failed.");
+ }
+
+ /* Get the context for the script to be run in. */
+ if (read_context_file(INITRC_FILE, &new_context) != 0) {
+ /* assume a reasonable default, rather than bailing out */
+ new_context = xstrdup("system_u:system_r:initrc_t");
+ ewarn("Assuming SELinux initrc context is %s", new_context);
+ }
+
+ /* Set the new context */
+ if (setexeccon(new_context) < 0) {
+ eerrorx("Could not set SELinux exec context to %s.", new_context);
+ }
+
+ free(new_context);
+
+ /*
+ * exec will recycle ptys so try and use open_init_pty if it exists
+ * which will open the pty with initrc_devpts_t, if it doesnt exist,
+ * fall back to plain exec
+ */
+ if (access("/usr/sbin/open_init_pty", X_OK)) {
+ if (execvp("/usr/sbin/open_init_pty", argv)) {
+ perror("execvp");
+ exit(-1);
+ }
+ } else if (execvp(argv[1], argv + 1)) {
+ perror("execvp");
+ exit(-1);
+ }
- dlclose(lib_handle);
+out:
+ free(run_init_t);
+ free(curr_t);
}
diff --git a/src/rc/rc-selinux.h b/src/rc/rc-selinux.h
index 8cf73b05..e28f3339 100644
--- a/src/rc/rc-selinux.h
+++ b/src/rc/rc-selinux.h
@@ -30,6 +30,6 @@ int selinux_util_open(void);
int selinux_util_label(const char *path);
int selinux_util_close(void);
-void selinux_setup(int argc, char **argv);
+void selinux_setup(char **argv);
#endif
diff --git a/src/rc/runscript.c b/src/rc/runscript.c
index 882cb8c0..0ac19661 100644
--- a/src/rc/runscript.c
+++ b/src/rc/runscript.c
@@ -1193,7 +1193,7 @@ openrc_run(int argc, char **argv)
#ifdef HAVE_SELINUX
/* Ok, we are ready to go, so setup selinux if applicable */
- selinux_setup(argc, argv);
+ selinux_setup(argv);
#endif
deps = true;