commit:     1932360adca3f9fe9b47bcfad7b8bd5efbd33bee
Author:     Jason Zaman <jason <AT> perfinion <DOT> com>
AuthorDate: Thu Oct 30 02:22:02 2014 +0000
Commit:     William Hubbs <williamh <AT> gentoo <DOT> org>
CommitDate: Mon Nov  3 15:31:25 2014 +0000
URL:        
http://sources.gentoo.org/gitweb/?p=proj/openrc.git;a=commit;h=1932360a

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

---
 mk/os-Linux.mk      |  15 ++-
 mk/pam.mk           |   6 ++
 src/rc/rc-selinux.c | 292 ++++++++++++++++++++++++++++++++++++++++++++++------
 src/rc/rc-selinux.h |   2 +-
 src/rc/runscript.c  |   2 +-
 5 files changed, 285 insertions(+), 32 deletions(-)

diff --git a/mk/os-Linux.mk b/mk/os-Linux.mk
index dc76b96..bb6fa37 100644
--- a/mk/os-Linux.mk
+++ b/mk/os-Linux.mk
@@ -9,6 +9,19 @@ LIBDL=         -Wl,-Bdynamic -ldl
 
 ifeq (${MKSELINUX},yes)
 CPPFLAGS+= -DHAVE_SELINUX
-LIBSELINUX= -lselinux
+LIBSELINUX?= -lselinux
 LDADD += $(LIBSELINUX)
+
+ifneq (${MKPAM},pam)
+# if using selinux but not pam then we need crypt
+LIBCRYPT?= -lcrypt
+LDADD += $(LIBCRYPT)
+endif
+
+endif
+
+ifeq (${MKAUDIT},yes)
+LIBAUDIT?=     -laudit
+CPPFLAGS+=     -DHAVE_AUDIT
+LDADD+=                ${LIBAUDIT}
 endif

diff --git a/mk/pam.mk b/mk/pam.mk
index 15ffb54..199896c 100644
--- a/mk/pam.mk
+++ b/mk/pam.mk
@@ -3,6 +3,12 @@ LIBPAM?=       -lpam
 CPPFLAGS+=     -DHAVE_PAM
 LDADD+=                ${LIBPAM}
 
+ifeq (${MKSELINUX},yes)
+# with selinux, pam_misc is needed too
+LIBPAM_MISC?=  -lpam_misc
+LDADD+=                ${LIBPAM_MISC}
+endif
+
 PAMDIR?=       /etc/pam.d
 PAMMODE?=      0644
 else ifneq (${MKPAM},)

diff --git a/src/rc/rc-selinux.c b/src/rc/rc-selinux.c
index 518b25d..7124e83 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 <ja...@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 8cf73b0..e28f333 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 882cb8c..0ac1966 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;

Reply via email to