summaryrefslogtreecommitdiffstats
path: root/core/modules/pam-slx-plug
diff options
context:
space:
mode:
authorSimon Rettberg2018-03-08 17:14:42 +0100
committerSimon Rettberg2018-03-08 17:14:42 +0100
commit53f8946416b456476d130334490790c3979d2f82 (patch)
tree6d2483d84477e109de568455ab96aa2cf807b253 /core/modules/pam-slx-plug
parent[hardware-stats] check if '--no-legend' is needed (diff)
downloadmltk-53f8946416b456476d130334490790c3979d2f82.tar.gz
mltk-53f8946416b456476d130334490790c3979d2f82.tar.xz
mltk-53f8946416b456476d130334490790c3979d2f82.zip
[pam-slx-plug] Starting to separate some of the pam/nsswitch logic out of sssd/pam
Preparation for our own ldap/ad login handling, sssd will only be used for nsswitch related stuff and fallback.
Diffstat (limited to 'core/modules/pam-slx-plug')
l---------core/modules/pam-slx-plug/data/etc/systemd/system/multi-user.target.wants/slx-update-pam-nss.service1
-rw-r--r--core/modules/pam-slx-plug/data/etc/systemd/system/slx-update-pam-nss.service8
-rw-r--r--core/modules/pam-slx-plug/data/opt/openslx/pam/auth-source.d/99-slx-ldap183
-rw-r--r--core/modules/pam-slx-plug/data/opt/openslx/pam/common/homedir-passwd27
-rwxr-xr-xcore/modules/pam-slx-plug/data/opt/openslx/pam/exec_account50
-rwxr-xr-xcore/modules/pam-slx-plug/data/opt/openslx/pam/exec_auth204
-rwxr-xr-xcore/modules/pam-slx-plug/data/opt/openslx/pam/exec_session8
-rwxr-xr-xcore/modules/pam-slx-plug/data/opt/openslx/pam/mkhome13
-rw-r--r--core/modules/pam-slx-plug/data/opt/openslx/pam/mount.d/99-fallback97
-rwxr-xr-xcore/modules/pam-slx-plug/data/opt/openslx/pam/systemd/create-pam-config121
-rw-r--r--core/modules/pam-slx-plug/module.build12
-rw-r--r--core/modules/pam-slx-plug/module.conf3
12 files changed, 727 insertions, 0 deletions
diff --git a/core/modules/pam-slx-plug/data/etc/systemd/system/multi-user.target.wants/slx-update-pam-nss.service b/core/modules/pam-slx-plug/data/etc/systemd/system/multi-user.target.wants/slx-update-pam-nss.service
new file mode 120000
index 00000000..450c4948
--- /dev/null
+++ b/core/modules/pam-slx-plug/data/etc/systemd/system/multi-user.target.wants/slx-update-pam-nss.service
@@ -0,0 +1 @@
+../slx-update-pam-nss.service \ No newline at end of file
diff --git a/core/modules/pam-slx-plug/data/etc/systemd/system/slx-update-pam-nss.service b/core/modules/pam-slx-plug/data/etc/systemd/system/slx-update-pam-nss.service
new file mode 100644
index 00000000..fa7a8bd0
--- /dev/null
+++ b/core/modules/pam-slx-plug/data/etc/systemd/system/slx-update-pam-nss.service
@@ -0,0 +1,8 @@
+[Unit]
+Description=Create fresh pam config and nsswitch.conf
+Before=graphical.target display-manager.target
+
+[Service]
+Type=oneshot
+ExecStart=/opt/openslx/pam/systemd/create-pam-config
+
diff --git a/core/modules/pam-slx-plug/data/opt/openslx/pam/auth-source.d/99-slx-ldap b/core/modules/pam-slx-plug/data/opt/openslx/pam/auth-source.d/99-slx-ldap
new file mode 100644
index 00000000..0b5ca0f6
--- /dev/null
+++ b/core/modules/pam-slx-plug/data/opt/openslx/pam/auth-source.d/99-slx-ldap
@@ -0,0 +1,183 @@
+#!/bin/ash -- sourced
+
+# Run 'account' or 'auth' PAM mode
+# account:
+# See if user is allowed to login (=exists in our case)
+# auth:
+# Try logging in via ldapsearch
+
+# parameters supplied via
+# /opt/openslx/pam/slx-ldap.d/*
+
+WAIT=
+
+unset_ldap_vars() {
+ local vn
+ for vn in $(set | grep -Eo '^(SHARE|LDAP)_[^=]+'); do
+ unset "$vn"
+ done
+ unset USER_UID USER_GID USER_GROUP REAL_ACCOUNT NETWORK_HOME HOME_MOUNT_OPTS
+}
+
+# ldapsearch can return fields either as
+# field: value
+# or
+# field:: base64encode(value)
+# -- handle both cases and return the value
+# extract_field <fieldname> <resultfile> [another resultfile [...]]
+extract_field() {
+ local l len file
+ local field=$1
+ shift
+ while [ $# -gt 0 ]; do
+ file=$1
+ shift
+ l=$( grep -m 1 -i "^${field}: " "$file" )
+ if [ -n "$l" ]; then
+ len=$(( ${#field} + 2 ))
+ echo "${l:$len}"
+ return
+ fi
+ l=$( grep -m 1 -i "^${field}:: " "$file" )
+ if [ -n "$l" ]; then
+ len=$(( ${#field} + 3 ))
+ echo "${l:$len}" | base64 -d
+ return
+ fi
+ done
+}
+
+run_auth() {
+ local BINDDN SEARCH_ANON SEARCH_USER PW RET
+ if [ -n "$LDAP_CACERT" ]; then
+ export LDAPTLS_CACERT="$LDAP_CACERT"
+ else
+ unset LDAPTLS_CACERT
+ fi
+ # See if user exists
+ SEARCH_ANON=$(mktemp)
+ TEMPFILES_LDAP="$TEMPFILES_LDAP $SEARCH_ANON"
+ for retries in 0 1 1 2 3 END; do
+ ldapsearch -x -LLL -l 3 -o nettimeout=3 -o ldif-wrap=no \
+ -H "$LDAP_URI" -b "$LDAP_BASE" uid="${PAM_USER}" \
+ dn distinguishedName homeMount realAccount uid uidNumber gidNumber ${LDAP_ATTR_MOUNT_OPTS} &> "${SEARCH_ANON}"
+ RET=$?
+ case "$RET" in
+ 0) break # OK
+ ;;
+ 51|52) sleep 3 # busy, let's try again
+ ;;
+ 255) # Network error
+ # grep the output to see whether the bind succeeded, it should have returned anything but -1
+ < "$SEARCH_ANON" grep -q '^ldap_bind:.*(-1)$' || break # If == -1, continue
+ ;;
+ *)
+ slxlog "pam-slxldap-ldapsearch" "Initial ldapsearch for $PAM_USER returned $RET" "$SEARCH_ANON"
+ WAIT=1
+ break
+ ;;
+ esac
+ sleep "$retries" &> /dev/null # lazy END handling
+ done
+ BINDDN=$(extract_field "dn" "$SEARCH_ANON")
+ [ -z "$BINDDN" ] && BINDDN=$(extract_field "distinguishedName" "$SEARCH_ANON")
+ [ -z "$BINDDN" ] && return 1
+ # User exists
+ if [ "$PAM_TYPE" = "account" ]; then
+ # 'account' checks just if the user is allowed to log in, bail out
+ USER_UID=$(extract_field "uidNumber" "$SEARCH_ANON")
+ USER_GID=$(extract_field "gidNumber" "$SEARCH_ANON")
+ USER_HOME=$(extract_field "homeDirectory" "$SEARCH_ANON")
+ return 0
+ fi
+ SEARCH_USER=$(mktemp)
+ TEMPFILES_LDAP="$TEMPFILES_LDAP $SEARCH_USER"
+ PW="/run/pw.${RANDOM}.${RANDOM}.${PAM_USER}.${RANDOM}.$$"
+ for retries in 0 1 1 0; do
+ if ! mkfifo -m 0600 "${PW}"; then
+ slxlog "pam-slxldap-fifo" "Could not create FIFO at ${PW}"
+ return 1
+ fi
+ ( # Blocking write to FIFO, fork into bg. Make sure to use a shell that understands echo -n (busybox ash does)
+ echo -n "${USER_PASSWORD}" > "${PW}"
+ ) &
+ # unquoted LDAP_ATTR_*
+ ldapsearch -x -LLL -l 5 -o nettimeout=5 -o ldif-wrap=no \
+ -H "$LDAP_URI" -b "$LDAP_BASE" uid="${PAM_USER}" -y "${PW}" -D "$BINDDN" uid="${PAM_USER}" \
+ homeMount realAccount uid uidNumber gidNumber ${LDAP_ATTR_MOUNT_OPTS} &> "${SEARCH_USER}"
+ RET=$?
+ rm -f -- "${PW}"
+ case "$RET" in
+ 0|49|50) break # Either success, or access denied / invalid user/pass
+ ;;
+ 51|52) sleep 3 # busy, let's try again
+ ;;
+ 255) # Network error
+ # grep the output to see whether the bind succeeded, it should have returned anything but -1
+ < "$SEARCH_USER" grep -q '^ldap_bind:.*(-1)$' || break # If == -1, continue
+ ;;
+ *)
+ slxlog "pam-slxldap-ldapsearch" "User-bind for $PAM_USER returned $RET" "$SEARCH_USER"
+ WAIT=1
+ break
+ ;;
+ esac
+ sleep "$retries"
+ done
+ [ "$RET" = 0 ] || return 1
+ USER_UID=$(extract_field "uidNumber" "$SEARCH_USER" "$SEARCH_ANON")
+ if [ -z "$USER_UID" ]; then
+ slxlog --echo "pam-slxldap-nouid" "User $PAM_USER found in ldap, but has no uidNumber"
+ return 1
+ fi
+ USER_GID=$(extract_field "gidNumber" "$SEARCH_USER" "$SEARCH_ANON")
+ if [ -z "$USER_GID" ]; then
+ slxlog --echo "pam-slxldap-nogid" "User $PAM_USER / $USER_UID found in ldap, but has no gidNumber"
+ unset USER_UID
+ return 1
+ fi
+ REAL_ACCOUNT=$(extract_field "realAccount" "$SEARCH_USER" "$SEARCH_ANON")
+ [ -z "$REAL_ACCOUNT" ] && REAL_ACCOUNT=$(extract_field "uid" "$SEARCH_USER" "$SEARCH_ANON")
+ NETWORK_HOME=$(extract_field "homeMount" "$SEARCH_USER" "$SEARCH_ANON")
+ USER_HOME=$(extract_field "homeDirectory" "$SEARCH_USER" "$SEARCH_ANON")
+ HOME_MOUNT_OPTS="${SHARE_HOME_MOUNT_OPTS}"
+ if [ -n "$LDAP_ATTR_HOME_MOUNT_OPTS" ]; then
+ RET=$(extract_field "${LDAP_ATTR_HOME_MOUNT_OPTS}" "$SEARCH_USER" "$SEARCH_ANON")
+ [ -n "$RET" ] && HOME_MOUNT_OPTS="$RET"
+ fi
+ # Group - might need another round trip to LDAP
+ USER_GROUP=$( awk -F: '{ if ($3 == '"$USER_GID"') { print $1; exit } }' /etc/group )
+ if [ -z "$USER_GROUP" ]; then
+ ldapsearch -x -LLL -l 2 -o nettimeout=2 -o ldif-wrap=no \
+ -H "$LDAP_URI" -b "$LDAP_BASE" "(&(objectClass=posixGroup)(gidNumber=${USER_GID}))" \
+ cn &> "${SEARCH_ANON}"
+ USER_GROUP=$(extract_field "cn" "$SEARCH_ANON")
+ fi
+ return 0
+}
+
+TEMPFILES_LDAP=
+
+for s_file in /opt/openslx/pam/slx-ldap.d/*; do
+ unset_ldap_vars
+ [ -f "$s_file" ] || continue
+ . "$s_file"
+ if [ -z "$LDAP_URI" ] || [ -z "$LDAP_BASE" ]; then
+ slxlog --echo "pam-slxldap-empty" "LDAP config '$s_file' is missing URI or BASE"
+ continue
+ fi
+ run_auth && break
+done
+
+if [ -z "$USER_UID" ]; then
+ unset_ldap_vars
+fi
+
+if [ -n "$TEMPFILES_LDAP" ]; then
+ rm -f -- $TEMPFILES_LDAP
+fi
+
+[ -n "$WAIT" ] && sleep 1
+
+true
+
diff --git a/core/modules/pam-slx-plug/data/opt/openslx/pam/common/homedir-passwd b/core/modules/pam-slx-plug/data/opt/openslx/pam/common/homedir-passwd
new file mode 100644
index 00000000..20b7991d
--- /dev/null
+++ b/core/modules/pam-slx-plug/data/opt/openslx/pam/common/homedir-passwd
@@ -0,0 +1,27 @@
+#!/bin/ash -- sourced
+
+# Required vars from context:
+# USER_UID
+# USER_GID
+# USER_NAME
+
+# 1) Sanitize the user's home directory, stored and updated in USER_HOME
+
+# 2) Write entry to /etc/passwd if it doesn't exist
+
+# Fixup local home path
+if [ -z "$USER_HOME" ] || [ "${USER_HOME:0:1}" = '\' ] || [ "${USER_HOME:1:1}" = ':' ]; then
+ USER_HOME="/home/_temp-home/$USER_NAME"
+else
+ # Make sure it's absolute, replace spaces, '\' and ':' by '_'
+ USER_HOME=$(echo "$USER_HOME" | sed -r 's,^(/*)(.*)$,/\2,;s/(:|\s|\\)+/_/g')
+fi
+readonly USER_HOME
+
+# Add/replace passwd entry if it doesn't exist yet
+PASSWD="${USER_NAME}:x:${USER_UID}:${USER_GID}:${USER_NAME}:${USER_HOME}:/bin/bash"
+readonly PASSWD
+if ! grep -Fxq -- "${PASSWD}" /etc/passwd; then
+ sed -i "/^${USER_NAME}:/d" /etc/passwd
+ echo "${PASSWD}" >> /etc/passwd
+fi
diff --git a/core/modules/pam-slx-plug/data/opt/openslx/pam/exec_account b/core/modules/pam-slx-plug/data/opt/openslx/pam/exec_account
new file mode 100755
index 00000000..f481d302
--- /dev/null
+++ b/core/modules/pam-slx-plug/data/opt/openslx/pam/exec_account
@@ -0,0 +1,50 @@
+#!/bin/ash
+
+[ "$PAM_TYPE" = "account" ] || exit 1
+
+USER_NAME="$PAM_USER"
+readonly PAM_USER USER_NAME
+
+export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/openslx/sbin:/opt/openslx/bin"
+
+# check for invalid char ':'
+if echo "$PAM_USER" | grep -Fq ':'; then
+ slxlog --echo "pam-format-username" "Username '$PAM_USER' contains disallowed character ':', denying access"
+ exit 1
+fi
+
+# check if the script runs as root
+[ "x$(whoami)" = "xroot" ] || exit 1
+
+# passwd but no shadow hints at a user we added - allow
+grep -q "^${PAM_USER}:" "/etc/shadow" && exit 1
+grep -q "^${PAM_USER}:" "/etc/passwd" && exit 0
+
+# Have neither, run hooks
+for auth_file in /opt/openslx/pam/auth-source.d/*; do
+ USER_UID=
+ USER_GID=
+ [ -f "$auth_file" ] || continue
+ . "$auth_file"
+ [ -n "$USER_UID" ] || continue
+ break
+done
+readonly USER_UID USER_GID
+
+[ -n "$USER_UID" ] || exit 1
+# Got ok from hook - cache in passwd if we got a USER_GID
+[ -n "$USER_GID" ] || exit 0 # OK without caching, no GID
+
+if ! echo "$USER_UID" | grep -Exq '[0-9]+'; then
+ slxlog --echo "pam-format-uid" "'$PAM_USER' has invalid userid '$USER_UID'"
+ exit 0
+fi
+if [ -n "$USER_GID" ] && ! echo "$USER_GID" | grep -Exq '[0-9]+'; then
+ slxlog --echo "pam-format-gid" "'$PAM_USER' has invalid groupid '$USER_GID'"
+ exit 0
+fi
+
+. /opt/openslx/pam/common/homedir-passwd
+
+exit 0
+
diff --git a/core/modules/pam-slx-plug/data/opt/openslx/pam/exec_auth b/core/modules/pam-slx-plug/data/opt/openslx/pam/exec_auth
new file mode 100755
index 00000000..ef964d5f
--- /dev/null
+++ b/core/modules/pam-slx-plug/data/opt/openslx/pam/exec_auth
@@ -0,0 +1,204 @@
+#!/bin/ash
+
+# grab the password from stdin asap
+[ "$PAM_TYPE" = "auth" ] || exit 1
+unset USER_PASSWORD
+read -r USER_PASSWORD > /dev/null 2>&1
+readonly USER_PASSWORD
+[ -z "$USER_PASSWORD" ] && echo "No password given." && exit 1
+
+USER_NAME="$PAM_USER"
+readonly PAM_USER USER_NAME
+
+# Needed as pam_script clears PATH
+export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/openslx/sbin:/opt/openslx/bin"
+
+# check for invalid char ':'
+if echo "$PAM_USER" | grep -Fq ':'; then
+ slxlog --echo "pam-format-username" "Username '$PAM_USER' contains disallowed character ':', denying access"
+ exit 1
+fi
+
+# check if the script runs as root
+[ "x$(whoami)" = "xroot" ] || exit 1
+
+# See if we have a shadow entry - skip user in that case
+grep -q "^${PAM_USER}:" "/etc/shadow" && exit 1
+
+# ppam -- pluggable pluggable authentication module
+# Source all scripts in the auth-source.d directory
+# until one succeeds.
+# A succeeding script should set USER_UID to the
+# uidNumber of the user authenticating, additionally
+# it must set USER_GID or USER_GROUP (or both).
+# Additional variables that can be set are
+# NETWORK_HOME (network path to home directory)
+# HOME_MOUNT_OPTS (mount options to use)
+# REAL_ACCOUNT (real account name in case any
+# mapping took place)
+for auth_file in /opt/openslx/pam/auth-source.d/*; do
+ NETWORK_HOME=
+ HOME_MOUNT_OPTS=
+ REAL_ACCOUNT=
+ USER_UID=
+ USER_GID=
+ USER_GROUP=
+ USER_HOME=
+ [ -f "$auth_file" ] || continue
+ . "$auth_file"
+ [ -n "$USER_UID" ] || continue
+ [ -n "${USER_GID}${USER_GROUP}" ] || continue
+ break
+done
+[ -z "$REAL_ACCOUNT" ] && REAL_ACCOUNT="$PAM_USER"
+readonly USER_UID REAL_ACCOUNT
+
+# No success - access denied
+[ -z "$USER_UID" ] && exit 1
+[ "x$USER_UID" = "x0" ] && exit 1
+
+# Validate
+if ! echo "$USER_UID" | grep -Exq '[0-9]+'; then
+ slxlog --echo "pam-format-uid" "'$PAM_USER' has invalid userid '$USER_UID'"
+ exit 1
+fi
+if [ -n "$USER_GID" ] && ! echo "$USER_GID" | grep -Exq '[0-9]+'; then
+ slxlog --echo "pam-format-gid" "'$PAM_USER' has invalid groupid '$USER_GID'"
+ exit 1
+fi
+if [ "$(echo "${USER_UID}${USER_GID}${USER_GROUP}${USER_HOME}" | wc -l)" != "1" ]; then
+ slxlog --echo "pam-format-any" "A ppam module returned multilined attributes for uid/gid/group/home"
+ exit 1
+fi
+
+# Make sure group exists locally
+GROUPENT=
+if [ -n "$USER_GID" ]; then
+ GROUPENT=$(getent group "$USER_GID" 2>/dev/null)
+fi
+if [ -z "$GROUPENT" ] && [ -n "$USER_GROUP" ]; then
+ GROUPENT=$(getent group "$USER_GROUP" 2>/dev/null)
+fi
+# Force -- neither group nor gid exist yet
+if [ -z "$GROUPENT" ]; then
+ if [ -z "$USER_GROUP" ]; then
+ USER_GROUP=generic
+ fi
+ if [ -n "$USER_GID" ]; then
+ addgroup -g "$USER_GID" "$USER_GROUP" >/dev/null 2>&1
+ else
+ addgroup "$USER_GROUP" >/dev/null 2>&1
+ fi
+ GROUPENT=$(getent group "$USER_GROUP")
+fi
+
+if [ -n "$GROUPENT" ]; then
+ [ -z "$USER_GID" ] && USER_GID=$(echo "$GROUPENT" | awk -F ':' '{print $3}')
+ [ -z "$USER_GROUP" ] && USER_GROUP=$(echo "$GROUPENT" | awk -F ':' '{print $1}')
+ if ! grep -q "^${USER_GROUP}:" '/etc/group'; then
+ echo "$GROUPENT" >> '/etc/group'
+ fi
+fi
+readonly USER_GID USER_GROUP
+
+. /opt/openslx/pam/common/homedir-passwd
+
+# The user's non-persistent home directory mount point, which should be their linux home
+TEMP_HOME_DIR="$USER_HOME"
+# The user's persistent home directory mount point
+PERSISTENT_HOME_DIR="${TEMP_HOME_DIR}/PERSISTENT"
+readonly TEMP_HOME_DIR PERSISTENT_HOME_DIR
+
+###############################################################################
+#
+# Preparations for volatile /home/<user>
+#
+#
+# check if we already mounted the home directory
+if ! awk '{print $2}' /proc/mounts | grep -Fxq -- "${TEMP_HOME_DIR}"; then
+ # no home, lets create it
+ if ! mkdir -p "${TEMP_HOME_DIR}"; then
+ slxlog --echo "pam-global-mktemphome" "Could not create '${TEMP_HOME_DIR}'."
+ fi
+ if ! mount -t tmpfs -o mode=700,size=1024m tmpfs "${TEMP_HOME_DIR}"; then
+ slxlog --echo "pam-global-tmpfstemphome" "Could not make a tmpfs on '${TEMP_HOME_DIR}'"
+ fi
+ if ! chown "${USER_UID}:${USER_GID}" "${TEMP_HOME_DIR}"; then
+ slxlog --echo "pam-global-chpersistent" "Could not chown '${TEMP_HOME_DIR}' to '${PAM_USER}'."
+ fi
+fi
+if [ -n "${REAL_ACCOUNT}" ]; then
+ echo "${REAL_ACCOUNT}" > "${TEMP_HOME_DIR}/.account"
+ chmod 0644 "${TEMP_HOME_DIR}/.account"
+fi
+
+
+###############################################################################
+#
+# Preparations for /home/<user>/PERSISTENT
+#
+#
+isHomeMounted() {
+ grep -Fuq " ${PERSISTENT_HOME_DIR} " /proc/mounts
+}
+
+PERSISTENT_OK=
+if ! isHomeMounted; then
+ if ! mkdir -p "${PERSISTENT_HOME_DIR}"; then
+ slxlog "pam-global-mkpersistent" "Could not create '${PERSISTENT_HOME_DIR}'."
+ else
+ for mount_file in /opt/openslx/pam/mount-persistent.d/*; do
+ [ -f "$mount_file" ] || continue
+ . "$mount_file"
+ if isHomeMounted; then
+ PERSISTENT_OK="yes"
+ break
+ fi
+ done
+ fi
+fi
+
+# Just try to delete the persistent dir. If the mount was successful, it will not work
+# If it was not successful, it will be removed so the user doesn't think he can store
+# anything in there
+rmdir -- "${PERSISTENT_HOME_DIR}" 2> /dev/null
+
+# Write warning message to tmpfs home
+if [ -n "${PERSISTENT_OK}" ]; then
+ # home directory mount SUCCESS
+ # create a WARNING.txt for the user with hint to PERSISTENT
+ # Remember for hooks in pam_script_auth.d
+ if [ "${NETWORK_HOME:0:2}" = '//' ]; then
+ PERSISTENT_NETPATH=$(echo "$NETWORK_HOME" | tr '/' '\')
+ else
+ PERSISTENT_NETPATH="$NETWORK_HOME"
+ fi
+ export PERSISTENT_NETPATH
+ cat > "${TEMP_HOME_DIR}/WARNING.txt" <<EOF
+ATTENTION: This is the non-persistent home directory!
+Files saved here will be lost on shutdown.
+Your real home is under ${PERSISTENT_HOME_DIR}
+Please save your files there.
+EOF
+else
+ # home directory mount FAILED
+ # create a WARNING.txt for the user, no PERSISTENT :-(
+ cat > "${TEMP_HOME_DIR}/WARNING.txt" <<EOF
+ATTENTION: This is a non-persistent home directory!
+Files saved here will be lost on shutdown.
+Please save your files on a USB drive or upload them
+to some web service.
+EOF
+fi
+chown "${USER_UID}" "${TEMP_HOME_DIR}/WARNING.txt"
+
+#
+# execute the stuff in pam_script_auth.d, if it exists
+#
+for file in /opt/openslx/scripts/pam_script_auth.d/*; do
+ [ -f "$file" ] || continue
+ ( . "$file" ) || slxlog "pam-source-hooks" "Could not source '$file'."
+done
+
+exit 0
+
diff --git a/core/modules/pam-slx-plug/data/opt/openslx/pam/exec_session b/core/modules/pam-slx-plug/data/opt/openslx/pam/exec_session
new file mode 100755
index 00000000..a789e062
--- /dev/null
+++ b/core/modules/pam-slx-plug/data/opt/openslx/pam/exec_session
@@ -0,0 +1,8 @@
+#!/bin/ash
+
+# TODO
+
+# PAM_TYPE will be "open_session" or "close_session"
+
+exit 0
+
diff --git a/core/modules/pam-slx-plug/data/opt/openslx/pam/mkhome b/core/modules/pam-slx-plug/data/opt/openslx/pam/mkhome
new file mode 100755
index 00000000..64d2260d
--- /dev/null
+++ b/core/modules/pam-slx-plug/data/opt/openslx/pam/mkhome
@@ -0,0 +1,13 @@
+#!/bin/ash
+
+U=$(id -u "$PAM_USER")
+[ -z "$U" ] && exit 1
+E="$(getent passwd "$U")"
+[ -z "$E" ] && exit 2
+G="$(echo "$E" | awk -F: '{print $4}')"
+[ -z "$G" ] && exit 3
+DIR="$(echo "$E" | awk -F: '{print $6}')"
+[ -z "$DIR" ] && exit 4
+mkdir -p "$DIR" || exit 5
+chown "$U:$G" "$DIR"
+
diff --git a/core/modules/pam-slx-plug/data/opt/openslx/pam/mount.d/99-fallback b/core/modules/pam-slx-plug/data/opt/openslx/pam/mount.d/99-fallback
new file mode 100644
index 00000000..0251458e
--- /dev/null
+++ b/core/modules/pam-slx-plug/data/opt/openslx/pam/mount.d/99-fallback
@@ -0,0 +1,97 @@
+#!/bin/ash - sourced by exec_auth
+
+[ -z "$NETWORK_HOME" ] && return
+
+OPTION_LIST="$HOME_MOUNT_OPTS"
+EXTRA_OPTS=
+if [ "${NETWORK_HOME:0:2}" = "//" ]; then
+ # CIFS
+ MOUNT="cifs"
+ if [ -z "$OPTION_LIST" ]; then
+ # No opts given, determine list of options we'll try
+ # TODO: Kerberos? How? cruid...
+ OPTION_LIST="vers=3.0,sec=ntlmssp vers=2.1,sec=ntlmssp vers=1.0,sec=ntlm vers=3.0,sec=ntlmv2 vers=1.0,sec=ntlmv2 vers=3.0,sec=ntlm vers=2.0,sec=ntlmssp #"
+ EXTRA_OPTS="forceuid,forcegid,nounix,file_mode=0700,dir_mode=0700,noacl,nobrl"
+ fi
+else
+ # Assume NFS? Leave empty, should work for NFS too
+ MOUNT=
+ if [ -n "$PAM_KRB5CCNAME" ]; then
+ export KRB5CCNAME="$PAM_KRB5CCNAME"
+ [ -z "$OPTION_LIST" ] && OPTION_LIST="sec=krb5 #"
+ fi
+fi
+[ -z "$OPTION_LIST" ] && OPTION_LIST="#"
+
+if [ "$MOUNT" = "cifs" ]; then
+ # Most servers can work without, but some don't
+ XDOMAIN=
+ if [ -n "$LDAP_BASE" ]; then
+ XDOMAIN=$( echo "$LDAP_BASE" | grep -o -E -i 'DC=([^,;]+)' | head -n 1 | cut -c 4- )
+ fi
+ if [ -z "$LDAP_DOMAIN_OVERRIDE" ]; then
+ XDOMAIN="$XDOMAIN #"
+
+ export USER="${REAL_ACCOUNT}"
+ export PASSWD="${USER_PASSWORD}"
+else
+ XDOMAIN="#"
+fi
+
+LOGFILES=
+PIDS=
+CNT=0
+for opt in $OPTION_LIST; do
+ # try with and without explicit domain argument
+ for dom in $LDAP_DOMAIN_OVERRIDE $XDOMAIN; do # No quotes
+ CNT=$(( CNT + 1 ))
+ FILE=$(mktemp)
+ LOGFILES="$LOGFILES $FILE"
+ COMMAND_LINE="-v"
+ OPTS=
+ if [ "$MOUNT" = "cifs" ]; then
+ COMMAND_LINE="$COMMAND_LINE -t cifs"
+ OPTS="${OPTS},uid=${USER_UID},gid=${USER_GID}"
+ fi
+ [ -n "$EXTRA_OPTS" ] && OPTS="${OPTS},${EXTRA_OPTS}"
+ [ "x$dom" != "x#" ] && OPTS="${OPTS},domain=$dom"
+ [ "x$opt" != "x#" ] && OPTS="${OPTS},$opt"
+ echo " ****** Trying '$OPTS'" > "$FILE"
+ [ -n "$OPTS" ] && COMMAND_LINE="$COMMAND_LINE -o ${OPTS:1}"
+ mount ${COMMAND_LINE} "${NETWORK_HOME}" "${PERSISTENT_HOME_DIR}" >> "${FILE}" 2>&1 &
+ PID=$!
+ # Wait max. 1 second; remember PID if this mount call seems to be running after we stop waiting
+ for waits in 1 2 3 4; do
+ usleep 250000
+ if isHomeMounted; then
+ # A previously invoked mount call might have succeeded while this one is still running; try to stop it right away
+ kill "$PID" &> /dev/null
+ break 3
+ fi
+ kill -0 "$PID" || break
+ done
+ kill -0 "$PID" && PIDS="$PIDS $PID" # Remember all PIDs
+ done
+done
+
+unset USER
+unset PASSWD
+
+if [ -n "$PIDS" ]; then
+ CNT=0
+ while ! isHomeMounted && [ "$CNT" -lt 10 ] && kill -0 $PIDS; do # No quotes
+ usleep 333000
+ CNT=$(( CNT + 1 ))
+ done
+ kill -9 $PIDS # Kill any leftovers; No quotes
+fi
+
+if ! isHomeMounted; then
+ LOG_COMBINED=$(mktemp)
+ [ -n "$LOGFILES" ] && cat ${LOGFILES} > "$LOG_COMBINED" # No quotes
+ slxlog --delete "pam-ad-mount" "Mount of '${VOLUME}' to '${PERSISTENT_HOME_DIR}' failed." "${LOG_COMBINED}"
+fi
+
+[ -n "${LOGFILES}" ] && rm -f -- ${LOGFILES} # No quotes
+true
+
diff --git a/core/modules/pam-slx-plug/data/opt/openslx/pam/systemd/create-pam-config b/core/modules/pam-slx-plug/data/opt/openslx/pam/systemd/create-pam-config
new file mode 100755
index 00000000..0138d3d0
--- /dev/null
+++ b/core/modules/pam-slx-plug/data/opt/openslx/pam/systemd/create-pam-config
@@ -0,0 +1,121 @@
+#!/bin/bash
+# -- bash for arrays
+
+export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/openslx/sbin:/opt/openslx/bin"
+
+declare -a auth
+declare -a account
+declare -a session
+declare -a nss
+declare -a dns
+
+# Our plugin, but account ONLY since it's fast
+account+=("[success=%NUM% new_authtok_reqd=done default=ignore] pam_exec.so quiet /opt/openslx/pam/exec_account")
+
+# unix
+auth+=("[success=%NUM% default=ignore] pam_unix.so nodelay")
+account+=("[success=%NUM% new_authtok_reqd=done default=ignore] pam_unix.so")
+nss+=("files" "cache")
+
+# check for bwIDM
+if [ -x "/opt/openslx/scripts/pam_bwidm" ]; then
+ auth+=("[success=%NUM% default=ignore] pam_exec.so quiet expose_authtok /opt/openslx/scripts/pam_bwidm")
+ account+=("[success=%NUM% new_authtok_reqd=done default=ignore] pam_exec.so quiet /opt/openslx/scripts/pam_bwidm")
+fi
+
+# Insert kerberos before our auth module
+if [ -s "/etc/ksb5.conf" ]; then
+ auth+=("optional pam_krb5.so minimum_uid=1000 use_first_pass")
+ session+=("optional pam_krb5.so minimum_uid=1000")
+fi
+
+# Our plugin, auth now
+auth+=("[success=%NUM% default=ignore] pam_exec.so quiet expose_authtok /opt/openslx/pam/exec_auth")
+
+# sssd if reasonable
+if systemctl is-enabled -q sssd.service && grep -q -e '^\s*id_provider' -e '^\s*auth_provider' "/etc/sssd/sssd.conf"; then
+ auth+=("[success=%NUM% default=ignore] pam_sss.so use_first_pass")
+ account+=("[success=%NUM% new_authtok_reqd=done default=ignore] pam_sss.so")
+ nss+=("sss")
+ # Skip sss if unix worked
+ session+=("[success=1] pam_unix.so")
+ session+=("optional pam_sss.so")
+else
+ session+=("optional pam_unix.so")
+fi
+
+# DNS
+dns+=("files" "cache")
+if systemctl is-enabled -q systemd-resolved; then
+ dns+=("resolve")
+fi
+dns+=("dns")
+
+session+=("optional pam_exec.so quiet /opt/openslx/pam/exec_session")
+
+#
+# Write pam configs
+tmpfile=$(mktemp)
+# common-auth
+skip=$(( ${#auth[@]} + 1 ))
+echo "# Generated $(date)" > "$tmpfile"
+for line in "${auth[@]}"; do
+ echo "auth ${line//%NUM%/$skip}"
+ skip=$(( skip - 1 ))
+done >> "$tmpfile"
+cat >> "$tmpfile" <<-HERE
+ auth optional pam_faildelay.so delay=2123123
+ auth requisite pam_deny.so
+ auth required pam_permit.so
+ auth optional pam_cap.so
+HERE
+cp -f -- "$tmpfile" "/etc/pam.d/common-auth"
+
+# common-account
+skip=${#account[@]}
+echo "# Generated $(date)" > "$tmpfile"
+for line in "${account[@]}"; do
+ echo "account ${line//%NUM%/$skip}"
+ skip=$(( skip - 1 ))
+done >> "$tmpfile"
+cat >> "$tmpfile" <<-HERE
+ account requisite pam_deny.so
+ account required pam_permit.so
+HERE
+cp -f -- "$tmpfile" "/etc/pam.d/common-account"
+
+# common-session
+cat > "$tmpfile" <<-HERE
+ session required pam_permit.so
+ session optional pam_umask.so
+ session required pam_systemd.so
+ session optional pam_env.so readenv=1
+ session optional pam_env.so readenv=1 envfile=/etc/default/locale
+ session optional pam_exec.so quiet /opt/openslx/pam/mkhome
+HERE
+for line in "${session[@]}"; do
+ echo "session $line"
+done >> "$tmpfile"
+cp -f -- "$tmpfile" "/etc/pam.d/common-session"
+
+#
+# Write nsswitch.conf
+cat > "/etc/nsswitch.conf" <<-HERE
+# Generated $(date)
+passwd: ${nss[@]}
+group: ${nss[@]}
+shadow: files
+
+hosts: ${dns[@]}
+networks: files
+
+protocols: db files
+services: db files
+ethers: db files
+rpc: db files
+
+netgroup: nis
+HERE
+
+exit 0
+
diff --git a/core/modules/pam-slx-plug/module.build b/core/modules/pam-slx-plug/module.build
new file mode 100644
index 00000000..241bcd5e
--- /dev/null
+++ b/core/modules/pam-slx-plug/module.build
@@ -0,0 +1,12 @@
+#!/bin/bash
+fetch_source() {
+ :
+}
+
+build() {
+ pinfo "Static module, nothing to build."
+}
+
+post_copy() {
+ :
+}
diff --git a/core/modules/pam-slx-plug/module.conf b/core/modules/pam-slx-plug/module.conf
new file mode 100644
index 00000000..237dd69c
--- /dev/null
+++ b/core/modules/pam-slx-plug/module.conf
@@ -0,0 +1,3 @@
+#!/bin/bash
+REQUIRED_BINARIES="
+"