All of lore.kernel.org
 help / color / mirror / Atom feed
From: Ray Strode <halfline@gmail.com>
To: util-linux@vger.kernel.org
Cc: Karel Zak <kzak@redhat.com>,
	Lennart Poettering <lennart@poettering.net>,
	Ray Strode <rstrode@redhat.com>
Subject: [PATCH] login-utils: import environment from user manager on systemd systems
Date: Tue, 25 Oct 2016 16:34:21 -0400	[thread overview]
Message-ID: <1477427661-19788-1-git-send-email-halfline@gmail.com> (raw)

From: Ray Strode <rstrode@redhat.com>

If the user is using a systemd system, then its useful to grab the
environment from the systemd user manager process.

This allows administrators to initialize the environment of the sessions
via systemd configuration.

Reference: https://github.com/systemd/systemd/pull/3904
Signed-off-by: Ray Strode <rstrode@redhat.com>
---
 login-utils/Makemodule.am |  5 +++
 login-utils/login.c       | 84 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 89 insertions(+)

diff --git a/login-utils/Makemodule.am b/login-utils/Makemodule.am
index be07ace..d65bfc1 100644
--- a/login-utils/Makemodule.am
+++ b/login-utils/Makemodule.am
@@ -16,70 +16,75 @@ endif
 if BUILD_SULOGIN
 sbin_PROGRAMS += sulogin
 dist_man_MANS += login-utils/sulogin.8
 sulogin_SOURCES = \
 	login-utils/sulogin.c \
 	login-utils/sulogin-consoles.c \
 	login-utils/sulogin-consoles.h
 sulogin_LDADD = $(LDADD) libcommon.la
 
 if HAVE_LIBCRYPT
 sulogin_LDADD += -lcrypt
 endif
 if HAVE_SELINUX
 sulogin_LDADD += -lselinux
 endif
 
 check_PROGRAMS += test_consoles
 test_consoles_SOURCES = login-utils/sulogin-consoles.c
 test_consoles_CFLAGS = $(AM_CFLAGS) -DTEST_PROGRAM
 test_consoles_LDADD = $(LDADD) libcommon.la
 endif # BUILD_SULOGIN
 
 
 if BUILD_LOGIN
 bin_PROGRAMS += login
 dist_man_MANS += login-utils/login.1
 login_SOURCES = \
 	login-utils/login.c \
 	login-utils/logindefs.c \
 	login-utils/logindefs.h
+login_CFLAGS =
 login_LDADD = $(LDADD) libcommon.la -lpam
 if HAVE_LINUXPAM
 login_LDADD += -lpam_misc
 endif
 if HAVE_AUDIT
 login_LDADD += -laudit
 endif
 if HAVE_SELINUX
 login_LDADD += -lselinux
 endif
+if HAVE_SYSTEMD
+login_LDADD += $(SYSTEMD_LIBS)
+login_CFLAGS += $(SYSTEMD_CFLAGS)
+endif
 endif # BUILD_LOGIN
 
 
 if BUILD_NOLOGIN
 sbin_PROGRAMS += nologin
 dist_man_MANS += login-utils/nologin.8
 nologin_SOURCES = login-utils/nologin.c
 endif
 
 
 if BUILD_UTMPDUMP
 usrbin_exec_PROGRAMS += utmpdump
 dist_man_MANS += login-utils/utmpdump.1
 utmpdump_SOURCES = login-utils/utmpdump.c
 utmpdump_LDADD = $(LDADD) libcommon.la
 endif
 
 
 if BUILD_CHFN_CHSH
 usrbin_exec_PROGRAMS += chfn chsh
 dist_man_MANS += \
 	login-utils/chfn.1 \
 	login-utils/chsh.1
 
 chfn_chsh_sources = \
 	login-utils/ch-common.h \
 	login-utils/ch-common.c
 chfn_chsh_cflags = $(SUID_CFLAGS) $(AM_CFLAGS)
 chfn_chsh_ldflags = $(SUID_LDFLAGS) $(AM_LDFLAGS)
 chfn_chsh_ldadd = libcommon.la
diff --git a/login-utils/login.c b/login-utils/login.c
index 2350fc3..9a9a86e 100644
--- a/login-utils/login.c
+++ b/login-utils/login.c
@@ -37,69 +37,74 @@
 #include <sys/time.h>
 #include <sys/resource.h>
 #include <sys/file.h>
 #include <termios.h>
 #include <string.h>
 #include <sys/ioctl.h>
 #include <sys/wait.h>
 #include <signal.h>
 #include <errno.h>
 #include <grp.h>
 #include <pwd.h>
 #include <utmp.h>
 #include <stdlib.h>
 #include <sys/syslog.h>
 #ifdef HAVE_LINUX_MAJOR_H
 # include <linux/major.h>
 #endif
 #include <netdb.h>
 #include <security/pam_appl.h>
 #ifdef HAVE_SECURITY_PAM_MISC_H
 # include <security/pam_misc.h>
 #elif defined(HAVE_SECURITY_OPENPAM_H)
 # include <security/openpam.h>
 #endif
 #include <sys/sendfile.h>
 
 #ifdef HAVE_LIBAUDIT
 # include <libaudit.h>
 #endif
 
