diff options
| author | Jason Zaman <jason@perfinion.com> | 2014-10-30 10:22:02 +0800 | 
|---|---|---|
| committer | William Hubbs <w.d.hubbs@gmail.com> | 2014-11-03 09:31:25 -0600 | 
| commit | 1932360adca3f9fe9b47bcfad7b8bd5efbd33bee (patch) | |
| tree | 48a6bcb311746736976c0650cf49ac567aaf42ae /src/rc | |
| parent | be952bebb3647069fb93b9791ee3439698f697ca (diff) | |
| download | openrc-1932360adca3f9fe9b47bcfad7b8bd5efbd33bee.tar.xz | |
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.c | 292 | ||||
| -rw-r--r-- | src/rc/rc-selinux.h | 2 | ||||
| -rw-r--r-- | src/rc/runscript.c | 2 | 
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; | 
