summaryrefslogtreecommitdiffstats
path: root/remote/modules/pam-bwidm/data/opt/openslx/scripts/pam_bwidm
blob: 13e40cb9729e128a3343e8c660220d1a99882e0b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
#!/bin/ash
#
# This script is to be called by PAM (specifically pam_exec).
# We expect the username in the form: username@organisation
# If it is in that form, we will query the masterserver for the list
# of supported IdPs and if one matches the user's organisation
# we will try to authenticate against it.

# fix PATH as PAM clears it
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/openslx/sbin:/opt/openslx/bin"
if ! busybox which curl || ! busybox which mktemp; then
	echo "'curl/mktemp' missing. This script won't work without it."
	exit 1
fi

# redirect stdout/stderr to temporary logfile
readonly LOGFILE="$(mktemp)"
# URL to query masterserver for IDPs
readonly IDP_QUERY_URL="https://bwlp-masterserver.ruf.uni-freiburg.de/webif/pam.php"
readonly IDP_QUERY_CACHE="/run/openslx/bwlp-idp"

# everything in a subshell in an effort to hide sensitive information
# from this script's environment
(
# redirect stdout and stderr to logfile
exec > "${LOGFILE}" 2>&1

# check if we are allowed to run
. /opt/openslx/config
[ -z "${SLX_BWIDM_AUTH}" -o "x${SLX_BWIDM_AUTH}" != "xyes" ] && echo "bwIDM login disabled in openslx-config." && exit 1

# grab the password from stdin asap, since there is no garantee some tool just reads it
unset USER_PASSWORD
if [ "x$PAM_TYPE" == "xauth" ]; then
	read -r USER_PASSWORD > /dev/null 2>&1
	readonly USER_PASSWORD
	[ -z "$USER_PASSWORD" ] && echo "No password given." && exit 1
fi

# sanity check on PAM_USER: contains '@'?
if [ -z "$PAM_USER" ] || [ "x${PAM_USER}" == "x${PAM_USER%@*}" ]; then
	# no @ contained, invalid username, abort
	echo "Invalid username '$PAM_USER'. Aborting."
	exit 1
fi

# valid username, we can already split it here
readonly USER_USERNAME="${PAM_USER%@*}"
readonly USER_ORGANISATION="${PAM_USER#*@}"
[ -z "$USER_ORGANISATION" ] && echo "Could not parse organisation from given login: ${PAM_USER}. Aborting." && exit 1
[ -z "$USER_USERNAME" ] && echo "Could not parse user from given login: ${PAM_USER}. Aborting." && exit 1

# The given username is valid. Now we get the list of IdPs from the bwlp masterserver
# and try to find the user's organisation

mkdir -p /run/openslx

# check if we have a (non-zero bytes) cached copy of the list
if [ ! -s "${IDP_QUERY_CACHE}" ]; then
	idpret="$(curl -w "%{http_code}" -o "${IDP_QUERY_CACHE}" --connect-timeout 5 --max-time 15 "$IDP_QUERY_URL")"
	if [ "x$idpret" != "x200" ]; then
		echo "Could not download the list of identity providers from '$IDP_QUERY_URL'. Aborting."
		rm -f -- "$IDP_QUERY_CACHE"
		exit 7
	fi
fi
# here we have the cache for sure, search for the given organisation's ECP URL
USER_ECP_URL="$(awk -v idp="${USER_ORGANISATION}" -F '=' '{if($1==idp) print $2}' < "$IDP_QUERY_CACHE")"
[ -z "$USER_ECP_URL" ] && echo "Could not determine ECP URL for '${USER_ORGANISATION}'" && exit 1

# recap: here we have validated
#	- username
#	- organisation
#	- ECP URL for that organisation

# now create the bwidm group: find the first free GID from 1000 "downwards" to 100
BWIDM_GROUP="$(getent group bwidm)"
if [ -z "$BWIDM_GROUP" ]; then
	BWIDM_GID=1000
	while [ "$BWIDM_GID" -gt 100 ]; do
		getent group "$BWIDM_GID" || break
		let BWIDM_GID--
	done
	if [ "$BWIDM_GID" -eq 100 ]; then
		# use demo's gid as a fallback
		readonly BWIDM_GID="$(id -g "demo")"
		[ -z "$BWIDM_GID" ] && echo "Could not determine the GID of 'demo'. Cannot use it as fallback. Aborting." && exit 1
	fi

	# now create the group
	if ! echo "bwidm:x:$BWIDM_GID:" >> /etc/group; then
		echo "Could not create 'bwidm' group with gid '$BWIDM_GID'. Aborting."
		exit 1
	fi
else
	readonly BWIDM_GID="$(echo $BWIDM_GROUP | cut -d: -f3)"
fi
if [ -z "$BWIDM_GID" ]; then
	echo "Could not determine BWIDM-GID. Aborting."
	exit 1
fi
readonly USER_GID="$BWIDM_GID"

# path to the SOAP envelope we are gonna need soon
readonly SOAP_ENVELOPE="/opt/openslx/bwidm_soap.xml"
[ ! -f "${SOAP_ENVELOPE}" ] && echo "Failed to find the SOAP envelope at '${SOAP_ENVELOPE}'. Aborting." && exit 1

# now the pam-type specific part starts
if [ "x$PAM_TYPE" == "xauth" ]; then
	# now we are ready to actually send the credentials to the IdP
	# to be sure everything is working as expected
	# we will first send a wrong password (by repeating the given password) and expect a 401
	ret=$(curl --connect-timeout 5 --max-time 15 -o /dev/null -w "%{http_code}" -d @"${SOAP_ENVELOPE}" -H "Content-Type: application/vnd.paos+xml" --basic -u "${USER_USERNAME}:${USER_PASSWORD}${USER_PASSWORD}" "$USER_ECP_URL")

	if [ "x$ret" != "x401" ]; then
		# this means something else is bad, just exit
		echo "False authentication attempt did not return 401 as expected but: $ret"
		exit 7
	fi
	# the fake auth call behaved as expected, do the actualy login
	ret=$(curl --connect-timeout 5 --max-time 15 -o /dev/null -w "%{http_code}" -d @"${SOAP_ENVELOPE}" -H "Content-Type: application/vnd.paos+xml" --basic -u "${USER_USERNAME}:${USER_PASSWORD}" "$USER_ECP_URL")
	
	if [ "x$ret" == "x200" ]; then
		# auth succeeded, lets create a local user representing the bwIDM user
		echo "Login for '$USER_USERNAME' on '$USER_ORGANISATION' succeeded."
		# create a random 6digit UID
		LOOPS=0
		while [ "$LOOPS" -lt 5 ]; do
			USER_UID="$(( 100000 + $RANDOM ))"
			# check existence of this UID, if its free, use it
			getent passwd "$USER_UID" || break
			let LOOPS++
		done
		if [ "$LOOPS" -eq 5 ]; then
			# could not find an empty random 6-digit UID, so we will use demo's UID...
			USER_UID="$(id -u demo)"
			[ -z "$USER_UID" ] && echo "Could not use UID of 'demo' as a fallback, aborting..." && exit 1
		fi

		# we have a uid, gid, lets just create the local user now
		if ! grep -q "^${PAM_USER}:" /etc/passwd; then
			echo "${PAM_USER}:x:${USER_UID}:${USER_GID}:${PAM_USER}:/home/${PAM_USER}:/bin/bash" >> /etc/passwd
		fi
		exit 0
	elif [ "x$ret" != "x401" ]; then
		# not 200, not 401, some other kind of error occured, inform slx-admin
		echo "Unexpected http response code for the login attempt: $ret"
		exit 7
	fi
	exit 1
fi

if [ "x$PAM_TYPE" == "xaccount" ]; then
	# the sanity checks we did before reacting to PAM_TYPE is enough to validate
	# the given username as a valid bwIDM username
	# ('@' contained and IdP found in the idp list fetched from the masterserver)
	# so just "accept"
	exit 0
fi

# script should never get to the following line
echo "$0 called for unsupported PAM_TYPE '$PAM_TYPE'. Aborting."
exit 1
)
## main script
mainret=$?
if [ "x$mainret" == "x7" ]; then
	# exit code 7 is our marker to push the logfile to the sat
	slxlog "pam-bwidm" "Internal error during bwIDM authentication" "${LOGFILE}"
	( sleep 1; rm -f -- "${LOGFILE}" ) &
	exit 1
fi
exit "${mainret}"