aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/librc/rc.h.in3
-rw-r--r--src/openrc-pam/openrc-pam.c202
2 files changed, 171 insertions, 34 deletions
diff --git a/src/librc/rc.h.in b/src/librc/rc.h.in
index 8b1c705a..f6700801 100644
--- a/src/librc/rc.h.in
+++ b/src/librc/rc.h.in
@@ -61,6 +61,9 @@ extern "C" {
/*! @name Reserved runlevel names */
#define RC_LEVEL_USERNONE "none"
+#define RC_USER_ENV_WHITELIST RC_SYS_USER_CONFDIR "/env_whitelist"
+#define RC_USER_ENV_WHITELIST_D RC_SYS_USER_CONFDIR "/env_whitelist.d"
+
/*! Is openrc being ran in usermode?
* @return true if yes, otherwise false */
bool rc_is_user(void);
diff --git a/src/openrc-pam/openrc-pam.c b/src/openrc-pam/openrc-pam.c
index a9b2e36b..40b9a6dc 100644
--- a/src/openrc-pam/openrc-pam.c
+++ b/src/openrc-pam/openrc-pam.c
@@ -1,48 +1,192 @@
#include <librc.h>
#include <pwd.h>
#include <security/pam_appl.h>
+#include <security/pam_ext.h>
#include <security/pam_modules.h>
#include <stdbool.h>
#include <stdio.h>
#include <syslog.h>
#include <unistd.h>
+#include <string.h>
#include "einfo.h"
+#include "queue.h"
+
+static void load_envs_from_file(const char *path, RC_STRINGLIST *out) {
+ FILE *fp = NULL;
+ char *line = NULL;
+ size_t n = 0;
+ char *p = NULL;
+
+ fp = fopen(path, "r");
+ if (!fp) {
+ return;
+ }
+ while (getline(&line, &n, fp) != -1) {
+ if ((p = strchr(line, '\n'))) {
+ *p = '\0';
+ };
+ rc_stringlist_addu(out, line);
+ }
+ fclose(fp);
+}
+
+static RC_STRINGLIST *load_dir(const char *dir) {
+ RC_STRINGLIST *list = rc_stringlist_new();
+ DIR *dp = NULL;
+ struct dirent *d = NULL;
+ char *path;
+
+ if ((dp = opendir(dir)) != NULL) {
+ while ((d = readdir(dp)) != NULL) {
+ xasprintf(&path, "%s/%s", dir, d->d_name);
+ load_envs_from_file(path, list);
+ }
+ }
+ closedir(dp);
+
+ return list;
+}
+
+static void set_user_env(pam_handle_t *pamh) {
+ RC_STRINGLIST *allowed_env = NULL;
+ RC_STRINGLIST *user_env = NULL;
+ RC_STRING *env;
+ RC_STRING *uenv;
+ char *p;
+ char *user_env_path;
+
+ pam_syslog(pamh, LOG_INFO, "Loading allowed envs in %s", RC_USER_ENV_WHITELIST_D);
+ allowed_env = load_dir(RC_USER_ENV_WHITELIST_D);
+
+ pam_syslog(pamh, LOG_INFO, "Loading allowed envs in %s", RC_USER_ENV_WHITELIST);
+ load_envs_from_file(RC_USER_ENV_WHITELIST, allowed_env);
+
+ xasprintf(&user_env_path, "%s/openrc/env", pam_getenv(pamh, "XDG_RUNTIME_DIR"));
+
+ pam_syslog(pamh, LOG_INFO, "Loading user envs in %s", user_env_path);
+ user_env = load_dir(user_env_path);
+
+ TAILQ_FOREACH(env, allowed_env, entries) {
+ pam_syslog(pamh, LOG_INFO, "allowed env %s", env->value);
+ TAILQ_FOREACH(uenv, user_env, entries) {
+ p = strchr(uenv->value, '=');
+ if (p) {
+ *p = '\0';
+ if (strcmp(env->value, uenv->value) == 0) {
+ *p = '=';
+ pam_syslog(pamh, LOG_INFO, "Exporting: %s", uenv->value);
+ pam_putenv(pamh, uenv->value);
+ } else {
+ *p = '=';
+ }
+ }
+ }
+ }
+
+ pam_syslog(pamh, LOG_INFO, "Finished loading user environment");
+
+ rc_stringlist_free(allowed_env);
+ rc_stringlist_free(user_env);
+ free(user_env_path);
+}
+
+static int
+exec_user_cmd(struct passwd *pw, char *cmd, char **envlist)
+{
+ int retval;
+ const char *shellname = basename_c(pw->pw_shell);
+
+ switch (fork()) {
+ case 0:
+ setgid(pw->pw_gid);
+ setuid(pw->pw_uid);
+
+ execle(pw->pw_shell, shellname, "-c", cmd, NULL, envlist);
+
+ return -1;
+ break;
+ case -1:
+ return -1;
+ break;
+ }
+ wait(&retval);
+ return retval;
+}
+
+static char *create_xdg_runtime_dir(struct passwd *pw) {
+ char *path = NULL;
+ char *openrc_path = NULL;
+
+ if (mkdir("/run/user", 0755) != 0 && errno != EEXIST)
+ return NULL;
+
+ xasprintf(&path, "/run/user/%d/", pw->pw_uid);
+
+ if (mkdir(path, 0700) != 0 && errno != EEXIST) {
+ free(path);
+ return NULL;
+ }
+
+ if (chown(path, pw->pw_uid, pw->pw_gid) != 0) {
+ free(path);
+ return NULL;
+ }
+
+ xasprintf(&openrc_path, "%s/%s", path, "openrc");
+
+ if (mkdir(openrc_path, 0700) != 0 && errno != EEXIST) {
+ free(openrc_path);
+ free(path);
+ return NULL;
+ }
+
+ if (chown(openrc_path, pw->pw_uid, pw->pw_gid) != 0) {
+ free(openrc_path);
+ free(path);
+ return NULL;
+ }
+
+ return path;
+}
static bool exec_openrc(pam_handle_t *pamh, const char *runlevel, bool lock) {
char *cmd = NULL;
const char *username;
struct passwd *pw = NULL;
+ char *xdg_runtime_dir;
+ char *xdg_runtime_dir_env;
char **envlist;
char **env;
- envlist = pam_getenvlist(pamh);
-
if (pam_get_user(pamh, &username, "username:") != PAM_SUCCESS)
return false;
pw = getpwnam(username);
if (!pw)
return false;
- elog(LOG_INFO, "Executing %s runlevel for user %s", runlevel, username);
+ /* XDG_RUNTIME_DIR is needed in so many places, that if it's not defined
+ * we're defining it on the standard location. */
+ if (pam_getenv(pamh, "XDG_RUNTIME_DIR") == NULL) {
+ xdg_runtime_dir = create_xdg_runtime_dir(pw);
+ if (!xdg_runtime_dir) {
+ return false;
+ }
- xasprintf(&cmd, "openrc --user %s %s", lock ? "--lock" : "--unlock", runlevel);
- switch (fork()) {
- case 0:
- setgid(pw->pw_gid);
- setuid(pw->pw_uid);
+ xasprintf(&xdg_runtime_dir_env, "XDG_RUNTIME_DIR=%s", xdg_runtime_dir);
+ pam_putenv(pamh, xdg_runtime_dir_env);
+ pam_syslog(pamh, LOG_INFO, "exporting: %s", xdg_runtime_dir_env);
+ free(xdg_runtime_dir);
+ free(xdg_runtime_dir_env);
+ }
+
+ envlist = pam_getenvlist(pamh);
- execle(pw->pw_shell, "-", "-c", cmd, NULL, envlist);
+ xasprintf(&cmd, "openrc --user %s %s", lock ? "--lock" : "--unlock", runlevel);
+ pam_syslog(pamh, LOG_INFO, "Executing %s for user %s", cmd, username);
+ exec_user_cmd(pw, cmd, envlist);
- free(cmd);
- return false;
- break;
- case -1:
- free(cmd);
- return false;
- break;
- }
- wait(NULL);
+ set_user_env(pamh);
for (env = envlist; *env; env++)
free(*env);
@@ -55,18 +199,13 @@ PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, cons
const char *runlevel = argc > 0 ? runlevel = argv[0] : "default";
(void)flags;
- setenv("EINFO_LOG", "openrc-pam", 1);
- elog(LOG_INFO, "Opening openrc session");
+ pam_syslog(pamh, LOG_INFO, "Opening openrc session");
if (exec_openrc(pamh, runlevel, true)) {
- elog(LOG_INFO, "Openrc session opened");
- unsetenv("RC_PAM_STARTING");
- unsetenv("EINFO_LOG");
+ pam_syslog(pamh, LOG_INFO, "Openrc session opened");
return PAM_SUCCESS;
} else {
- elog(LOG_ERR, "Failed to open session");
- unsetenv("RC_PAM_STARTING");
- unsetenv("EINFO_LOG");
+ pam_syslog(pamh, LOG_ERR, "Failed to open session");
return PAM_SESSION_ERR;
}
}
@@ -75,18 +214,13 @@ PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, con
const char *runlevel = argc > 1 ? argv[1] : "none";
(void)flags;
- setenv("EINFO_LOG", "openrc-pam", 1);
- elog(LOG_INFO, "Closing openrc session");
+ pam_syslog(pamh, LOG_INFO, "Closing openrc session");
if (exec_openrc(pamh, runlevel, false)) {
- elog(LOG_INFO, "Openrc session closed");
- unsetenv("RC_PAM_STOPPING");
- unsetenv("EINFO_LOG");
+ pam_syslog(pamh, LOG_INFO, "Openrc session closed");
return PAM_SUCCESS;
} else {
- elog(LOG_ERR, "Failed to close session");
- unsetenv("RC_PAM_STOPPING");
- unsetenv("EINFO_LOG");
+ pam_syslog(pamh, LOG_ERR, "Failed to close session");
return PAM_SESSION_ERR;
}
}