Code für 2-FA mit TOTP vorläufig entfernt
Bernd Wurst

Bernd Wurst commited on 2023-12-08 14:39:22
Zeige 6 geänderte Dateien mit 4 Einfügungen und 306 Löschungen.

... ...
@@ -1,35 +0,0 @@
1
-<?php
2
-/*
3
-This file belongs to the Webinterface of schokokeks.org Hosting
4
-
5
-Written by schokokeks.org Hosting, namely
6
-  Bernd Wurst <bernd@schokokeks.org>
7
-  Hanno Böck <hanno@schokokeks.org>
8
-
9
-This code is published under a 0BSD license.
10
-
11
-Nevertheless, in case you use a significant part of this code, we ask (but not require, see the license) that you keep the authors' names in place and return your changes to the public. We would be especially happy if you tell us what you're going to do with this code.
12
-*/
13
-
14
-require_once('inc/base.php');
15
-require_role(ROLE_SYSTEMUSER);
16
-
17
-require_once('totp.php');
18
-
19
-$id = (int) $_REQUEST['totp'];
20
-
21
-$sure = user_is_sure();
22
-if ($sure === null) {
23
-    $section = 'loginsecurity_overview';
24
-    title("Zwei-Faktor-Anmeldung");
25
-    are_you_sure("totp={$id}", "Möchten Sie die Zwei-Faktor-Anmeldung wirklich entfernen?");
26
-} elseif ($sure === true) {
27
-    delete_systemuser_totp($id);
28
-    if (!$debugmode) {
29
-        header("Location: overview");
30
-    }
31
-} elseif ($sure === false) {
32
-    if (!$debugmode) {
33
-        header("Location: overview");
34
-    }
35
-}
... ...
@@ -1,191 +0,0 @@
1
-<?php
2
-/*
3
-This file belongs to the Webinterface of schokokeks.org Hosting
4
-
5
-Written by schokokeks.org Hosting, namely
6
-  Bernd Wurst <bernd@schokokeks.org>
7
-  Hanno Böck <hanno@schokokeks.org>
8
-
9
-This code is published under a 0BSD license.
10
-
11
-Nevertheless, in case you use a significant part of this code, we ask (but not require, see the license) that you keep the authors' names in place and return your changes to the public. We would be especially happy if you tell us what you're going to do with this code.
12
-*/
13
-
14
-require_once('vendor/autoload.php');
15
-
16
-function list_systemuser_totp($uid = null)
17
-{
18
-    if (!$uid) {
19
-        $uid = (int)$_SESSION['userinfo']['uid'];
20
-    }
21
-    $result = db_query("SELECT id, description, setuptime FROM system.systemuser_totp WHERE uid=?", [$uid]);
22
-    $ret = [];
23
-    while ($line = $result->fetch()) {
24
-        $ret[] = $line;
25
-    }
26
-    return $ret;
27
-}
28
-
29
-function check_systemuser_password($password)
30
-{
31
-    $result = db_query("SELECT passwort AS password FROM system.passwoerter WHERE uid=:uid", [":uid" => $_SESSION['userinfo']['uid']]);
32
-    if (@$result->rowCount() > 0) {
33
-        $entry = $result->fetch(PDO::FETCH_OBJ);
34
-        $db_password = $entry->password;
35
-        $hash = crypt($password, $db_password);
36
-        if ($hash == $db_password) {
37
-            return true;
38
-        }
39
-    }
40
-    return false;
41
-}
42
-
43
-
44
-function generate_systemuser_secret()
45
-{
46
-    $ga = new PHPGangsta_GoogleAuthenticator();
47
-
48
-    $secret = $ga->createSecret();
49
-    DEBUG('GA-Secret: ' . $secret);
50
-    return $secret;
51
-}
52
-
53
-function check_systemuser_locked($uid)
54
-{
55
-    if (!$uid) {
56
-        $uid = $_SESSION['userinfo']['uid'];
57
-    }
58
-    $result = db_query("SELECT 1 FROM system.systemuser_totp WHERE unlock_timestamp IS NOT NULL and unlock_timestamp > NOW() AND uid=?", [$uid]);
59
-    return ($result->rowCount() > 0);
60
-}
61
-
62
-function check_systemuser_totp($uid, $code)
63
-{
64
-    $ga = new PHPGangsta_GoogleAuthenticator();
65
-    $secret = null;
66
-    $checkResult = false;
67
-    if (isset($_SESSION['totp_secret'])) {
68
-        // Während des Setup
69
-        $secret = $_SESSION['totp_secret'];
70
-        $checkResult = $ga->verifyCode($secret, $code, 2);    // 2 = 2*30sec clock tolerance
71
-    } else {
72
-        // Normalbetrieb
73
-        if (!$uid) {
74
-            $uid = $_SESSION['userinfo']['uid'];
75
-        }
76
-        $result = db_query("SELECT id,secret,failures FROM system.systemuser_totp WHERE uid=? AND (unlock_timestamp IS NULL OR unlock_timestamp<NOW())", [$uid]);
77
-        while ($tmp = $result->fetch()) {
78
-            $totp_id = $tmp['id'];
79
-            $secret = $tmp['secret'];
80
-
81
-            if (check_systemuser_blacklist($uid, $totp_id, $code)) {
82
-                DEBUG('Replay-Attack');
83
-                return false;
84
-            }
85
-
86
-            $checkResult = $ga->verifyCode($secret, $code, 2);    // 2 = 2*30sec clock tolerance
87
-            if ($checkResult) {
88
-                db_query("UPDATE system.systemuser_totp SET lastused=CURRENT_TIMESTAMP() WHERE id=?", [$totp_id]);
89
-                blacklist_systemuser_token($uid, $totp_id, $code);
90
-                DEBUG('OK');
91
-            } else {
92
-                DEBUG('TOTP-Code war falsch, checke gegen Restoretoken');
93
-                if ($code == totp_restoretoken($totp_id)) {
94
-                    // Das Restoretoken wird als gültiges OTP anerkannt (eigentlich nicht okay aber einfacher)
95
-                    return true;
96
-                }
97
-                if ($tmp['failures'] > 0 && $tmp['failures'] % 5 == 0) {
98
-                    db_query("UPDATE system.systemuser_totp SET failures = failures+1, unlock_timestamp = NOW() + INTERVAL 5 MINUTE WHERE id=?", [$totp_id]);
99
-                } else {
100
-                    db_query("UPDATE system.systemuser_totp SET failures = failures+1 WHERE id=?", [$totp_id]);
101
-                }
102
-                DEBUG('FAILED');
103
-            }
104
-            if ($checkResult) {
105
-                // Wenn einer stimmt, dann reicht uns das
106
-                return true;
107
-            }
108
-        }
109
-    }
110
-    return $checkResult;
111
-}
112
-
113
-function save_totp_config($description)
114
-{
115
-    if (!isset($_SESSION['totp_secret'])) {
116
-        system_failure("Session kaputt");
117
-    }
118
-    $args = [":uid" => $_SESSION['userinfo']['uid'], ":secret" => $_SESSION['totp_secret'], ":restoretoken" => random_string(30), ":description" => $description];
119
-    db_query("INSERT INTO system.systemuser_totp (description, uid, secret, restoretoken) VALUES (:description, :uid, :secret, :restoretoken)", $args);
120
-    unset($_SESSION['totp_secret']);
121
-    return db_insert_id();
122
-}
123
-
124
-function totp_restoretoken($totp_id)
125
-{
126
-    $result = db_query(
127
-        "SELECT restoretoken FROM system.systemuser_totp WHERE id=:id",
128
-        [":id" => $totp_id]
129
-    );
130
-    $data = $result->fetch();
131
-    DEBUG("Restoretoken für #{$totp_id} ist {$data['restoretoken']}");
132
-    return $data['restoretoken'];
133
-}
134
-
135
-function generate_systemuser_qrcode_image($secret)
136
-{
137
-    $username = $_SESSION['userinfo']['username'];
138
-    $url = 'otpauth://totp/' . $username . '@schokokeks.org?secret=' . $secret;
139
-
140
-    $descriptorspec = [
141
-    0 => ["pipe", "r"],  // STDIN ist eine Pipe, von der das Child liest
142
-    1 => ["pipe", "w"],  // STDOUT ist eine Pipe, in die das Child schreibt
143
-    2 => ["pipe", "w"],
144
-  ];
145
-
146
-    $process = proc_open('qrencode -t PNG -s 5 -o -', $descriptorspec, $pipes);
147
-
148
-    if (is_resource($process)) {
149
-        // $pipes sieht nun so aus:
150
-        // 0 => Schreibhandle, das auf das Child STDIN verbunden ist
151
-        // 1 => Lesehandle, das auf das Child STDOUT verbunden ist
152
-
153
-        fwrite($pipes[0], $url);
154
-        fclose($pipes[0]);
155
-
156
-        $pngdata = stream_get_contents($pipes[1]);
157
-        fclose($pipes[1]);
158
-
159
-        // Es ist wichtig, dass Sie alle Pipes schließen bevor Sie
160
-        // proc_close aufrufen, um Deadlocks zu vermeiden
161
-        $return_value = proc_close($process);
162
-
163
-        return $pngdata;
164
-    } else {
165
-        warning('Es ist ein interner Fehler im Webinterface aufgetreten, aufgrund dessen kein QR-Code erstellt werden kann. Sollte dieser Fehler mehrfach auftreten, kontaktieren Sie bitte die Administratoren.');
166
-    }
167
-}
168
-
169
-
170
-function delete_systemuser_totp($id)
171
-{
172
-    $args = [":id" => $id,
173
-                ":uid" => $_SESSION['userinfo']['uid'], ];
174
-
175
-    db_query("DELETE FROM system.systemuser_totp WHERE id=:id AND uid=:uid", $args);
176
-}
177
-
178
-
179
-function blacklist_systemuser_token($uid, $totpid, $token)
180
-{
181
-    $args = [":totpid" => $totpid, ":uid" => $uid, ":token" => $token];
182
-    db_query("INSERT INTO system.systemuser_totp_used (timestamp, uid, totpid, token) VALUES (NOW(), :uid, :totpid, :token)", $args);
183
-}
184
-
185
-function check_systemuser_blacklist($uid, $totpid, $token)
186
-{
187
-    $args = [":totpid" => $totpid, ":uid" => $uid, ":token" => $token];
188
-    db_query("DELETE FROM system.systemuser_totp_used WHERE timestamp < NOW() - INTERVAL 10 MINUTE");
189
-    $result = db_query("SELECT id FROM system.systemuser_totp_used WHERE uid=:uid AND totpid=:totpid AND token=:token", $args);
190
-    return ($result->rowCount() > 0);
191
-}
... ...
@@ -1,4 +1,4 @@
1
-name = totp
2
-description = 2-Faktor-Authentifizierung für Login
3
-permission = Verwalten der 2-Faktor-Authentifizierung
1
+name = loginsecurity
2
+description = Passkey-Verwaltung
3
+permission = Verwalten des Passkeys zum Login
4 4
 