+#ifdef HAVE_LIBSYSTEMD
+# include <systemd/sd-bus.h>
+#endif
+
 #include "c.h"
 #include "setproctitle.h"
 #include "pathnames.h"
 #include "strutils.h"
 #include "nls.h"
 #include "env.h"
 #include "xalloc.h"
 #include "all-io.h"
 #include "fileutils.h"
+#include "strv.h"
 #include "ttyutils.h"
 
 #include "logindefs.h"
 
 #define is_pam_failure(_rc)	((_rc) != PAM_SUCCESS)
 
 #define LOGIN_MAX_TRIES        3
 #define LOGIN_EXIT_TIMEOUT     5
 #define LOGIN_TIMEOUT          60
 
 #ifdef USE_TTY_GROUP
 # define TTY_MODE 0620
 #else
 # define TTY_MODE 0600
 #endif
 
 #define	TTYGRPNAME	"tty"	/* name of group to own ttys */
 #define VCS_PATH_MAX	64
 
 /*
  * Login control struct
  */
 struct login_context {
 	const char	*tty_path;	/* ttyname() return value */
 	const char	*tty_name;	/* tty_path without /dev prefix */
 	const char	*tty_number;	/* end of the tty_path */
 	mode_t		tty_mode;	/* chmod() mode */
 
 	char		*username;	/* from command line or PAM */
 
@@ -994,108 +999,187 @@ static void fork_session(struct login_context *cxt)
 
 	/*
 	 * child
 	 */
 	sigaction(SIGHUP, &oldsa_hup, NULL);		/* restore old state */
 	sigaction(SIGTERM, &oldsa_term, NULL);
 	if (got_sig)
 		exit(EXIT_FAILURE);
 
 	/*
 	 * Problem: if the user's shell is a shell like ash that doesn't do
 	 * setsid() or setpgrp(), then a ctrl-\, sending SIGQUIT to every
 	 * process in the pgrp, will kill us.
 	 */
 
 	/* start new session */
 	setsid();
 
 	/* make sure we have a controlling tty */
 	open_tty(cxt->tty_path);
 	openlog("login", LOG_ODELAY, LOG_AUTHPRIV);	/* reopen */
 
 	/*
 	 * TIOCSCTTY: steal tty from other process group.
 	 */
 	if (ioctl(0, TIOCSCTTY, 1))
 		syslog(LOG_ERR, _("TIOCSCTTY failed: %m"));
 	signal(SIGINT, SIG_DFL);
 }
 
