summaryrefslogtreecommitdiffstats
path: root/shib/client_auth.php
blob: 0af34de511908bb8cef39d901f8671f8ea3d08b9 (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
<?php

use JetBrains\PhpStorm\NoReturn;

chdir('..');

require_once 'config.php';

/*
Header('Content-Type: text/plain; charset=utf-8');
die( json_encode($_SERVER, JSON_PRETTY_PRINT) );

// */

// Autoload classes from ./inc which adhere to naming scheme <lowercasename>.inc.php
spl_autoload_register(function ($class) {
	$file = 'inc/' . preg_replace('/[^a-z0-9]/', '', mb_strtolower($class)) . '.inc.php';
	if (!file_exists($file))
		return;
	require_once $file;
});

#[NoReturn]
function error(string $msg): void
{
	die('<!doctype html><html lang="en"><body><div id="bwlp-error">' . htmlspecialchars($msg) . '</div></body></html>');
}



Header('X-Frame-Options: DENY');
Header("Content-Security-Policy: frame-ancestors 'none';");
Header('Content-Type: text/html; charset=utf-8');

$token = Request::get('token');
$qrtoken = !empty(Request::get('qr'));
if (strlen($token) <= 8) {
	error('Unknown request');
}

// Got a token

$user = '';
// Get username from eppn is preferred
if (isset($_SERVER[CONFIG_EPPN])) {
	$user = preg_replace('/[^a-z0-9_@\-.]/', '', strtolower($_SERVER[CONFIG_EPPN]));
	if (!empty($user) && is_numeric($user[0])) {
		$user = 'x' . $user; // First character must not be a digit...
	}
}
if (empty($user)) {
	// No user - generate random username from persistent-id
	$user = 'x' . substr(md5($_SERVER['persistent-id']), 0, 6);
}
if (isset($_SERVER[CONFIG_SCOPED_AFFILIATION]) && !str_contains($user, '@') && preg_match('/@[^@;\s]+/', $_SERVER[CONFIG_SCOPED_AFFILIATION], $out)) {
	// We do not have an affiliation in the form of @something.com - try to extract from scoped affiliation
	$user .= $out[0];
}
if (!str_contains($user, '@')) {
	// *shrug*
	$user .= '@unknown';
}
if (strlen($user) > 31) {
	$user = substr($user, 0, 31);
}
/*
Database::exec("CREATE TABLE IF NOT EXISTS client_token (
 username varchar(128) not null,
 token char(32) not null,
 dateline int(10) unsigned not null,
 KEY (username),
 KEY (token),
 KEY (dateline)
) DEFAULT CHARSET=ascii COLLATE=ascii_general_ci");
 */
// Prevent too many attempts (max 20 in 5 mins)
Database::exec("DELETE FROM client_token WHERE dateline < UNIX_TIMESTAMP() - 300");
$num = Database::queryFirst("SELECT Count(*) AS num FROM client_token WHERE username = :user", ['user' => $user]);
if ($num['num'] > 200) {
	error('DoS detected');
}

$token2 = substr(md5(mt_rand() . ',' . microtime(true) . ',' . posix_getpid() . ','
	. $_SERVER['REMOTE_ADDR'] . $_SERVER['REMOTE_PORT']), 0, 10);

// For edit mode via CoW
$dmsd = null;
if (Util::getRole() === 'TUTOR') {
	$shibSess = ShibAuth::login(null, 300);
	if (!empty($shibSess['token'])) {
		$dmsd = $shibSess['token'];
	}
}

// See if valid QR code request
if ($qrtoken) {
	$f = Database::queryFirst("SELECT username FROM client_token WHERE qrtoken = :token LIMIT 1", ['token' => $token]);
	if ($f === false) {
		error('Unknown QR code login token');
	}
	if ($f['username'] !== '') {
		error('Token already used');
	}
	$num = Database::exec("UPDATE client_token SET username = :user, token = :pass, dmsdsession = :dmsd
            WHERE username = '' AND qrtoken = :token",
		['user' => $user, 'pass' => $token . $token2, 'token' => $token, 'dmsd' => $dmsd]);
	if ($num !== 1) {
		error('Token already used');
	}
	error('Login erfolgreich. Sie können diese Seite jetzt schließen.');
}

// Non-QRCode
Database::exec("INSERT INTO client_token (username, token, dmsdsession, dateline)
		VALUES (:user, :pass, :dmsd, UNIX_TIMESTAMP())",
	['user' => $user, 'pass' => $token . $token2, 'dmsd' => $dmsd]);
$hash = md5($token);

if (!empty($dmsd)) {
	$dmsd = "<div id=\"bwlp-cow-token\">$dmsd</div>";
}

echo <<<BLUBB
<!doctype html>
<html lang="en">
<body>
	<div style="display:none">
		<div id="bwlp-username">$user</div>
		<div id="bwlp-password">$token2</div>
		<div id="bwlp-hash">$hash</div>
		$dmsd
BLUBB;
// For debugging
/*
foreach ($_SERVER as $k => $v) {
	if (preg_match('/[a-z]/', $k)) {
		echo '<div id="' . htmlspecialchars($k) . '">' . htmlspecialchars($v) . '</div>';
	}
}
 */
echo '</div>Please wait...</body></html>';
exit;