... ...
@@ -1,75 +0,0 @@
1
-<?php
2
-/*
3
-This file belongs to the Webinterface of schokokeks.org Hosting
4
-
5
-Written by schokokeks.org Hosting, namely
6
-  Bernd Wurst <bernd@schokokeks.org>
7
-  Hanno Böck <hanno@schokokeks.org>
8
-
9
-This code is published under a 0BSD license.
10
-
11
-Nevertheless, in case you use a significant part of this code, we ask (but not require, see the license) that you keep the authors' names in place and return your changes to the public. We would be especially happy if you tell us what you're going to do with this code.
12
-*/
13
-
14
-require_once('totp.php');
15
-require_once('inc/base.php');
16
-require_once('inc/security.php');
17
-require_role(ROLE_SYSTEMUSER);
18
-
19
-$username = $_SESSION['userinfo']['username'];
20
-
21
-$section = 'loginsecurity_overview';
22
-title("Zwei-Faktor-Anmeldung für Ihren Account");
23
-
24
-warning('Nach Einrichtung der Zwei-Faktor-Anmeldung benötigen Sie für jeden Login einen Einmal-Code, den Sie mit einem Code-Generator - meist ein Smartphone mit einer entsprechenden App - erzeugen. Dieser Code wird aus einem gemeinsamen Geheimnis und der aktuellen Zeit jeweils neu berechnet.');
25
-output('<p>Zur Einrichtung der Zwei-Faktor-Anmeldung für den Benutzer <strong>' . $username . '</strong>, scannen Sie mit Ihrer Code-Generator-App den unten stehenden QR-Code oder geben Sie das Geheimnis/Secret manuell in den Code-Generator ein.</p>');
26
-
27
-output('<h3>QR-Code für Code-Generator-App</h3>');
28
-output('<p>Der Zugang wird erst dann mit dem zweiten Faktor geschützt, wenn Sie unten einmalig einen korrekten Code eingegeben haben.</p>');
29
-
30
-if (!isset($_SESSION['totp_secret'])) {
31
-    $_SESSION['totp_secret'] = generate_systemuser_secret();
32
-}
33
-
34
-$qrcode_image = generate_systemuser_qrcode_image($_SESSION['totp_secret']);
35
-output('<p><img src="data:image/png;base64,' . base64_encode($qrcode_image) . '" /></p>
36
-<p>Secret-Code für manuelle initialisierung des Code-Generators: <span style="font-size: 120%;">' . $_SESSION['totp_secret'] . '</span></p>');
37
-
38
-
39
-$passed = false;
40
-$totp_id = null;
41
-if (isset($_POST['password']) && isset($_POST['token'])) {
42
-    // Prüfen, ob das Passwort und der Code stimmen
43
-    if (!check_systemuser_password($_POST['password'])) {
44
-        input_error('Das Passwort scheint falsch zu sein.');
45
-        $passed = false;
46
-    }
47
-    if (check_systemuser_totp($_SESSION['userinfo']['uid'], $_POST['token'])) {
48
-        // Passwort stimmt, Token stimmt
49
-        // Config abspeichern
50
-        $description = null;
51
-        if (isset($_POST['description'])) {
52
-            $description = $_POST['description'];
53
-        }
54
-        $totp_id = save_totp_config($description);
55
-        $passed = true;
56
-    } else {
57
-        input_error('Der Code hat nicht gestimmt. Bitte prüfen Sie auch, ob die Egeräte-Uhrzeit stimmt.');
58
-        $passed = false;
59
-    }
60
-}
61
-
62
-
63
-if ($passed) {
64
-    output('<p>Der obige Code wurde als zweiter Faktor eingerichtet!</p><p>Bitte notieren Sie sich den folgenden Restore-Code für den Fall, dass der Code-Generator nicht mehr verfügbar ist (Beschädigung am Handy, ...).</p><p>Restore-Code: <span style="font-size: 120%;">' . totp_restoretoken($totp_id) . '</span></p>');
65
-} else {
66
-
67
-    $form = '<p>Geben Sie zur Identifikation bitte Ihr Passwort und den aktuell vom Generator erzeugten Code ein.</p>
68
-<p>Passwort: <input type="password" name="password" /></p>
69
-<p>Bezeichnung für diesen Code-Generator: <input type="text" name="description" /></p>
70
-<p>Aktueller TOTP-Code: <input type="text" name="token" /></p>';
71
-
72
-    $form .= '<p><input type="submit" value="Einrichten" /></p>';
73
-
74
-    output(html_form('totp_setup', 'setup', '', $form));
75
-}
... ...
@@ -14,7 +14,6 @@ Nevertheless, in case you use a significant part of this code, we ask (but not r
14 14
 require_once('inc/base.php');
15 15
 require_once('inc/debug.php');
16 16
 require_once('inc/error.php');
17
-require_once('modules/loginsecurity/include/totp.php');
18 17
 
19 18
 define('ROLE_ANONYMOUS', 0);
20 19
 define('ROLE_MAILACCOUNT', 1);
... ...
@@ -33,7 +33,7 @@ if (have_module('webmailtotp') && isset($_POST['webinterface_totpcode']) && isse
33 33
         $_SESSION['role'] = ROLE_ANONYMOUS;
34 34
         logger(LOG_WARNING, "session/start", "login", "wrong totp code (username: »{$_SESSION['totp_username']}«)");
35 35
         warning('Ihre Anmeldung konnte nicht durchgeführt werden. Geben Sie bitte einen neuen Code ein.');
36
-        show_page('webmailtotp-login');
36
+        show_page('totp-login');
37 37
         die();
38 38
     } else {
39 39
         setup_session($role, $_SESSION['totp_username']);
40 40