The attached patch adds a pam profile for su -l (and su -),
patches su to use that profile if su - is used, and adds a
pseudo-xdg-session pam module.

It isn't possible to guarantee there's a "real" XDG session
for that user for the su - session to join, and you making logind
handle su - and similar sessions properly requires a fair bit of
surgery, so we set up a summy XDG_RUNTIME_DIR instead.
diff -Nru shadow-4.2/debian/changelog shadow-4.2/debian/changelog
--- shadow-4.2/debian/changelog	2015-11-12 14:33:56.000000000 +0000
+++ shadow-4.2/debian/changelog	2016-04-27 19:28:51.000000000 +0100
@@ -1,3 +1,11 @@
+shadow (1:4.2-3.2) stretch; urgency=medium
+
+  * Add su-l pam.d stack and patch su to use it
+  * pam_pseudo_session: module to make minimal dummy XDG environment
+    for su - style sessions
+
+ -- Vivek Das Mohapatra <vi...@collabora.com>  Wed, 27 Apr 2016 12:00:18 +0100
+
 shadow (1:4.2-3.1) unstable; urgency=medium
 
   * Non-maintainer upload.
diff -Nru shadow-4.2/debian/login.install shadow-4.2/debian/login.install
--- shadow-4.2/debian/login.install	2014-11-19 20:41:19.000000000 +0000
+++ shadow-4.2/debian/login.install	2016-04-27 19:29:03.000000000 +0100
@@ -17,9 +17,11 @@
 usr/share/man/man8/faillog.8
 usr/share/man/man8/lastlog.8
 usr/share/man/man8/nologin.8
+usr/share/man/man8/pam_pseudo_session.8
 usr/sbin/nologin
 usr/bin/faillog
 usr/bin/lastlog
 usr/bin/newgrp
 bin/login
 bin/su
