Util-Linux package development
 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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox