Subversion Repositories oidplus

Rev

Rev 567 | Rev 572 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 daniel-mar 1
<?php
2
 
3
/*
4
 * OIDplus 2.0
511 daniel-mar 5
 * Copyright 2019 - 2021 Daniel Marschall, ViaThinkSoft
2 daniel-mar 6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
 
511 daniel-mar 20
if (!defined('INSIDE_OIDPLUS')) die();
21
 
2 daniel-mar 22
class OIDplusAuthUtils {
23
 
566 daniel-mar 24
        // Useful functions
25
 
457 daniel-mar 26
        public static function getRandomBytes($len) {
27
                if (function_exists('openssl_random_pseudo_bytes')) {
28
                        $a = openssl_random_pseudo_bytes($len);
29
                        if ($a) return $a;
30
                }
31
 
465 daniel-mar 32
                if (function_exists('mcrypt_create_iv')) {
466 daniel-mar 33
                        $a = bin2hex(mcrypt_create_iv($len, MCRYPT_DEV_URANDOM));
465 daniel-mar 34
                        if ($a) return $a;
35
                }
36
 
457 daniel-mar 37
                if (function_exists('random_bytes')) {
38
                        $a = random_bytes($len);
39
                        if ($a) return $a;
40
                }
41
 
42
                // Fallback to non-secure RNG
43
                $a = '';
44
                while (strlen($a) < $len*2) {
45
                        $a .= sha1(uniqid(mt_rand(), true));
46
                }
47
                $a = substr($a, 0, $len*2);
48
                return hex2bin($a);
49
        }
50
 
566 daniel-mar 51
        // Content provider
2 daniel-mar 52
 
566 daniel-mar 53
        protected function getAuthContentStore() {
54
                static $contentProvider = null;
2 daniel-mar 55
 
566 daniel-mar 56
                if (is_null($contentProvider)) {
57
                        if (isset($_REQUEST['OIDPLUS_AUTH_JWT'])) {
570 daniel-mar 58
                                // Automated AJAX requests (new version)
566 daniel-mar 59
                                $contentProvider = new OIDplusAuthContentStoreJWT();
60
                                $contentProvider->loadJWT($_REQUEST['OIDPLUS_AUTH_JWT']);
61
                        } else if (isset($_REQUEST['batch_ajax_unlock_key'])) {
570 daniel-mar 62
                                // Automated AJAX requests (backwards compatibility)
63
                                // The login procedure will be performaned in RA plugin 910 and Admin plugin 910
566 daniel-mar 64
                                $contentProvider = new OIDplusAuthContentStoreDummy();
65
                        } else {
570 daniel-mar 66
                                // Normal login via web-browser
566 daniel-mar 67
                                $contentProvider = new OIDplusAuthContentStoreSession();
68
                        }
69
                }
2 daniel-mar 70
 
566 daniel-mar 71
                return $contentProvider;
2 daniel-mar 72
        }
73
 
566 daniel-mar 74
        // RA authentication functions
2 daniel-mar 75
 
566 daniel-mar 76
        public function raLogin($email) {
77
                return $this->getAuthContentStore()->raLogin($email);
78
        }
2 daniel-mar 79
 
566 daniel-mar 80
        public function raLogout($email) {
81
                return $this->getAuthContentStore()->raLogout($email);
2 daniel-mar 82
        }
83
 
566 daniel-mar 84
        public function raCheckPassword($ra_email, $password) {
329 daniel-mar 85
                $ra = new OIDplusRA($ra_email);
453 daniel-mar 86
 
459 daniel-mar 87
                $authInfo = $ra->getAuthInfo();
453 daniel-mar 88
 
89
                $plugins = OIDplus::getAuthPlugins();
90
                if (count($plugins) == 0) {
91
                        throw new OIDplusException(_L('No RA authentication plugins found'));
92
                }
93
                foreach ($plugins as $plugin) {
459 daniel-mar 94
                        if ($plugin->verify($authInfo, $password)) return true;
453 daniel-mar 95
                }
96
 
97
                return false;
329 daniel-mar 98
        }
99
 
566 daniel-mar 100
        public function raNumLoggedIn() {
101
                return $this->getAuthContentStore()->raNumLoggedIn();
85 daniel-mar 102
        }
103
 
566 daniel-mar 104
        public function raLogoutAll() {
105
                return $this->getAuthContentStore()->raLogoutAll();
2 daniel-mar 106
        }
107
 
566 daniel-mar 108
        public function loggedInRaList() {
567 daniel-mar 109
                if (OIDplus::authUtils()->forceAllLoggedOut()) {
110
                        return array();
111
                } else {
112
                        return $this->getAuthContentStore()->loggedInRaList();
113
                }
2 daniel-mar 114
        }
115
 
566 daniel-mar 116
        public function isRaLoggedIn($email) {
117
                return $this->getAuthContentStore()->isRaLoggedIn($email);
2 daniel-mar 118
        }
119
 
14 daniel-mar 120
        // Admin authentication functions
2 daniel-mar 121
 
566 daniel-mar 122
        public function adminLogin() {
123
                return $this->getAuthContentStore()->adminLogin();
2 daniel-mar 124
        }
125
 
566 daniel-mar 126
        public function adminLogout() {
127
                return $this->getAuthContentStore()->adminLogout();
2 daniel-mar 128
        }
129
 
566 daniel-mar 130
        public function adminCheckPassword($password) {
421 daniel-mar 131
                $passwordData = OIDplus::baseConfig()->getValue('ADMIN_PASSWORD', '');
132
                if (empty($passwordData)) {
360 daniel-mar 133
                        throw new OIDplusException(_L('No admin password set in %1','userdata/baseconfig/config.inc.php'));
261 daniel-mar 134
                }
456 daniel-mar 135
 
421 daniel-mar 136
                if (strpos($passwordData, '$') !== false) {
456 daniel-mar 137
                        if ($passwordData[0] == '$') {
138
                                // Version 3: BCrypt
139
                                return password_verify($password, $passwordData);
140
                        } else {
457 daniel-mar 141
                                // Version 2: SHA3-512 with salt
456 daniel-mar 142
                                list($s_salt, $hash) = explode('$', $passwordData, 2);
143
                        }
421 daniel-mar 144
                } else {
456 daniel-mar 145
                        // Version 1: SHA3-512 without salt
421 daniel-mar 146
                        $s_salt = '';
147
                        $hash = $passwordData;
148
                }
149
                return strcmp(sha3_512($s_salt.$password, true), base64_decode($hash)) === 0;
2 daniel-mar 150
        }
151
 
566 daniel-mar 152
        public function isAdminLoggedIn() {
567 daniel-mar 153
                if (OIDplus::authUtils()->forceAllLoggedOut()) {
154
                        return false;
155
                } else {
156
                        return $this->getAuthContentStore()->isAdminLoggedIn();
157
                }
2 daniel-mar 158
        }
159
 
310 daniel-mar 160
        // Authentication keys for validating arguments (e.g. sent by mail)
2 daniel-mar 161
 
162
        public static function makeAuthKey($data) {
424 daniel-mar 163
                $data = OIDplus::baseConfig()->getValue('SERVER_SECRET') . '/AUTHKEY/' . $data;
421 daniel-mar 164
                $calc_authkey = sha3_512($data, false);
2 daniel-mar 165
                return $calc_authkey;
166
        }
167
 
168
        public static function validateAuthKey($data, $auth_key) {
421 daniel-mar 169
                return strcmp(self::makeAuthKey($data), $auth_key) === 0;
2 daniel-mar 170
        }
171
 
329 daniel-mar 172
        // "Veto" functions to force logout state
173
 
174
        public static function forceAllLoggedOut() {
175
                if (isset($_SERVER['SCRIPT_FILENAME']) && (basename($_SERVER['SCRIPT_FILENAME']) == 'sitemap.php')) {
176
                        // The sitemap may not contain any confidential information,
177
                        // even if the user is logged in, because the admin could
178
                        // accidentally copy-paste the sitemap to a
179
                        // search engine control panel while they are logged in
180
                        return true;
181
                } else {
182
                        return false;
183
                }
184
        }
427 daniel-mar 185
 
424 daniel-mar 186
        // CSRF functions
427 daniel-mar 187
 
424 daniel-mar 188
        private $enable_csrf = true;
427 daniel-mar 189
 
424 daniel-mar 190
        public function enableCSRF() {
191
                $this->enable_csrf = true;
192
        }
427 daniel-mar 193
 
424 daniel-mar 194
        public function disableCSRF() {
195
                $this->enable_csrf = false;
196
        }
427 daniel-mar 197
 
424 daniel-mar 198
        public function genCSRFToken() {
564 daniel-mar 199
                return bin2hex(self::getRandomBytes(64));
424 daniel-mar 200
        }
427 daniel-mar 201
 
424 daniel-mar 202
        public function checkCSRF() {
427 daniel-mar 203
                if (!$this->enable_csrf) return;
564 daniel-mar 204
                if (!isset($_REQUEST['csrf_token']) || !isset($_COOKIE['csrf_token']) || ($_REQUEST['csrf_token'] !== $_COOKIE['csrf_token'])) {
205
                        throw new OIDplusException(_L('Wrong CSRF Token'));
424 daniel-mar 206
                }
207
        }
329 daniel-mar 208
 
421 daniel-mar 209
        // Generate RA passwords
210
 
461 daniel-mar 211
        public static function raGeneratePassword($password): OIDplusRAAuthInfo {
453 daniel-mar 212
                $def_method = OIDplus::config()->getValue('default_ra_auth_method');
213
 
214
                $plugins = OIDplus::getAuthPlugins();
215
                foreach ($plugins as $plugin) {
216
                        if (basename($plugin->getPluginDirectory()) === $def_method) {
217
                                return $plugin->generate($password);
218
                        }
219
                }
220
                throw new OIDplusException(_L('Default RA auth method/plugin "%1" not found',$def_method));
421 daniel-mar 221
        }
222
 
223
        // Generate admin password
224
 
225
        /* Nothing here; the admin password will be generated in setup_base.js */
226
 
227
}