+lib/*/security/*.so
diff -Nru shadow-4.2/debian/login.su-l.pam shadow-4.2/debian/login.su-l.pam
--- shadow-4.2/debian/login.su-l.pam	1970-01-01 01:00:00.000000000 +0100
+++ shadow-4.2/debian/login.su-l.pam	2016-04-27 19:36:54.000000000 +0100
@@ -0,0 +1,9 @@
+#%PAM-1.0
+auth 		include		su
+account		include		su
+password 	include		su
+session		optional	pam_keyinit.so force revoke
+session		include		su
+session 	optional 	pam_pseudo_session.so
+
+
diff -Nru shadow-4.2/debian/patches/470_su_pam_xdg_session shadow-4.2/debian/patches/470_su_pam_xdg_session
--- shadow-4.2/debian/patches/470_su_pam_xdg_session	1970-01-01 01:00:00.000000000 +0100
+++ shadow-4.2/debian/patches/470_su_pam_xdg_session	2016-04-27 19:23:20.000000000 +0100
@@ -0,0 +1,335 @@
+--- a/src/su.c
++++ b/src/su.c
+@@ -960,6 +960,7 @@
+ 
+ #ifdef USE_PAM
+ 	int ret;
++	const char *pam_service = NULL;
+ #endif				/* USE_PAM */
+ 
+ 	old_debian_behavior = (getenv("SU_NO_SHELL_ARGS") != NULL);
+@@ -977,7 +978,8 @@
+ 	initenv ();
+ 
+ #ifdef USE_PAM
+-	ret = pam_start ("su", name, &conv, &pamh);
++	pam_service = fakelogin ? "su-l" : "su";
++	ret = pam_start (pam_service, name, &conv, &pamh);
+ 	if (PAM_SUCCESS != ret) {
+ 		SYSLOG ((LOG_ERR, "pam_start: error %d", ret);
+ 		fprintf (stderr,
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -5,4 +5,5 @@
+ AUTOMAKE_OPTIONS = 1.5 dist-bzip2 foreign
+ 
+ SUBDIRS = po man libmisc lib src \
+-	contrib doc etc
++	contrib doc etc pam
++
+--- /dev/null
++++ b/pam/Makefile.am
+@@ -0,0 +1,12 @@
++pamdir = $(libdir)/security
++man_MANS = pam_pseudo_session.8
++pam_pseudo_session_la_SOURCES = pam_pseudo_session.c pam_pseudo_session.sym
++pam_pseudo_session_la_CFLAGS = $(AM_CFLAGS) -shared -fPIC -DPIC
++pam_pseudo_session_la_LDFLAGS = $(AM_LDFLAGS) \
++	-module \
++	-export-dynamic \
++	-avoid-version \
++    -Wl,--version-script=$(top_srcdir)/pam/pam_pseudo_session.sym
++pam_pseudo_session_la_LIBADD = $(LIBPAM)
++
++pam_LTLIBRARIES = pam_pseudo_session.la
+--- /dev/null
++++ b/pam/pam_pseudo_session.sym
+@@ -0,0 +1,6 @@
++{
++global:
++        pam_sm_close_session;
++        pam_sm_open_session;
++local: *;
++};
+--- a/configure.in
++++ b/configure.in
+@@ -9,7 +9,7 @@
+ 
+ AC_GNU_SOURCE
+ 
+-AM_DISABLE_SHARED
++AM_ENABLE_SHARED
+ AM_ENABLE_STATIC
+ 
+ AM_MAINTAINER_MODE
+@@ -656,6 +656,7 @@
+ 	man/zh_TW/Makefile
+ 	libmisc/Makefile
+ 	lib/Makefile
++	pam/Makefile
+ 	src/Makefile
+ 	contrib/Makefile
+ 	etc/Makefile
+--- /dev/null
++++ b/pam/pam_pseudo_session.c
+@@ -0,0 +1,177 @@
++/* pam_pseudo_session module */
++
++/*
++ * Written by Vivek Das Mohapatra <vi...@collabora.com> 2016-04-26
++ * Copyright © Collabora Ltd 2016
++ * provides a pseudo-xdg set of environment variables and directories
++ * to allow basic functionality like systemd --test to work in a
++ * su - style user session. Explicitly does _not_ join the su -
++ * to an existing user XDG environment.
++ */
++
++#include <stdlib.h>
++#include <stdio.h>
++#include <stdarg.h>
++#include <sys/stat.h>
++#include <sys/types.h>
++#include <pwd.h>
++#include <syslog.h>
++#include <signal.h>
++#include <unistd.h>
++#include <errno.h>
++#include <sys/wait.h>
++#include <assert.h>
++
++#define PAM_SM_SESSION
++#define XDG_PSEUDOTMP "/run/user/pseudo-xdg"
++
++
++#include <security/pam_modules.h>
++#include <security/pam_modutil.h>
++#include <security/_pam_macros.h>
++#include <security/pam_ext.h>
++
++static void safeprintf (char *buf, size_t limit, const char *format, ...)
++{
++  int written;
++  va_list ap;
++
++  va_start(ap, format);
++  written = vsnprintf(buf, limit - 1, format, ap);
++  va_end(ap);
++
++  if (written >= limit - 1)
++    buf[limit - 1] = '\0';
++}
++
++static int
++get_user_data (pam_handle_t *handle, struct passwd** user)
++{
++  int result;
++  const char *name = NULL;
++  struct passwd *entry = NULL;
++
++  *user = NULL;
++  result = pam_get_user(handle, &name, NULL);
++
++  if (result != PAM_SUCCESS)
++    {
++      pam_syslog(handle, LOG_ERR, "Failed to get user name.");
++      return result;
++    }
++
++  if (name == NULL || *name == '\0')
++    {
++      pam_syslog(handle, LOG_ERR, "User name is empty.");
++      return PAM_USER_UNKNOWN;
++    }
++
++  entry = pam_modutil_getpwnam(handle, name);
++
++  if(!entry)
++    {
++      pam_syslog(handle, LOG_ERR, "Failed to get user data.");
++      return PAM_USER_UNKNOWN;
++    }
++
++  *user = entry;
++
++  return PAM_SUCCESS;
++}
++
++static int
++env_setup (pam_handle_t *handle, const char *path)
++{
++  char env_key[255];
++
++  safeprintf(env_key, sizeof(env_key), "XDG_RUNTIME_DIR=%s", path);
++
++  return pam_putenv(handle, env_key);
++}
++
++static int
++pseudo_session_setup (pam_handle_t *handle,
++                      struct passwd *user,
++                      char *path,
++                      int pathmax)
++{
++  int result;
++
++  safeprintf(path, pathmax, XDG_PSEUDOTMP "/%d", user->pw_uid);
++
++  result = mkdir(XDG_PSEUDOTMP, 0755);
++  if (result != 0 && errno != EEXIST)
++    return PAM_SYSTEM_ERR;
++
++  result = mkdir(path, 0700);
++  if (result == 0)
++    {
++      result = chown(path, user->pw_uid, user->pw_gid);
++      if (result == 0)
++        return PAM_SUCCESS;
++      else
++        return PAM_SYSTEM_ERR;
++    }
++
++  // Ok if it already exists, if it has the right perms:
++  // or if it's owned by root:root which is a potential race
++  // if > 1 su - style sessions happen close together:
++  if(errno == EEXIST)
++    {
++      struct stat dir = { 0 };
++      int r;
++      r = stat(path, &dir);
++      if (r == 0)
++        {
++          if((dir.st_uid == user->pw_uid) &&
++             (dir.st_gid == user->pw_gid))
++            {
++              return PAM_SUCCESS;
++            }
++          else if ((dir.st_uid == 0) && (dir.st_gid == 0))
++            {
++              result = chown(path, user->pw_uid, user->pw_gid);
++              if (result == 0)
++                  return PAM_SUCCESS;
++            }
++        }
++    }
++
++  if (errno == EPERM)
++    return PAM_PERM_DENIED;
++
++  return PAM_SYSTEM_ERR;
++}
++
++PAM_EXTERN int
++pam_sm_open_session (pam_handle_t *handle, int flags,
++                     int argc, const char **argv)
++{
++  int result;
++  char path[255];
++  struct passwd *user = NULL;
++
++  assert(handle);
++
++  result = get_user_data(handle, &user);
++
++  if (result != PAM_SUCCESS)
++    return result;
++
++  result = pseudo_session_setup(handle, user, &path[0], sizeof(path));
++
++  if (result != PAM_SUCCESS)
++    return result;
++
++  return env_setup(handle, &path[0]);
++}
++
++PAM_EXTERN int
++pam_sm_close_session (pam_handle_t *pamh, int flags,
++                      int argc, const char **argv)
++{
++  // In theory we could to some sort of lock/count style checking
++  // here and delete the pseudo XDG directory when it's gone but
++  // there's not much point:
++  return PAM_SUCCESS;
++}
+--- /dev/null
++++ b/pam/pam_pseudo_session.8
+@@ -0,0 +1,80 @@
++'\" t
++.\"     Title: pam_pseudo_session
++.\"    Author: [see the "AUTHOR" section]
++.\"      Date: 27/04/2016
++.\"  Language: English
++.\"
++.TH "PAM_PSEUDO_SESSION" "8" "06/04/2016" "pam_pseudo_session" "pam_pseudo_session"
++.\" -----------------------------------------------------------------
++.\" * Define some portability stuff
++.\" -----------------------------------------------------------------
++.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
++.\" http://bugs.debian.org/507673
++.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
++.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
++.ie \n(.g .ds Aq \(aq
++.el       .ds Aq '
++.\" -----------------------------------------------------------------
++.\" * set default formatting
++.\" -----------------------------------------------------------------
++.\" disable hyphenation
++.nh
++.\" disable justification (adjust text to left margin only)
++.ad l
++.\" -----------------------------------------------------------------
++.\" * MAIN CONTENT STARTS HERE *
++.\" -----------------------------------------------------------------
++.SH "NAME"
++pam_pseudo_session \- create dummy XDG environments for su - style sessions
++.SH "SYNOPSIS"
++.HP \w'\fBpam_pseudo_session\&.so\fR\ 'u
++\fBsession optional pam_pseudo_session\&.so\fR
++.SH "DESCRIPTION"
++.PP
++The pam_pseudo_session module sets up a minimal dummy XDG environment and
++runtime directory to allow commands such as systemd --test to run\&. All
++pseudo-login sessions which use this module share the same dummy XDG
++environment but this is not shared with (or the same as) real logins' XDG
++environments\&.
++.SH "MODULE TYPES PROVIDED"
++.PP
++Only the
++\fBsession\fR
++type is provided\&.
++.SH "RETURN VALUES"
++.PP
++PAM_SUCCESS
++.RS 4
++The XDG runtime directory was created and the environment populated\&.
++.RE
++.PP
++PAM_PERM_DENIED
++.RS 4
++The module was unable to create the dummy XDG directory (should never happen
++in normal PAM operation)\&.
++.RE
++.PP
++PAM_SYSTEM_ERR
++.RS 4
++A non-permission related error occurred - you should check the contents
++of /run/user\&.
++.RE
++.PP
++PAM_USER_UNKNOWN
++.RS 4
++The user is not known to the system\&.
++.RE
++.SH "FILES"
++.PP
++/run/user/pseudo-xdg/*
++.RS 4
++The directory where pam_pseudo_session will try to create the dummy XDG tree\&.
++.RE
++.SH "SEE ALSO"
++.PP
++
++\fBpam.d\fR(5),
++\fBpam\fR(7)\&.
++.SH "AUTHOR"
++.PP
++pam_pseudo_session was written by Vivek Das Mohapatra <vivek@collabora\&.com>\&.
diff -Nru shadow-4.2/debian/patches/series shadow-4.2/debian/patches/series
--- shadow-4.2/debian/patches/series	2015-11-12 14:24:49.000000000 +0000
+++ shadow-4.2/debian/patches/series	2016-04-22 19:42:26.000000000 +0100
@@ -35,3 +35,4 @@
 1000_configure_userns
 1010_vietnamese_translation
 1020_fix_user_busy_errors
+470_su_pam_xdg_session
diff -Nru shadow-4.2/debian/rules shadow-4.2/debian/rules
--- shadow-4.2/debian/rules	2014-11-19 20:49:09.000000000 +0000
+++ shadow-4.2/debian/rules	2016-04-27 19:29:26.000000000 +0100
@@ -21,7 +21,7 @@
 include /usr/share/cdbs/1/class/autotools.mk
 
 # Adds extra options when calling the configure script:
-DEB_CONFIGURE_EXTRA_FLAGS := --disable-shared --without-libcrack --mandir=/usr/share/man --with-libpam --enable-shadowgrp --enable-man --disable-account-tools-setuid --with-group-name-max-length=32 --without-acl --without-attr --without-tcb
+DEB_CONFIGURE_EXTRA_FLAGS := --without-libcrack --libdir=/lib/$(DEB_HOST_MULTIARCH) --mandir=/usr/share/man --with-libpam --enable-shadowgrp --enable-man --disable-account-tools-setuid --with-group-name-max-length=32 --without-acl --without-attr --without-tcb
 ifneq ($(DEB_BUILD_GNU_TYPE),$(DEB_HOST_GNU_TYPE))
   DEB_CONFIGURE_EXTRA_FLAGS += --host=$(DEB_HOST_GNU_TYPE)
 endif
@@ -37,6 +37,7 @@
 endif
 	dh_installpam -p login
 	dh_installpam -p login --name=su
+	dh_installpam -p login --name=su-l
 	install -c -m 444 debian/login.defs debian/login/etc/login.defs
 	install -c -m 444 debian/securetty.$(DEB_HOST_ARCH_OS) debian/login/etc/securetty
 	dh_lintian -p login

Reply via email to