+#ifdef HAVE_LIBSYSTEMD
+/*
+ * Import environment from systemd user manager
+ */
+static void import_systemd_user_environ(struct login_context *cxt)
+{
+	struct passwd *pwd = cxt->pwd;
+	int rc;
+	sd_bus *bus = NULL;
+	sd_bus_error error = SD_BUS_ERROR_NULL;
+	sd_bus_message *reply = NULL;
+	char *env_entry = NULL;
+	uid_t old_euid = geteuid();
+	int euid_changed = 0;
+
+	if (pwd->pw_uid != 0) {
+		assert(old_euid == getuid());
+
+		if (seteuid(pwd->pw_uid) < 0) {
+			syslog(LOG_ERR, _("seteuid failed: %m"));
+			return;
+		}
+		euid_changed = 1;
+	}
+
+	rc = sd_bus_default_user(&bus);
+
+	if (rc < 0) {
+		syslog(LOG_NOTICE, _("user bus unavailable: %m"));
+		return;
+	}
+
+	rc = sd_bus_get_property(bus,
+				 "org.freedesktop.systemd1",
+				 "/org/freedesktop/systemd1",
+				 "org.freedesktop.systemd1.Manager",
+				 "Environment",
+				 &error,
+				 &reply,
+				 "as");
+
+	if (rc < 0) {
+		syslog(LOG_NOTICE, _("user bus unable to return environment: %m"));
+		goto out;
+	}
+
+	rc = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "s");
+
+	if (rc < 0)
+		goto out;
+
+	while ((rc = sd_bus_message_read_basic(reply, SD_BUS_TYPE_STRING, &env_entry)) > 0) {
+		char **env_tuple;
+
+		env_tuple = strv_split(env_entry, "=");
+
+		if (env_tuple && env_tuple[0] && env_tuple[1])
+			xsetenv(env_tuple[0], env_tuple[1], 0);
+
+		strv_free (env_tuple);
+	}
+	sd_bus_message_exit_container(reply);
+
+out:
+	sd_bus_error_free(&error);
+	reply = sd_bus_message_unref(reply);
+	bus = sd_bus_unref(bus);
+
+	if (euid_changed && seteuid(old_euid) < 0) {
+		syslog(LOG_ALERT, _("seteuid() failed"));
+		exit(EXIT_FAILURE);
+	}
+}
+#endif
+
 /*
  * Initialize $TERM, $HOME, ...
  */
 static void init_environ(struct login_context *cxt)
 {
 	struct passwd *pwd = cxt->pwd;
 	char *termenv, **env;
 	char tmp[PATH_MAX];
 	int len, i;
 
 	termenv = getenv("TERM");
 	if (termenv)
 		termenv = xstrdup(termenv);
 
 	/* destroy environment unless user has requested preservation (-p) */
 	if (!cxt->keep_env) {
 		environ = xmalloc(sizeof(char *));
 		memset(environ, 0, sizeof(char *));
 	}
 
 	xsetenv("HOME", pwd->pw_dir, 0);	/* legal to override */
 	xsetenv("USER", pwd->pw_name, 1);
 	xsetenv("SHELL", pwd->pw_shell, 1);
 	xsetenv("TERM", termenv ? termenv : "dumb", 1);
 	free(termenv);
 
 	if (pwd->pw_uid) {
 		if (logindefs_setenv("PATH", "ENV_PATH", _PATH_DEFPATH) != 0)
 			err(EXIT_FAILURE, _("failed to set the %s environment variable"), "PATH");
 
 	} else if (logindefs_setenv("PATH", "ENV_ROOTPATH", NULL) != 0 &&
 		   logindefs_setenv("PATH", "ENV_SUPATH", _PATH_DEFPATH_ROOT) != 0) {
 			err(EXIT_FAILURE, _("failed to set the %s environment variable"), "PATH");
 	}
 
 	/* mailx will give a funny error msg if you forget this one */
 	len = snprintf(tmp, sizeof(tmp), "%s/%s", _PATH_MAILDIR, pwd->pw_name);
 	if (len > 0 && (size_t) len < sizeof(tmp))
 		xsetenv("MAIL", tmp, 0);
 
 	/* LOGNAME is not documented in login(1) but HP-UX 6.5 does it. We'll
 	 * not allow modifying it.
 	 */
 	xsetenv("LOGNAME", pwd->pw_name, 1);
 
 	env = pam_getenvlist(cxt->pamh);
 	for (i = 0; env && env[i]; i++)
 		putenv(env[i]);
+
+#ifdef HAVE_LIBSYSTEMD
+	import_systemd_user_environ(cxt);
+#endif
 }
 
 /*
  * This is called for the -h option, initializes cxt->{hostname,hostaddress}.
  */
 static void init_remote_info(struct login_context *cxt, char *remotehost)
 {
 	const char *domain;
 	char *p;
 	struct addrinfo hints, *info = NULL;
 
 	cxt->remote = 1;
 
 	get_thishost(cxt, &domain);
 
 	if (domain && (p = strchr(remotehost, '.')) &&
 	    strcasecmp(p + 1, domain) == 0)
 		*p = '\0';
 
 	cxt->hostname = xstrdup(remotehost);
 
 	memset(&hints, 0, sizeof(hints));
 	hints.ai_flags = AI_ADDRCONFIG;
 	cxt->hostaddress[0] = 0;
 
 	if (getaddrinfo(cxt->hostname, NULL, &hints, &info) == 0 && info) {
 		if (info->ai_family == AF_INET) {
 			struct sockaddr_in *sa =
 				    (struct sockaddr_in *) info->ai_addr;
 
-- 
2.7.4


             reply	other threads:[~2016-10-25 20:34 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-10-25 20:34 Ray Strode [this message]
2016-10-25 21:06 ` [PATCH] login-utils: import environment from user manager on systemd systems Linda Walsh
2016-10-26 19:38   ` Ray Strode
2016-10-27 21:53     ` L. Walsh
2016-10-28 15:06       ` Ray Strode
2016-10-28  3:02 ` L. Walsh
2016-10-28 15:14   ` Ray Strode
2016-12-07 19:45 ` Ray Strode
2016-12-07 20:04   ` Ruediger Meier
2016-12-08 11:24     ` Karel Zak
2016-12-08 15:00       ` Ray Strode
2016-12-08 18:39         ` Fwd: " Ray Strode

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=1477427661-19788-1-git-send-email-halfline@gmail.com \
    --to=halfline@gmail.com \
    --cc=kzak@redhat.com \
    --cc=lennart@poettering.net \
    --cc=rstrode@redhat.com \
    --cc=util-linux@vger.kernel.org \
    /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.