summaryrefslogtreecommitdiffstats
path: root/prog/studisfri/studis_account.php
blob: 2605da06891c49ecfbe72e86cb25a3dcbd5477a9 (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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
<?php
function studis_get ($cookie) {
	$string = "";
	$resp = @file_get_contents("https://studisfri.uni-lj.si/StudentProfil/KontaktniPodatki", false, stream_context_create(["http" => ["method" => "GET", "header" => "Cookie: {$cookie}"]]));
	if (strpos($resp, "/Account/Logout") === false)
		return false;
	$x = new DOMDocument();
	@$x->loadHTML($resp);
	$un = trim(explode(" ", trim($x->getElementsByTagName("address")[0]->nodeValue))[0]);
	$string .= $resp;
	$resp = @file_get_contents("https://studisfri.uni-lj.si/DashboardStudent", false, stream_context_create(["http" => ["method" => "GET", "header" => "Cookie: {$cookie}"]]));
	if (strpos($resp, "/Account/Logout") === false)
		return false;
	$string .= $resp;
	$resp = @file_get_contents("https://studisfri.uni-lj.si/Student/ElektronskiIndeksStudent", false, stream_context_create(["http" => ["method" => "GET", "header" => "Cookie: {$cookie}"]]));
	if (strpos($resp, "/Account/Logout") === false)
		return false;
	$string .= $resp;
	if (strpos($un, "@") !== false) {
		global $db;
		$stmt = $db->prepare("update users set cookies=:cookies where username=:username");
		$stmt->bindParam(":username", $un, PDO::PARAM_STR);
		$stmt->bindParam(":cookies", $cookie, PDO::PARAM_STR);
		$stmt->execute();
		$cookies = [];
		foreach ($http_response_header as $h) {
			if (strtolower(explode(": ", $h)[0]) == "set-cookie") {
				$cookie = explode("; ", explode(": ", $h)[1])[0];
				$cookies[] = $cookie;
				add_infinite_cookie($cookie);
			} else
				if (strtolower(explode(": ", $h)[0]) != "location")
					header($h);
		}
		if (sizeof($cookies)) {
			$stmt = $db->prepare("update users set cookies=:cookies where username=:username");
			$stmt->bindParam(":username", $un, PDO::PARAM_STR);
			$cookies = implode("; ", $cookies);
			$stmt->bindParam(":cookies", $cookies, PDO::PARAM_STR);
			$stmt->execute();
		}
	}
	return ["hash" => hash("sha256", $string, true), "username" => $un];
}
function add_infinite_cookie ($cookie) {
	header("Set-Cookie: $cookie; Path=/; Expires=Fri, 31 Dec 9999 23:59:59 GMT; Secure; HttpOnly", false);
}
function make_login_page ($resp) {
	$replace = <<<HEREDOC
		<details style=margin:1cm>
			<summary>
				▶ Pokaži polje za nalaganje obstoječe seje na
				strežnik (za napredne uporabnike)
			</summary>
			
    <div class="form-group">
		<label for=Session>
			Sejni piškotek <code>.ASPXAUTH</code> prilepite v spodnje polje obliki, kot bi bil poslan v <code>Cookie:</code> headerju. Primer: <code>.ASPXAUTH=713851603</code>
		</label>
	<div class="col-sm-offset-2 col-sm-8">
	    <div class="input-group">
		<div class="input-group-addon">
		    <i class="fa fa-fw fa-unlock-alt"></i>
		</div>
		<input autocomplete="off" class="form-control" id="Session" name="Session" placeholder="Sejni piškotki" type="text" />
	</div>
	    <p>Uporabniško ime in geslo morate prav tako vnesti. Posebej bodite pazljivi, da je geslo pravnilno vnešeno, saj strežnik njegove pravilnosti ne bo preverjal.</p>
    </div>

		</details>
	<div class="modal-footer">
HEREDOC;
	echo str_replace('<div class="modal-footer">', $replace, str_replace("studisfri.uni-lj.si", $_SERVER["HTTP_HOST"], $resp));
}
function waste_login ($tekst) {
	if (!empty($_REQUEST["potrdilo"])) {
		$resp = file_get_contents("https://studisfri.uni-lj.si/Account/Login", false, stream_context_create(["http" => ["follow_location" => 0, "method" => "POST", "header" => "Content-Type: application/x-www-form-urlencoded\r\nCookie: {$_SERVER["HTTP_COOKIE"]}", "content" => "__RequestVerificationToken=" . urlencode($_POST["rvt"]) . "&Username=" . urlencode($_POST["username"]) . "&Password=" . urlencode($_POST["password"])]]));
		# file_put_contents("/tmp/resp.html", $resp);
		# file_put_contents("/tmp/http_response_header.txt", implode("\r\n", $http_response_header));
		if (strpos($http_response_header[0], "302") !== false) {
			http_response_code(303);
			$cookies = [];
			foreach ($http_response_header as $h) {
				if (strtolower(explode(": ", $h)[0]) == "set-cookie") {
					$cookie = explode("; ", explode(": ", $h)[1])[0];
					$cookies[] = $cookie;
					add_infinite_cookie($cookie);
				} else
					if (strtolower(explode(": ", $h)[0]) != "location")
						header($h);
			}
			global $db;
			$stmt = $db->prepare("insert into users (username, cookies, password, last) values (:username, :cookies, :password, CURRENT_TIMESTAMP) on conflict(username) do update set username=:username, cookies=:cookies, password=:password, last=CURRENT_TIMESTAMP");
			$stmt->bindParam(":username", $_POST["username"], PDO::PARAM_STR);
			$cookies = implode("; ", $cookies);
			$stmt->bindParam(":cookies", $cookies, PDO::PARAM_STR);
			$password = password_hash($_POST["password"], PASSWORD_DEFAULT);
			$stmt->bindParam(":password", $password, PDO::PARAM_STR);
			$stmt->execute();
			header("Location: /");
		} else {
			echo make_login_page($resp);
		}
	} else {
		echo $tekst . ' Če nadaljujete s prijavo z uporabniškim imenom in geslom, bo porabljena ena vaša prijava brez kvalificiranega potrdila.<form method=post><input type=hidden name=username value="' . htmlspecialchars($_POST["Username"]) . '" /><input type=hidden name=password value="' . htmlspecialchars($_POST["Password"]) . '" /><input type=hidden name=rvt value=' . htmlspecialchars($_POST["__RequestVerificationToken"]) . ' /><input type=submit name=potrdilo value="Nadaljuj s prijavo v STUDIS >>>" /></form>Če se vam večkrat zaporedoma kaže to sporočilo, ohranjanje sej na strežniku mogoče ne deluje. Prosim, da mi v tem primeru pošljete pismo na naslov <a href=mailto:anton@sijanec.eu>anton@šijanec.eu</a> in do popravila sistema uporabljate <a href=https://studisfri.uni-lj.si>uradni portal STUDIS</a>.';
	}
}
umask(0077);
if (!empty($_REQUEST["src"])) {
	die(file_get_contents($_SERVER["SCRIPT_FILENAME"]));
}
$db = new PDO("sqlite:studis.sqlite3", null, null, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
$did = false;
if (!$db || !empty($_REQUEST["dberror"])) {
	http_response_code(503);
	echo "Strežnik ne more odpreti podatkovne zbirke. Prosim, kontaktirajte me na naslov <a href=mailto:anton@sijanec.eu>anton@šijanec.eu</a>. Med nedelovanjem moje storitve lahko uporabljate uradni portal na spletni strani <a href=https://studisfri.uni-lj.si>studisfri.uni-lj.si</a>, vendar boste koristili preostale prijave.";
	$did = true;
}
$db->query("create table if not exists users (username TEXT PRIMARY KEY UNIQUE NOT NULL CHECK(length(username) > 0), cookies TEXT UNIQUE NOT NULL, password TEXT NOT NULL, last default CURRENT_TIMESTAMP, mail INTEGER, hash TEXT CHECK(length(hash) == 32))
");
if (strpos(strtolower($_SERVER["DOCUMENT_URI"]), "script") !== false) {
	studis_get($_SERVER["HTTP_COOKIE"]);
	die(file_get_contents("script.js"));
}
if (strpos(strtolower($_SERVER["DOCUMENT_URI"]), "odjava") !== false) {
	if ($_SERVER["REQUEST_METHOD"] == "POST") {
		$stmt = $db->prepare("update users set mail=0 where hash=:hash");
		$stmt->bindParam(":hash", $_REQUEST["hash"], PDO::PARAM_LOB);
		$stmt->execute();
		echo "Zahteva po odjavi uspešno prejeta.";
	} else {
		echo "S klikom na gumb Odjava se odjavite od pridobivanja sporočil na ta elektronski naslov. Ponovno se lahko prijavite na sporočila samo v <a href=nastavitve>nastavitvah</a> med tem, ko ste prijavljeni. Ta odjava deluje tudi takrat, ko niste prijavljeni, vendar je povezava za odjavo delujoča le od zadnje vzpostavljene seje.<form method=post><input type=submit name=odjava value='Odjavi se od prejemanja elektronskih sporočil' /></form>";
	}
	$did = true;
}
if (strpos(strtolower($_SERVER["DOCUMENT_URI"]), "nastavitve") !== false) {
	$r = studis_get($_SERVER["HTTP_COOKIE"]);
	if ($r) {
		if ($_SERVER["REQUEST_METHOD"] == "POST") {
			if (password_verify($_SERVER["HTTP_COOKIE"], $_POST["csrf"])) {
				$value = 0;
				if (@$_POST["mail"] == "prosim")
					$value = 1;
				$stmt = $db->prepare("update users set mail=$value, hash=:hash where username=:username");
				$stmt->bindParam(":username", $r["username"], PDO::PARAM_STR);
				$stmt->bindParam(":hash", $r["hash"], PDO::PARAM_LOB);
				$stmt->execute();
			} else {
				echo "Zgodil se je CSRF napad ali pa napaka na strežniku. Ne spreminjam nobenih nastavitev. Kontaktirate lahko administratorja na naslov <a href=mailto:anton@sijanec.eu>anton@šijanec.eu</a>.";
			}
		}
		$csrf = password_hash($_SERVER["HTTP_COOKIE"], PASSWORD_DEFAULT);
		$stmt = $db->prepare("select mail from users where username=:username");
		$stmt->bindParam(":username", $r["username"], PDO::PARAM_STR);
		$stmt->execute();
		echo "<h1>dodatne nastavitve posredniškega strežnika studisfri</h1><form method=POST><input type=hidden name=csrf value=$csrf><input type=checkbox id=mail name=mail value=prosim " . ($stmt->fetchColumn(0) ? "checked" : "") . " /><label for=mail>pošlji obvestilo na moj elektronski naslov ({$r["username"]}), ko se spremeni kaj na Studisu (preverjanje štirikrat dnevno) oziroma če prijava v STUDIS ne uspe</label><br><input type=submit name=submit value=shrani /></form><a href=/>Nazaj na glavno stran <<<</a>";
	} else {
		http_response_code(303);
		header("Location: /Account/Login");
	}
	$did = true;
}
if (strpos(strtolower($_SERVER["DOCUMENT_URI"]), "logout") !== false) {
	http_response_code(303);
	setcookie(".ASPXAUTH", "pls expire my digga", 1, "/", null, true, true);
	header("Location: /");
	// echo "Odjava uspešna. Morebitno sejo ohranjam na strežniku. Preusmerjam na prijavno stran v petih sekundah. <meta http-equiv=refresh content=5;/ />";
	$did = true;
}
if (strpos(strtolower($_SERVER["DOCUMENT_URI"]), "cookies") !== false) {
	if (strpos($_REQUEST["cookies"], "\n") !== false || strpos($_REQUEST["location"], "\n") !== false)
		die("hacker reported to the fbi");
	header("Location: " . $_REQUEST["location"]);
	foreach (explode("; ", $_REQUEST["cookies"]) as $c)
		add_infinite_cookie($c);
}
if (strpos(strtolower($_SERVER["DOCUMENT_URI"]), "cron") !== false) {
	$ret = $db->query("select username, cookies, mail, hash from users");
	header("Content-Type: text/plain");
	foreach ($ret as $row) {
		$g = studis_get($row[1]);
		if ($g === false) {
			echo "neuspelo\t" . $row[0];
			if ($row[2]) {
				$uehash = urlencode($row[3]);
				mail($row[0], "Neuspela prijava v portal STUDIS", "Spoštovani,\r\n\r\nobveščam vas, da se posredniški strežnik STUDISa v vaš profil ni uspel prijaviti. Lahko gre le za začasno napako (izpad omrežne povezave), ali pa je potekla vaša seja na strežniku. Poštna obvestila lahko izklopite brez prijave na povezavi https://studisfri.4a.si/Account/odjava?hash=$uehash ali pa s pismom administratorju na naslov anton@sijanec.eu.\r\n\r\nLep pozdrav\r\nPHP\r\n", "From: studisfri@4a.si\r\nReply-To: anton@sijanec.eu");
				echo "\tmail";
			}
			echo PHP_EOL;
		} else {
			$uc = urlencode($row[1]);
			$izhod = `./screenshot.sh 'https://studisfri.4a.si/Account/cookies?cookies=$uc&location=/' 2>&1`;
			$h = "";
			foreach (explode("\n", $izhod) as $v) {
				$x = explode(" ", $v);
				if ($x[0] == "zgoščena_vrednost")
					$h = " Posnetek zaslona vaše nadzorne plošče na portalu STUDIS si lahko ogledate na https://s.4a.si/studisfri/{$x[1]}.png";
			}
			echo "uspelo\t" . $g["username"];
			if ($row[2] && $g["hash"] != $row[3] && strpos($izhod, "datoteka_že_obstaja") === false) {
				$uehash = urlencode($g["hash"]);
				mail($row[0], "Sprememba na portalu STUDIS", "Spoštovani,\r\n\r\nobveščam vas, da se je na vašem STUDIS portalu {$row[0]} pojavila sprememba. Portal STUDIS je dostopen na povezavi https://studisfri.4a.si/. Ta obvestila lahko izklopite brez prijave na naslovu https://studisfri.4a.si/Account/odjava?hash=$uehash ali pa s pismom administratorju na naslov anton@sijanec.eu.$h\r\n\r\nLep pozdrav\r\nPHP\r\n\r\n\r\n---------\r\nDiagnostične informacije sledijo:\r\nPrejšnja zgoščena vrednost STUDIS: " . bin2hex($row[3]) . "\r\nTrenutna zgoščena vrednost STUDIS: " . bin2hex($g["hash"]) . "\r\n\r\nIzhod programa screenshot.sh:\r\n$izhod", "From: studisfri@4a.si\r\nReply-To: anton@sijanec.eu");
				echo "\tmail";
			}
			$stmt = $db->prepare("update users set last=CURRENT_TIMESTAMP, hash=:hash where username=:username");
			$stmt->bindParam(":username", $row[0], PDO::PARAM_STR);
			$stmt->bindParam(":hash", $g["hash"], PDO::PARAM_LOB);
			$stmt->execute();
			echo PHP_EOL;
		}
	}
	die();
}
if (strpos(strtolower($_SERVER["DOCUMENT_URI"]), "setculture") !== false) {
	@file_get_contents("https://studisfri.uni-lj.si/Account/SetCulture?culture={$_GET['culture']}", false, stream_context_create(["http" => ["method" => "GET", "header" => "Cookie: {$_SERVER["HTTP_COOKIE"]}"]]));
	http_response_code(303);
	header("Location: {$_GET["ReturnUrl"]}");
}
if (strpos(strtolower($_SERVER["DOCUMENT_URI"]), "registercertificate") !== false) {
	http_response_code(404);
	echo "Kvalificiranega digitalnega potrdila ne morete registrirati preko tega posredniškega strežnika. Za registracijo potrdila uporabite uradno spletno stran na naslovu <a href=https://studisfri.uni-lj.si/Account/RegisterCertificate>studisfri.uni-lj.si</a>.";
	$did = true;
}
if (strpos(strtolower($_SERVER["DOCUMENT_URI"]), "login") !== false) {
	$resp = @file_get_contents("https://studisfri.uni-lj.si/Account/Login", false, stream_context_create(["http" => ["method" => "GET", "header" => "Cookie: {$_SERVER["HTTP_COOKIE"]}"]]));
	if (strpos($resp, "/Account/Logout") !== false) {
		http_response_code(303);
		header("Location: /");
	} else {
		if ($_SERVER["REQUEST_METHOD"] != "POST") {
			foreach ($http_response_header as $h) {
				if (strtolower(explode(": ", $h)[0]) == "set-cookie") {
					$cookie = explode("; ", explode(": ", $h)[1])[0];
					$cookies[] = $cookie;
					add_infinite_cookie($cookie);
				} else {
					header($h);
				}
			}
			echo make_login_page($resp);
		} else {
			$stmt = $db->prepare("select cookies, password from users where username=:username");
			$stmt->bindParam(":username", $_POST["Username"], PDO::PARAM_STR);
			// $stmt->debugDumpParams();
			$stmt->execute();
			$row = $stmt->fetch();
			if (!empty($_POST["Session"])) {
				$resp = @file_get_contents("https://studisfri.uni-lj.si/StudentProfil/KontaktniPodatki", false, stream_context_create(["http" => ["method" => "GET", "header" => "Cookie: {$_POST["Session"]}"]]));
				if (strpos($resp, "/Account/Logout") !== false) {
					$x = new DOMDocument();
					@$x->loadHTML($resp);
					$un = trim(explode(" ", trim($x->getElementsByTagName("address")[0]->nodeValue))[0]);
					if ($un == $_POST["Username"]) {
						$stmt = $db->prepare("insert into users (username, cookies, password, last) values (:username, :cookies, :password, CURRENT_TIMESTAMP) ON CONFLICT(username) DO UPDATE SET username=:username, cookies=:cookies, password=:password");
						$stmt->bindParam(":username", $_POST["Username"]);
						$stmt->bindParam(":cookies", $_POST["Session"]);
						$pwhash = password_hash($_POST["Password"], PASSWORD_DEFAULT);
						$stmt->bindParam(":password", $pwhash);
						$stmt->execute();
						http_response_code(303);
						foreach (explode("; ", $_POST["Session"]) as $cookie) {
							add_infinite_cookie($cookie);
						}
						header("Location: /");
					} else {
						echo "Uporabniško ime podane seje ($un) se ne ujema z vašim podanim uporabniškim imenom v prijavnem obrazcu ({$_POST["Username"]}). <a href=/>Nazaj na prijavno stran <<<</a>";
					}
				} else {
					waste_login("Poslani sejni piškotki vas ne prijavijo v STUDIS.");
				}
			} else {
				if ($row == false || $row["cookies"] == false) {
					waste_login("Strežnik nima shranjene vaše seje.");
				} else {
					$resp = @file_get_contents("https://studisfri.uni-lj.si/Account/Login", false, stream_context_create(["http" => ["method" => "GET", "header" => "Cookie: {$row["cookies"]}"]]));
					if (strpos($resp, "/Account/Logout") !== false) {
						if (password_verify($_POST["Password"], $row["password"])) {
							http_response_code(303);
							foreach (explode("; ", $row["cookies"]) as $cookie) {
								add_infinite_cookie($cookie);
							}
							header("Location: /");
						} else {
							waste_login("Napačno geslo. <b>Na strežniku obstaja aktivna seja.</b> Če ste menjali svoje ID UL geslo, se lahko prijavite s starim, kar bo nadaljevalo vašo obstoječo sejo, če se pa starega gesla ne spomnite, pa lahko nadaljujete s prijavo z novim geslom in s tem porabite eno prijavo (v kolikor je geslo pravilno).");
						}
					} else {
						waste_login("Seja, shranjena na strežniku, je potekla.");
					}
				}
			}
		}
	}
	$did = true;
}
if (!$did) {
	echo "Program ni naredil ničesar. Če vidite to sporočilo, se je zgodila napaka. Prosim, kontaktirajte me na naslov <a href=mailto:anton@sijanec.eu>anton@šijanec.eu</a>. <a href=/>Nazaj na glavno stran</a>";
}
?>
<meta name=viewport content='width=device-width, initial-scale=1.0'>
<br><br><br>
<hr>
<details><summary>Prikaži diagnostične informacije</summary>
<pre>
<?php htmlspecialchars(var_export($_SERVER) . var_export($_REQUEST)); ?>
</pre>
Trenutno upravljam s sejami naslednjih uporabnikov:
<style>
table, tr, th, td {
	border: 1px solid red;
}
</style>
<table>
<tr>
<th>uporabniško ime</th>
<th>čas zadnje uspešne osvežitve seje v UTC</th>
<th>želi elektronsko pošto</th>
</tr>
<?php
$ret = $db->query("select username, last, mail from users");
foreach ($ret as $row) {
	echo "<tr><td>" . htmlspecialchars($row["username"]) . "</td><td>{$row['last']}</td><td>" . ($row["mail"] ? "da" : "ne") . "</td>";
}
?>