Passed
Push — master ( 87388f...f2fa31 )
by Nils
11:05
created

authenticateThroughAD()   F

Complexity

Conditions 19
Paths 36

Size

Total Lines 186
Code Lines 126

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 19
eloc 126
c 1
b 0
f 0
nc 36
nop 4
dl 0
loc 186
rs 3.6133

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Teampass - a collaborative passwords manager.
7
 * ---
8
 * This library is distributed in the hope that it will be useful,
9
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11
 * ---
12
 *
13
 * @project   Teampass
14
 *
15
 * @file      identify.php
16
 * ---
17
 *
18
 * @author    Nils Laumaillé ([email protected])
19
 *
20
 * @copyright 2009-2022 Teampass.net
21
 *
22
 * @license   https://spdx.org/licenses/GPL-3.0-only.html#licenseText GPL-3.0
23
 * ---
24
 *
25
 * @see       https://www.teampass.net
26
 */
27
28
use LdapRecord\Connection;
29
use LdapRecord\Container;
30
31
require_once 'SecureHandler.php';
32
session_name('teampass_session');
33
session_start();
34
if (isset($_SESSION['CPM']) === false || (int) $_SESSION['CPM'] !== 1) {
35
    die('Hacking attempt...');
36
}
37
38
// Load config
39
if (file_exists('../includes/config/tp.config.php')) {
40
    include_once '../includes/config/tp.config.php';
41
} elseif (file_exists('./includes/config/tp.config.php')) {
42
    include_once './includes/config/tp.config.php';
43
} else {
44
    throw new Exception("Error file '/includes/config/tp.config.php' not exists", 1);
45
}
46
47
if (! isset($SETTINGS['cpassman_dir']) || empty($SETTINGS['cpassman_dir']) === true || $SETTINGS['cpassman_dir'] === '.') {
48
    $SETTINGS = [];
49
    $SETTINGS['cpassman_dir'] = '..';
50
}
51
52
require_once $SETTINGS['cpassman_dir'] . '/sources/main.functions.php';
53
require_once $SETTINGS['cpassman_dir'] . '/includes/config/include.php';
54
require_once $SETTINGS['cpassman_dir'] . '/includes/config/settings.php';
55
56
// If Debug then clean the files
57
if (DEBUGLDAP === true) {
58
    define('DEBUGLDAPFILE', $SETTINGS['path_to_files_folder'] . '/ldap.debug.txt');
59
    $fp = fopen(DEBUGLDAPFILE, 'w');
60
    fclose($fp);
61
}
62
if (DEBUGDUO === true) {
63
    define('DEBUGDUOFILE', $SETTINGS['path_to_files_folder'] . '/duo.debug.txt');
64
    $fp = fopen(DEBUGDUOFILE, 'w');
65
    fclose($fp);
66
}
67
68
// Prepare POST variables
69
$post_type = filter_input(INPUT_POST, 'type', FILTER_SANITIZE_STRING);
70
$post_login = filter_input(INPUT_POST, 'login', FILTER_SANITIZE_STRING);
71
$post_sig_response = filter_input(INPUT_POST, 'sig_response', FILTER_SANITIZE_STRING);
72
//$post_cardid = filter_input(INPUT_POST, 'cardid', FILTER_SANITIZE_STRING);
73
$post_data = filter_input(INPUT_POST, 'data', FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES);
74
75
// connect to the server
76
if (defined('DB_PASSWD_CLEAR') === false) {
77
    define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
78
}
79
require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
80
if (defined('DB_PASSWD_CLEAR') === false) {
81
    define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
82
}
83
DB::$host = DB_HOST;
84
DB::$user = DB_USER;
85
DB::$password = DB_PASSWD_CLEAR;
86
DB::$dbName = DB_NAME;
87
DB::$port = DB_PORT;
88
DB::$encoding = DB_ENCODING;
89
if ($post_type === 'identify_duo_user') {
90
    //--------
91
    // DUO AUTHENTICATION
92
    //--------
93
    // This step creates the DUO request encrypted key
94
95
    // load library
96
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Authentication/DuoSecurity/Duo.php';
97
    $sig_request = Duo::signRequest(
98
        $SETTINGS['IKEY'],
99
        $SETTINGS['SKEY'],
100
        $SETTINGS['AKEY'],
101
        $post_login
102
    );
103
    if (DEBUGDUO === true) {
104
        debugIdentify(
105
            DEBUGDUO,
106
            DEBUGDUOFILE,
107
            "\n\n-----\n\n" .
108
                'sig request : ' . $post_login . "\n" .
109
                'resp : ' . $sig_request . "\n"
110
        );
111
    }
112
113
    // load csrfprotector
114
    $csrfp_config = include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/csrfp/libs/csrfp.config.php';
115
    // return result
116
    echo '[{"sig_request" : "' . $sig_request . '" , "csrfp_token" : "' . $csrfp_config['CSRFP_TOKEN'] . '" , "csrfp_key" : "' . filter_var($_COOKIE[$csrfp_config['CSRFP_TOKEN']], FILTER_SANITIZE_STRING) . '"}]';
117
// ---
118
    // ---
119
} elseif ($post_type === 'identify_duo_user_check') {
120
    //--------
121
    // DUO AUTHENTICATION
122
    // this step is verifying the response received from the server
123
    //--------
124
125
    // load library
126
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Authentication/DuoSecurity/Duo.php';
127
    $authenticated_username = Duo::verifyResponse(
128
        $SETTINGS['duo_ikey'],
129
        $SETTINGS['duo_skey'],
130
        $SETTINGS['duo_akey'],
131
        $post_sig_response
132
    );
133
134
    // return the response (which should be the user name)
135
    if ($authenticated_username === $post_login) {
136
        // Check if this account exists in Teampass or only in LDAP
137
        if (isset($SETTINGS['ldap_mode']) === true && (int) $SETTINGS['ldap_mode'] === 1) {
138
            // is user in Teampass?
139
            DB::queryfirstrow(
140
                'SELECT id
141
                FROM ' . prefixTable('users') . '
142
                WHERE login = %s',
143
                $post_login
144
            );
145
            if (DB::count() === 0) {
146
                // Ask your administrator to create your account in Teampass
147
                echo '[{"authenticated_username" : "ERR|This user is not yet imported inside Teampass."}]';
148
            }
149
        }
150
151
        echo '[{"authenticated_username" : "' . $authenticated_username . '"}]';
152
    } else {
153
        echo '[{"authenticated_username" : "' . $authenticated_username . '"}]';
154
    }
155
    // ---
156
    // ---
157
} elseif ($post_type === 'identify_user') {
158
    //--------
159
    // NORMAL IDENTICATION STEP
160
    //--------
161
162
    // Ensure Complexity levels are translated
163
    if (defined('TP_PW_COMPLEXITY') === false) {
164
        define(
165
            'TP_PW_COMPLEXITY',
166
            array(
167
                TP_PW_STRENGTH_1 => array(TP_PW_STRENGTH_1, langHdl('complex_level1'), 'fas fa-thermometer-empty text-danger'),
168
                TP_PW_STRENGTH_2 => array(TP_PW_STRENGTH_2, langHdl('complex_level2'), 'fas fa-thermometer-quarter text-warning'),
169
                TP_PW_STRENGTH_3 => array(TP_PW_STRENGTH_3, langHdl('complex_level3'), 'fas fa-thermometer-half text-warning'),
170
                TP_PW_STRENGTH_4 => array(TP_PW_STRENGTH_4, langHdl('complex_level4'), 'fas fa-thermometer-three-quarters text-success'),
171
                TP_PW_STRENGTH_5 => array(TP_PW_STRENGTH_5, langHdl('complex_level5'), 'fas fa-thermometer-full text-success'),
172
            )
173
        );
174
    }
175
176
    // Load superGlobals
177
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
178
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
179
    // Prepare GET variables
180
    $sessionPwdAttempts = $superGlobal->get('pwd_attempts', 'SESSION');
181
    // increment counter of login attempts
182
    if ($sessionPwdAttempts === '') {
183
        $sessionPwdAttempts = 1;
184
    } else {
185
        ++$sessionPwdAttempts;
186
    }
187
188
    $superGlobal->put('pwd_attempts', $sessionPwdAttempts, 'SESSION');
189
    // manage brute force
190
    if ($sessionPwdAttempts <= 3) {
191
        $sessionPwdAttempts = 0;
192
193
        // identify the user through Teampass process
194
        identifyUser(
195
            $post_data,
196
            $SETTINGS
197
        );
198
    } elseif (isset($_SESSION['next_possible_pwd_attempts']) && time() > $_SESSION['next_possible_pwd_attempts'] && $sessionPwdAttempts > 3) {
199
        $sessionPwdAttempts = 0;
200
        // identify the user through Teampass process
201
        identifyUser(
202
            $post_data,
203
            $SETTINGS
204
        );
205
    } else {
206
        $_SESSION['next_possible_pwd_attempts'] = time() + 10;
207
        // Encrypt data to return
208
        echo prepareExchangedData(
209
    $SETTINGS['cpassman_dir'],
210
            [
211
                'value' => 'bruteforce_wait',
212
                'user_admin' => isset($sessionAdmin) ? (int) $sessionAdmin : 0,
213
                'initial_url' => isset($sessionUrl) === true ? $sessionUrl : '',
214
                'pwd_attempts' => (int) $sessionPwdAttempts,
215
                'error' => true,
216
                'message' => langHdl('error_bad_credentials_more_than_3_times'),
217
            ],
218
            'encode'
219
        );
220
        return false;
221
    }
222
    // ---
223
    // ---
224
    // ---
225
} elseif ($post_type === 'get2FAMethods') {
226
    //--------
227
    // Get MFA methods
228
    //--------
229
    //
230
231
    // Encrypt data to return
232
    echo prepareExchangedData(
233
    $SETTINGS['cpassman_dir'],
234
        [
235
            'agses' => isset($SETTINGS['agses_authentication_enabled']) === true
236
                && (int) $SETTINGS['agses_authentication_enabled'] === 1 ? true : false,
237
            'google' => isset($SETTINGS['google_authentication']) === true
238
                && (int) $SETTINGS['google_authentication'] === 1 ? true : false,
239
            'yubico' => isset($SETTINGS['yubico_authentication']) === true
240
                && (int) $SETTINGS['yubico_authentication'] === 1 ? true : false,
241
            'duo' => isset($SETTINGS['duo']) === true
242
                && (int) $SETTINGS['duo'] === 1 ? true : false,
243
        ],
244
        'encode'
245
    );
246
    return false;
247
}
248
249
/**
250
 * Complete authentication of user through Teampass
251
 *
252
 * @param string $sentData Credentials
253
 * @param array $SETTINGS Teampass settings
254
 *
255
 * @return bool
256
 */
257
function identifyUser(string $sentData, array $SETTINGS): bool
258
{
259
    // Load config
260
    if (file_exists('../includes/config/tp.config.php')) {
261
        include_once '../includes/config/tp.config.php';
262
    } elseif (file_exists('./includes/config/tp.config.php')) {
263
        include_once './includes/config/tp.config.php';
264
    } else {
265
        throw new Exception("Error file '/includes/config/tp.config.php' not exists", 1);
266
    }
267
    include_once $SETTINGS['cpassman_dir'] . '/includes/config/settings.php';
268
    include_once $SETTINGS['cpassman_dir'] . '/sources/main.functions.php';
269
    include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php';
270
    
271
    header('Content-type: text/html; charset=utf-8');
272
    error_reporting(E_ERROR);
273
274
    // Load AntiXSS
275
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/voku/helper/AntiXSS.php';
276
    $antiXss = new voku\helper\AntiXSS();
277
278
    // Load superGlobals
279
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
280
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
281
282
    // Prepare GET variables
283
    $sessionUserLanguage = $superGlobal->get('user_language', 'SESSION');
284
    $sessionKey = $superGlobal->get('key', 'SESSION');
285
    $sessionAdmin = $superGlobal->get('user_admin', 'SESSION');
286
    $sessionPwdAttempts = $superGlobal->get('pwd_attempts', 'SESSION');
287
    $sessionUrl = $superGlobal->get('initial_url', 'SESSION');
288
    $server = [];
289
    $server['PHP_AUTH_USER'] = $superGlobal->get('PHP_AUTH_USER', 'SERVER');
290
    $server['PHP_AUTH_PW'] = $superGlobal->get('PHP_AUTH_PW', 'SERVER');
291
292
    // connect to the server
293
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
294
    DB::$host = DB_HOST;
295
    DB::$user = DB_USER;
296
    DB::$password = defined('DB_PASSWD_CLEAR') === false ? defuseReturnDecrypted(DB_PASSWD, $SETTINGS) : DB_PASSWD_CLEAR;
297
    DB::$dbName = DB_NAME;
298
    DB::$port = DB_PORT;
299
    DB::$encoding = DB_ENCODING;
300
    // User's language loading
301
    include_once $SETTINGS['cpassman_dir'] . '/includes/language/' . $sessionUserLanguage . '.php';
302
    
303
    // decrypt and retreive data in JSON format
304
    if (empty($sessionKey) === true) {
305
        $dataReceived = $sentData;
306
    } else {
307
        $dataReceived = prepareExchangedData(
308
    $SETTINGS['cpassman_dir'],$sentData, 'decode', $sessionKey);
309
        $superGlobal->put('key', $sessionKey, 'SESSION');
310
    }
311
312
    // prepare variables    
313
    $userCredentials = identifyGetUserCredentials(
314
        $SETTINGS,
315
        (string) $server['PHP_AUTH_USER'],
316
        (string) $server['PHP_AUTH_PW'],
317
        (string) filter_var($dataReceived['pw'], FILTER_SANITIZE_STRING),
318
        (string) filter_var($dataReceived['login'], FILTER_SANITIZE_STRING)
319
    );
320
    $username = $userCredentials['username'];
321
    $passwordClear = $userCredentials['passwordClear'];
322
323
    // DO initial checks
324
    $userInitialData = identifyDoInitialChecks(
325
        $SETTINGS,
326
        (int) $sessionPwdAttempts,
327
        (string) $username,
328
        (int) $sessionAdmin,
329
        (string) $sessionUrl,
330
        (string) filter_var($dataReceived['user_2fa_selection'], FILTER_SANITIZE_STRING)
331
    );
332
    if ($userInitialData['error'] === true) {
333
        echo prepareExchangedData(
334
            $SETTINGS['cpassman_dir'],
335
            $userInitialData['array'],
336
            'encode'
337
        );
338
        return false;
339
    }
340
341
    $userInfo = $userInitialData['userInfo'];
342
    $return = '';
343
344
345
    $userLdap = identifyDoLDAPChecks(
346
        $SETTINGS,
347
        $userInfo,
348
        (string) $username,
349
        (string) $passwordClear,
350
        (int) $sessionAdmin,
351
        (string) $sessionUrl,
352
        (int) $sessionPwdAttempts
353
    );
354
    if ($userLdap['error'] === true) {
355
        echo prepareExchangedData(
356
            $SETTINGS['cpassman_dir'],
357
            $userLdap['array'],
358
            'encode'
359
        );
360
        return false;
361
    }
362
363
    $userPasswordVerified = $userLdap['ldapConnection'];
364
    $ldapConnection = $userLdap['ldapConnection'];
365
    //$retLDAP = $userLdap['retLDAP'];
366
367
    $user_initial_creation_through_ldap = $userLdap['user_initial_creation_through_ldap'];
368
369
    // Check user and password
370
    if ($userPasswordVerified === false && (int) checkCredentials($passwordClear, $userInfo, $dataReceived, $username, $SETTINGS) !== 1) {
371
        echo prepareExchangedData(
372
            $SETTINGS['cpassman_dir'],
373
            [
374
                'value' => '',
375
                'user_admin' => isset($sessionAdmin) ? (int) $sessionAdmin : 0,
376
                'initial_url' => isset($sessionUrl) === true ? $sessionUrl : '',
377
                'pwd_attempts' => (int) $sessionPwdAttempts,
378
                'error' => 'user_not_exists2',
379
                'message' => langHdl('error_bad_credentials'),
380
            ],
381
            'encode'
382
        );
383
        return false;
384
    }
385
386
387
    // Check user against MFA method if selected
388
    $userLdap = identifyDoMFAChecks(
389
        $SETTINGS,
390
        $userInfo,
391
        $dataReceived,
392
        $userInitialData,
393
        (string) $username
394
    );
395
    if ($userLdap['error'] === true) {
396
        echo prepareExchangedData(
397
    $SETTINGS['cpassman_dir'],
398
            $userLdap['mfaData'],
399
            'encode'
400
        );
401
        return false;
402
    }
403
404
    // Manage case where user has initiated Google Auth
405
    if (is_array($userLdap['mfaData']) === true
406
        && array_key_exists('firstTime', $userLdap['mfaData']) === true
407
        && count($userLdap['mfaData']['firstTime']) > 0
408
        && $userInitialData['user_mfa_mode'] === 'google'
409
    ) {
410
        echo prepareExchangedData(
411
    $SETTINGS['cpassman_dir'],
412
            [
413
                'value' => $userLdap['mfaData']['firstTime']['value'],
414
                'user_admin' => isset($sessionAdmin) ? (int) $sessionAdmin : 0,
415
                'initial_url' => isset($sessionUrl) === true ? $sessionUrl : '',
416
                'pwd_attempts' => (int) $sessionPwdAttempts,
417
                'error' => false,
418
                'message' => $userLdap['mfaData']['firstTime']['message'],
419
                'mfaStatus' => $userLdap['mfaData']['firstTime']['mfaStatus'],
420
            ],
421
            'encode'
422
        );
423
        return false;
424
    }
425
426
    // Manage DUO auth
427
428
    // Can connect if
429
    // 1- no LDAP mode + user enabled + pw ok
430
    // 2- LDAP mode + user enabled + ldap connection ok + user is not admin
431
    // 3- LDAP mode + user enabled + pw ok + usre is admin
432
    // This in order to allow admin by default to connect even if LDAP is activated
433
    if ((isset($SETTINGS['ldap_mode']) === true && (int) $SETTINGS['ldap_mode'] === 0
434
            && (int) $userInfo['disabled'] === 0)
435
        || (isset($SETTINGS['ldap_mode']) === true && (int) $SETTINGS['ldap_mode'] === 1
436
            && $ldapConnection === true && (int) $userInfo['disabled'] === 0 && $username !== 'admin')
437
        || (isset($SETTINGS['ldap_mode']) === true && (int) $SETTINGS['ldap_mode'] === 2
438
            && $ldapConnection === true && (int) $userInfo['disabled'] === 0 && $username !== 'admin')
439
        || (isset($SETTINGS['ldap_mode']) === true && (int) $SETTINGS['ldap_mode'] === 1
440
            && $username === 'admin' && (int) $userInfo['disabled'] === 0)
441
        || (isset($SETTINGS['ldap_and_local_authentication']) === true && (int) $SETTINGS['ldap_and_local_authentication'] === 1
442
            && isset($SETTINGS['ldap_mode']) === true && in_array($SETTINGS['ldap_mode'], ['1', '2']) === true
443
            && (int) $userInfo['disabled'] === 0)
444
    ) {
445
        $superGlobal->put('autoriser', true, 'SESSION');
446
        $superGlobal->put('pwd_attempts', 0, 'SESSION');
447
        /*// Debug
448
                debugIdentify(
449
                    DEBUGDUO,
450
                    DEBUGDUOFILE,
451
                    "User's token: " . $key . "\n"
452
                );*/
453
454
        // Check if any unsuccessfull login tries exist
455
        $rows = DB::query(
456
            'SELECT date
457
            FROM ' . prefixTable('log_system') . "
458
            WHERE field_1 = %s
459
            AND type = 'failed_auth'
460
            AND label = 'password_is_not_correct'
461
            AND date >= %s AND date < %s",
462
            $userInfo['login'],
463
            $userInfo['last_connexion'],
464
            time()
465
        );
466
        $arrAttempts = [];
467
        if (DB::count() > 0) {
468
            foreach ($rows as $record) {
469
                array_push(
470
                    $arrAttempts,
471
                    date($SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'], (int) $record['date'])
472
                );
473
            }
474
        }
475
        $superGlobal->put('unsuccessfull_login_attempts_list', $arrAttempts, 'SESSION', 'user');
476
        $superGlobal->put('unsuccessfull_login_attempts_shown', DB::count() === 0 ? true : false, 'SESSION', 'user');
477
        $superGlobal->put('unsuccessfull_login_attempts_nb', DB::count(), 'SESSION', 'user');
478
        // Log into DB the user's connection
479
        (isset($SETTINGS['log_connections']) === true
480
        && (int) $SETTINGS['log_connections'] === 1) ?
481
        logEvents($SETTINGS, 'user_connection', 'connection', (string) $userInfo['id'], stripslashes($username)) 
482
        : '';
483
            
484
        // Save account in SESSION
485
        $superGlobal->put('login', stripslashes($username), 'SESSION');
486
        $superGlobal->put('name', empty($userInfo['name']) === false ? stripslashes($userInfo['name']) : '', 'SESSION');
487
        $superGlobal->put('lastname', empty($userInfo['lastname']) === false ? stripslashes($userInfo['lastname']) : '', 'SESSION');
488
        $superGlobal->put('user_id', (int) $userInfo['id'], 'SESSION');
489
        $superGlobal->put('user_pwd', $passwordClear, 'SESSION');
490
        $superGlobal->put('admin', $userInfo['admin'], 'SESSION');
491
        $superGlobal->put('user_manager', $userInfo['gestionnaire'], 'SESSION');
492
        $superGlobal->put('user_can_manage_all_users', $userInfo['can_manage_all_users'], 'SESSION');
493
        $superGlobal->put('user_read_only', $userInfo['read_only'], 'SESSION');
494
        $superGlobal->put('last_pw_change', (int) $userInfo['last_pw_change'], 'SESSION');
495
        $superGlobal->put('last_pw', $userInfo['last_pw'], 'SESSION');
496
        $superGlobal->put('can_create_root_folder', $userInfo['can_create_root_folder'], 'SESSION');
497
        $superGlobal->put('personal_folder', $userInfo['personal_folder'], 'SESSION');
498
        $superGlobal->put('user_language', $userInfo['user_language'], 'SESSION');
499
        $superGlobal->put('user_email', $userInfo['email'], 'SESSION');
500
        $superGlobal->put('user_ga', $userInfo['ga'], 'SESSION');
501
        $superGlobal->put('user_avatar', $userInfo['avatar'], 'SESSION');
502
        $superGlobal->put('user_avatar_thumb', $userInfo['avatar_thumb'], 'SESSION');
503
        $superGlobal->put('user_upgrade_needed', $userInfo['upgrade_needed'], 'SESSION');
504
        $superGlobal->put('user_force_relog', $userInfo['force-relog'], 'SESSION');
505
        $superGlobal->put(
506
            'user_treeloadstrategy',
507
            (isset($userInfo['treeloadstrategy']) === false || empty($userInfo['treeloadstrategy']) === true) ? 'full' : $userInfo['treeloadstrategy'],
508
            'SESSION',
509
            'user'
510
        );
511
        $superGlobal->put('user_treeloadstrategy', $userInfo['treeloadstrategy'], 'SESSION', 'user');
512
        $superGlobal->put('user_agsescardid', $userInfo['agses-usercardid'], 'SESSION', 'user');
513
        $superGlobal->put('user_language', $userInfo['user_language'], 'SESSION', 'user');
514
        $superGlobal->put('user_timezone', $userInfo['usertimezone'], 'SESSION', 'user');
515
        $superGlobal->put('session_duration', $dataReceived['duree_session'] * 60, 'SESSION', 'user');
516
        $superGlobal->put('api-key', $userInfo['user_api_key'], 'SESSION', 'user');
517
        $superGlobal->put('special', $userInfo['special'], 'SESSION', 'user');
518
        $superGlobal->put('auth_type', $userInfo['auth_type'], 'SESSION', 'user');
519
        // manage session expiration
520
        $superGlobal->put('sessionDuration', (int) (time() + ($dataReceived['duree_session'] * 60)), 'SESSION');
521
522
        // check feedback regarding user password validity
523
        $return = checkUserPasswordValidity(
524
            $userInfo,
525
            $superGlobal->get('numDaysBeforePwExpiration', 'SESSION'),
526
            $superGlobal->get('last_pw_change', 'SESSION'),
527
            $SETTINGS
528
        );
529
        $superGlobal->put('validite_pw', $return['validite_pw'], 'SESSION');
530
        $superGlobal->put('last_pw_change', $return['last_pw_change'], 'SESSION');
531
        $superGlobal->put('numDaysBeforePwExpiration', $return['numDaysBeforePwExpiration'], 'SESSION');
532
        $superGlobal->put('user_force_relog', $return['user_force_relog'], 'SESSION');
533
534
535
        $superGlobal->put(
536
            'last_connection',
537
            empty($userInfo['last_connexion']) === false ? (int) $userInfo['last_connexion'] : (int) time(),
538
            'SESSION'
539
        );
540
        
541
        $superGlobal->put(
542
            'latest_items',
543
            empty($userInfo['latest_items']) === false ? explode(';', $userInfo['latest_items']) : [],
544
            'SESSION'
545
        );
546
        
547
        $superGlobal->put(
548
            'favourites',
549
            empty($userInfo['favourites']) === false ? explode(';', $userInfo['favourites']) : [],
550
            'SESSION'
551
        );
552
        
553
        $superGlobal->put(
554
            'groupes_visibles',
555
            empty($userInfo['groupes_visibles']) === false ? explode(';', $userInfo['groupes_visibles']) : [],
556
            'SESSION'
557
        );
558
        
559
        $superGlobal->put(
560
            'no_access_folders',
561
            empty($userInfo['groupes_interdits']) === false ? explode(';', $userInfo['groupes_interdits']) : [],
562
            'SESSION'
563
        );
564
        
565
        // User's roles
566
        if (strpos($userInfo['fonction_id'] !== NULL ? (string) $userInfo['fonction_id'] : '', ',') !== -1) {
567
            // Convert , to ;
568
            $userInfo['fonction_id'] = str_replace(',', ';', (string) $userInfo['fonction_id']);
569
            DB::update(
570
                prefixTable('users'),
571
                [
572
                    'fonction_id' => $userInfo['fonction_id'],
573
                ],
574
                'id = %i',
575
                $superGlobal->get('user_id', 'SESSION')
576
            );
577
        }
578
        $superGlobal->put('fonction_id', $userInfo['fonction_id'], 'SESSION');
579
        $superGlobal->put('user_roles', array_filter(explode(';', $userInfo['fonction_id'])), 'SESSION');
580
        // build array of roles
581
        $superGlobal->put('user_pw_complexity', 0, 'SESSION');
582
        $superGlobal->put('arr_roles', [], 'SESSION');
583
        foreach ($superGlobal->get('user_roles', 'SESSION') as $role) {
584
            $resRoles = DB::queryFirstRow(
585
                'SELECT title, complexity
586
                FROM ' . prefixTable('roles_title') . '
587
                WHERE id=%i',
588
                $role
589
            );
590
            $superGlobal->put(
591
                $role,
592
                [
593
                    'id' => $role,
594
                    'title' => $resRoles['title'],
595
                ],
596
                'SESSION',
597
                'arr_roles'
598
            );
599
            // get highest complexity
600
            if (intval($superGlobal->get('user_pw_complexity', 'SESSION')) < intval($resRoles['complexity'])) {
601
                $superGlobal->put('user_pw_complexity', $resRoles['complexity'], 'SESSION');
602
            }
603
        }
604
605
        // build complete array of roles
606
        $superGlobal->put('arr_roles_full', [], 'SESSION');
607
        $rows = DB::query('SELECT id, title FROM ' . prefixTable('roles_title') . ' ORDER BY title ASC');
608
        foreach ($rows as $record) {
609
            $superGlobal->put(
610
                $record['id'],
611
                [
612
                    'id' => $record['id'],
613
                    'title' => $record['title'],
614
                ],
615
                'SESSION',
616
                'arr_roles_full'
617
            );
618
        }
619
        // Set some settings
620
        $SETTINGS['update_needed'] = '';
621
622
        // User signature keys
623
        $returnKeys = prepareUserEncryptionKeys($userInfo, $passwordClear);        
624
        $superGlobal->put('public_key', $returnKeys['public_key'], 'SESSION', 'user');
625
        $superGlobal->put('private_key', $returnKeys['private_key_clear'], 'SESSION', 'user');
626
627
        // Update table
628
        DB::update(
629
            prefixTable('users'),
630
            array_merge(
631
                [
632
                    'key_tempo' => $superGlobal->get('key', 'SESSION'),
633
                    'last_connexion' => time(),
634
                    'timestamp' => time(),
635
                    'disabled' => 0,
636
                    'no_bad_attempts' => 0,
637
                    'session_end' => $superGlobal->get('sessionDuration', 'SESSION'),
638
                    'user_ip' => $dataReceived['client'],
639
                ],
640
                $returnKeys['update_keys_in_db']
641
            ),
642
            'id=%i',
643
            $userInfo['id']
644
        );
645
        // Debug
646
        /*debugIdentify(
647
            DEBUGDUO,
648
            DEBUGDUOFILE,
649
            "Preparing to identify the user rights\n"
650
        );
651
        */
652
        // Get user's rights
653
        if ($user_initial_creation_through_ldap !== false) {
654
            // is new LDAP user. Show only his personal folder
655
            if ($SETTINGS['enable_pf_feature'] === '1') {
656
                $superGlobal->put('personal_visible_groups', [$userInfo['id']], 'SESSION');
657
                $superGlobal->put('personal_folders', [$userInfo['id']], 'SESSION');
658
            } else {
659
                $superGlobal->put('personal_visible_groups', [], 'SESSION');
660
                $superGlobal->put('personal_folders', [], 'SESSION');
661
            }
662
            $superGlobal->put('all_non_personal_folders', [], 'SESSION');
663
            $superGlobal->put('groupes_visibles', [], 'SESSION');
664
            $superGlobal->put('read_only_folders', [], 'SESSION');
665
            $superGlobal->put('list_folders_limited', '', 'SESSION');
666
            $superGlobal->put('list_folders_editable_by_role', [], 'SESSION');
667
            $superGlobal->put('list_restricted_folders_for_items', [], 'SESSION');
668
            $superGlobal->put('nb_folders', 1, 'SESSION');
669
            $superGlobal->put('nb_roles', 0, 'SESSION');
670
        } else {
671
            identifyUserRights(
672
                $userInfo['groupes_visibles'],
673
                $superGlobal->get('no_access_folders', 'SESSION'),
674
                $userInfo['admin'],
675
                $userInfo['fonction_id'],
676
                $SETTINGS
677
            );
678
        }
679
        // Get some more elements
680
        $superGlobal->put('screenHeight', $dataReceived['screenHeight'], 'SESSION');
681
        // Get last seen items
682
        $superGlobal->put('latest_items_tab', [], 'SESSION');
683
        $superGlobal->put('nb_roles', 0, 'SESSION');
684
        foreach ($superGlobal->get('latest_items', 'SESSION') as $item) {
685
            if (! empty($item)) {
686
                $dataLastItems = DB::queryFirstRow(
687
                    'SELECT id,label,id_tree
688
                    FROM ' . prefixTable('items') . '
689
                    WHERE id=%i',
690
                    $item
691
                );
692
                $superGlobal->put(
693
                    $item,
694
                    [
695
                        'id' => $item,
696
                        'label' => $dataLastItems['label'],
697
                        'url' => 'index.php?page=items&amp;group=' . $dataLastItems['id_tree'] . '&amp;id=' . $item,
698
                    ],
699
                    'SESSION',
700
                    'latest_items_tab'
701
                );
702
            }
703
        }
704
        // send back the random key
705
        $return = $dataReceived['randomstring'];
706
        // Send email
707
        if (
708
            isset($SETTINGS['enable_send_email_on_user_login'])
709
            && (int) $SETTINGS['enable_send_email_on_user_login'] === 1
710
            && (int) $sessionAdmin !== 1
711
        ) {
712
            // get all Admin users
713
            $receivers = '';
714
            $rows = DB::query('SELECT email FROM ' . prefixTable('users') . " WHERE admin = %i and email != ''", 1);
715
            foreach ($rows as $record) {
716
                if (empty($receivers)) {
717
                    $receivers = $record['email'];
718
                } else {
719
                    $receivers = ',' . $record['email'];
720
                }
721
            }
722
            // Add email to table
723
            DB::insert(
724
                prefixTable('emails'),
725
                [
726
                    'timestamp' => time(),
727
                    'subject' => langHdl('email_subject_on_user_login'),
728
                    'body' => str_replace(
729
                        [
730
                            '#tp_user#',
731
                            '#tp_date#',
732
                            '#tp_time#',
733
                        ],
734
                        [
735
                            ' ' . $superGlobal->get('login', 'SESSION') . ' (IP: ' . getClientIpServer() . ')',
736
                            date($SETTINGS['date_format'], (int) $superGlobal->get('last_connection', 'SESSION')),
737
                            date($SETTINGS['time_format'], (int) $superGlobal->get('last_connection', 'SESSION')),
738
                        ],
739
                        langHdl('email_body_on_user_login')
740
                    ),
741
                    'receivers' => $receivers,
742
                    'status' => 'not_sent',
743
                ]
744
            );
745
        }
746
747
        // Ensure Complexity levels are translated
748
        if (defined('TP_PW_COMPLEXITY') === false) {
749
            define(
750
                'TP_PW_COMPLEXITY',
751
                [
752
                    TP_PW_STRENGTH_1 => array(TP_PW_STRENGTH_1, langHdl('complex_level1'), 'fas fa-thermometer-empty text-danger'),
753
                    TP_PW_STRENGTH_2 => array(TP_PW_STRENGTH_2, langHdl('complex_level2'), 'fas fa-thermometer-quarter text-warning'),
754
                    TP_PW_STRENGTH_3 => array(TP_PW_STRENGTH_3, langHdl('complex_level3'), 'fas fa-thermometer-half text-warning'),
755
                    TP_PW_STRENGTH_4 => array(TP_PW_STRENGTH_4, langHdl('complex_level4'), 'fas fa-thermometer-three-quarters text-success'),
756
                    TP_PW_STRENGTH_5 => array(TP_PW_STRENGTH_5, langHdl('complex_level5'), 'fas fa-thermometer-full text-success'),
757
                ]
758
            );
759
        }
760
761
        /*
762
        $sessionUrl = '';
763
        if ($SETTINGS['cpassman_dir'] === '..') {
764
            $SETTINGS['cpassman_dir'] = '.';
765
        }
766
        */
767
    } elseif ((int) $userInfo['disabled'] === 1) {
768
        // User and password is okay but account is locked
769
        echo prepareExchangedData(
770
            $SETTINGS['cpassman_dir'],
771
            [
772
                'value' => $return,
773
                'user_id' => $superGlobal->get('user_id', 'SESSION') !== null ? (int) $superGlobal->get('user_id', 'SESSION') : '',
774
                'user_admin' => $superGlobal->get('admin', 'SESSION') !== null ? (int) $superGlobal->get('admin', 'SESSION') : 0,
775
                'initial_url' => isset($sessionUrl) === true ? $sessionUrl : '',
776
                'pwd_attempts' => 0,
777
                'error' => 'user_is_locked',
778
                'message' => langHdl('account_is_locked'),
779
                'first_connection' => $superGlobal->get('validite_pw', 'SESSION') === false ? true : false,
780
                'password_complexity' => TP_PW_COMPLEXITY[$superGlobal->get('user_pw_complexity', 'SESSION')][1],
781
                'password_change_expected' => $userInfo['special'] === 'password_change_expected' ? true : false,
782
                'private_key_conform' => $superGlobal->get('private_key', 'SESSION', 'user') !== null
783
                    && empty($superGlobal->get('private_key', 'SESSION', 'user')) === false
784
                    && $superGlobal->get('private_key', 'SESSION', 'user') !== 'none' ? true : false,
785
                'session_key' => $superGlobal->get('key', 'SESSION'),
786
                //'has_psk' => empty($superGlobal->get('encrypted_psk', 'SESSION', 'user')) === false ? true : false,
787
                'can_create_root_folder' => $superGlobal->get('can_create_root_folder', 'SESSION') !== null ? (int) $superGlobal->get('can_create_root_folder', 'SESSION') : '',
788
                'shown_warning_unsuccessful_login' => $superGlobal->get('unsuccessfull_login_attempts_shown', 'SESSION', 'user'),
789
                'nb_unsuccessful_logins' => $superGlobal->get('unsuccessfull_login_attempts_nb', 'SESSION', 'user'),
790
            ],
791
            'encode'
792
        );
793
        return false;
794
    } else {
795
        // User exists in the DB but Password is false
796
        // check if user is locked
797
        $userIsLocked = false;
798
        $nbAttempts = intval($userInfo['no_bad_attempts'] + 1);
799
        if (
800
            $SETTINGS['nb_bad_authentication'] > 0
801
            && intval($SETTINGS['nb_bad_authentication']) < $nbAttempts
802
        ) {
803
            $userIsLocked = true;
804
            // log it
805
            if (
806
                isset($SETTINGS['log_connections']) === true
807
                && (int) $SETTINGS['log_connections'] === 1
808
            ) {
809
                logEvents($SETTINGS, 'user_locked', 'connection', (string) $userInfo['id'], stripslashes($username));
810
            }
811
        }
812
        DB::update(
813
            prefixTable('users'),
814
            [
815
                'key_tempo' => $superGlobal->get('key', 'SESSION'),
816
                'disabled' => $userIsLocked,
817
                'no_bad_attempts' => $nbAttempts,
818
            ],
819
            'id=%i',
820
            $userInfo['id']
821
        );
822
        // What return shoulb we do
823
        if ($userIsLocked === true) {
824
            echo prepareExchangedData(
825
                $SETTINGS['cpassman_dir'],
826
                [
827
                    'value' => $return,
828
                    'user_id' => $superGlobal->get('user_id', 'SESSION') !== null ? (int) $superGlobal->get('user_id', 'SESSION') : '',
829
                    'user_admin' => $superGlobal->get('admin', 'SESSION') !== null ? (int) $superGlobal->get('admin', 'SESSION') : 0,
830
                    'initial_url' => isset($sessionUrl) === true ? $sessionUrl : '',
831
                    'pwd_attempts' => 0,
832
                    'error' => 'user_is_locked',
833
                    'message' => langHdl('account_is_locked'),
834
                    'first_connection' => $superGlobal->get('validite_pw', 'SESSION') === false ? true : false,
835
                    'password_complexity' => TP_PW_COMPLEXITY[$superGlobal->get('user_pw_complexity', 'SESSION')][1],
836
                    'password_change_expected' => $userInfo['special'] === 'password_change_expected' ? true : false,
837
                    'private_key_conform' => $superGlobal->get('user_id', 'SESSION') !== null
838
                        && empty($superGlobal->get('private_key', 'SESSION', 'user')) === false
839
                        && $superGlobal->get('private_key', 'SESSION', 'user') !== 'none' ? true : false,
840
                    'session_key' => $superGlobal->get('key', 'SESSION'),
841
                    //'has_psk' => empty($superGlobal->get('encrypted_psk', 'SESSION', 'user')) === false ? true : false,
842
                    'can_create_root_folder' => $superGlobal->get('can_create_root_folder', 'SESSION') !== null ? (int) $superGlobal->get('can_create_root_folder', 'SESSION') : '',
843
                    'shown_warning_unsuccessful_login' => $superGlobal->get('unsuccessfull_login_attempts_shown', 'SESSION', 'user'),
844
                    'nb_unsuccessful_logins' => $superGlobal->get('unsuccessfull_login_attempts_nb', 'SESSION', 'user'),
845
                ],
846
                'encode'
847
            );
848
            return false;
849
        }
850
        echo prepareExchangedData(
851
            $SETTINGS['cpassman_dir'],
852
            [
853
                'value' => $return,
854
                'user_id' => $superGlobal->get('user_id', 'SESSION') !== null ? (int) $superGlobal->get('user_id', 'SESSION') : '',
855
                'user_admin' => $superGlobal->get('admin', 'SESSION') !== null ? (int) $superGlobal->get('admin', 'SESSION') : 0,
856
                'initial_url' => isset($sessionUrl) === true ? $sessionUrl : '',
857
                'pwd_attempts' => (int) $sessionPwdAttempts,
858
                'error' => 'user_not_exists3',
859
                'message' => langHdl('error_bad_credentials'),
860
                'first_connection' => $superGlobal->get('validite_pw', 'SESSION') === false ? true : false,
861
                'password_complexity' => TP_PW_COMPLEXITY[$superGlobal->get('user_pw_complexity', 'SESSION')][1],
862
                'password_change_expected' => $userInfo['special'] === 'password_change_expected' ? true : false,
863
                'private_key_conform' => $superGlobal->get('user_id', 'SESSION') !== null
864
                        && empty($superGlobal->get('private_key', 'SESSION', 'user')) === false
865
                        && $superGlobal->get('private_key', 'SESSION', 'user') !== 'none' ? true : false,
866
                'session_key' => $superGlobal->get('key', 'SESSION'),
867
                    //'has_psk' => empty($superGlobal->get('encrypted_psk', 'SESSION', 'user')) === false ? true : false,
868
                'can_create_root_folder' => $superGlobal->get('can_create_root_folder', 'SESSION') !== null ? (int) $superGlobal->get('can_create_root_folder', 'SESSION') : '',
869
                'shown_warning_unsuccessful_login' => $superGlobal->get('unsuccessfull_login_attempts_shown', 'SESSION', 'user'),
870
                'nb_unsuccessful_logins' => $superGlobal->get('unsuccessfull_login_attempts_nb', 'SESSION', 'user'),
871
            ],
872
            'encode'
873
        );
874
        return false;
875
    }
876
877
    // Debug
878
    /*
879
    debugIdentify(
880
        DEBUGDUO,
881
        DEBUGDUOFILE,
882
        "\n\n----\n" .
883
            'Identified : ' . filter_var($return, FILTER_SANITIZE_STRING) . "\n\n"
884
    );
885
    */
886
    echo prepareExchangedData(
887
        $SETTINGS['cpassman_dir'],
888
        [
889
            'value' => $return,
890
            'user_id' => $superGlobal->get('user_id', 'SESSION') !== null ? (int) $superGlobal->get('user_id', 'SESSION') : '',
891
            'user_admin' => $superGlobal->get('admin', 'SESSION') !== null ? (int) $superGlobal->get('admin', 'SESSION') : 0,
892
            'initial_url' => $antiXss->xss_clean($sessionUrl),
893
            'pwd_attempts' => 0,
894
            'error' => false,
895
            'message' => $superGlobal->get('user_upgrade_needed', 'SESSION', 'user') !== null && (int) $superGlobal->get('user_upgrade_needed', 'SESSION', 'user') === 1 ? 'ask_for_otc' : '',
896
            'first_connection' => $superGlobal->get('validite_pw', 'SESSION') === false ? true : false,
897
            'password_complexity' => TP_PW_COMPLEXITY[$superGlobal->get('user_pw_complexity', 'SESSION')][1],
898
            'password_change_expected' => $userInfo['special'] === 'password_change_expected' ? true : false,
899
            'private_key_conform' => $superGlobal->get('user_id', 'SESSION') !== null
900
                && empty($superGlobal->get('private_key', 'SESSION', 'user')) === false
901
                && $superGlobal->get('private_key', 'SESSION', 'user') !== 'none' ? true : false,
902
            'session_key' => $superGlobal->get('key', 'SESSION'),
903
            //'has_psk' => empty($superGlobal->get('encrypted_psk', 'SESSION', 'user')) === false ? true : false,
904
            'can_create_root_folder' => $superGlobal->get('can_create_root_folder', 'SESSION') !== null ? (int) $superGlobal->get('can_create_root_folder', 'SESSION') : '',
905
            'shown_warning_unsuccessful_login' => $superGlobal->get('unsuccessfull_login_attempts_shown', 'SESSION', 'user'),
906
            'nb_unsuccessful_logins' => $superGlobal->get('unsuccessfull_login_attempts_nb', 'SESSION', 'user'),
907
            'upgrade_needed' => isset($userInfo['upgrade_needed']) === true ? (int) $userInfo['upgrade_needed'] : 0,
908
            'special' => isset($userInfo['special']) === true ? (int) $userInfo['special'] : 0,
909
        ],
910
        'encode'
911
    );
912
913
    return true;
914
}
915
916
917
/**
918
 * 
919
 * Prepare user keys
920
 * 
921
 * @param array $userInfo   User account information
922
 * @param string $passwordClear
923
 *
924
 * @return array
925
 */
926
function prepareUserEncryptionKeys($userInfo, $passwordClear) : array
927
{
928
    if (is_null($userInfo['private_key']) === true || empty($userInfo['private_key']) === true || $userInfo['private_key'] === 'none') {
929
        // No keys have been generated yet
930
        // Create them
931
        $userKeys = generateUserKeys($passwordClear);
932
933
        return [
934
            'public_key' => $userKeys['public_key'],
935
            'private_key_clear' => $userKeys['private_key_clear'],
936
            'update_keys_in_db' => [
937
                'public_key' => $userKeys['public_key'],
938
                'private_key' => $userKeys['private_key'],
939
            ],
940
        ];
941
    } 
942
    
943
    if ($userInfo['special'] === 'generate-keys') {
944
        return [
945
            'public_key' => $userInfo['public_key'],
946
            'private_key_clear' => '',
947
            'update_keys_in_db' => [],
948
        ];
949
    }
950
    
951
    // Don't perform this in case of special login action
952
    if ($userInfo['special'] === 'otc_is_required_on_next_login' || $userInfo['special'] === 'user_added_from_ldap') {
953
        return [
954
            'public_key' => $userInfo['public_key'],
955
            'private_key_clear' => '',
956
            'update_keys_in_db' => [],
957
        ];
958
    }
959
    
960
    // Uncrypt private key
961
    return [
962
        'public_key' => $userInfo['public_key'],
963
        'private_key_clear' => decryptPrivateKey($passwordClear, $userInfo['private_key']),
964
        'update_keys_in_db' => [],
965
    ];
966
}
967
968
969
/**
970
 * CHECK PASSWORD VALIDITY
971
 * Don't take into consideration if LDAP in use
972
 * 
973
 * @param array $userInfo                       User account information
974
 * @param int $numDaysBeforePwExpiration
975
 * @param int $lastPwChange
976
 * @param array $SETTINGS                       Teampass settings
977
 *
978
 * @return array
979
 */
980
function checkUserPasswordValidity($userInfo, $numDaysBeforePwExpiration, $lastPwChange, $SETTINGS)
981
{
982
    if (isset($SETTINGS['ldap_mode']) === true && (int) $SETTINGS['ldap_mode'] === 1) {
983
        return [
984
            'validite_pw' => true,
985
            'last_pw_change' => true,
986
            'user_force_relog' => '',
987
            'numDaysBeforePwExpiration' => '',
988
        ];
989
    }
990
991
    if (isset($userInfo['last_pw_change']) === true) {
992
        if ((int) $SETTINGS['pw_life_duration'] === 0) {
993
            return [
994
                'validite_pw' => true,
995
                'last_pw_change' => '',
996
                'user_force_relog' => 'infinite',
997
                'numDaysBeforePwExpiration' => '',
998
            ];
999
        }
1000
        
1001
        return [
1002
            'validite_pw' => $numDaysBeforePwExpiration <= 0 ? false : true,
1003
            'last_pw_change' => '',
1004
            'user_force_relog' => 'infinite',
1005
            'numDaysBeforePwExpiration' => $SETTINGS['pw_life_duration'] - round(
1006
                (mktime(0, 0, 0, (int) date('m'), (int) date('d'), (int) date('y')) - $lastPwChange) / (24 * 60 * 60)),
1007
        ];
1008
    } else {
1009
        return [
1010
            'validite_pw' => false,
1011
            'last_pw_change' => '',
1012
            'user_force_relog' => '',
1013
            'numDaysBeforePwExpiration' => '',
1014
        ];
1015
    }
1016
}
1017
1018
1019
/**
1020
 * Authenticate a user through AD.
1021
 *
1022
 * @param string $username      Username
1023
 * @param array $userInfo       User account information
1024
 * @param string $passwordClear Password
1025
 * @param array $SETTINGS       Teampass settings
1026
 *
1027
 * @return array
1028
 */
1029
function authenticateThroughAD(string $username, array $userInfo, string $passwordClear, array $SETTINGS): array
0 ignored issues
show
Unused Code introduced by
The parameter $userInfo is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

1029
function authenticateThroughAD(string $username, /** @scrutinizer ignore-unused */ array $userInfo, string $passwordClear, array $SETTINGS): array

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1030
{
1031
    // Load expected libraries
1032
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Illuminate/Contracts/Auth/Authenticatable.php';
1033
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Tightenco/Collect/Support/Traits/EnumeratesValues.php';
1034
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Tightenco/Collect/Support/Traits/Macroable.php';
1035
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Tightenco/Collect/Support/helpers.php';
1036
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Tightenco/Collect/Support/Arr.php';
1037
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Tightenco/Collect/Contracts/Support/Jsonable.php';
1038
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Tightenco/Collect/Contracts/Support/Arrayable.php';
1039
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Tightenco/Collect/Support/Enumerable.php';
1040
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Tightenco/Collect/Support/Collection.php';
1041
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/CarbonTimeZone.php';
1042
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Units.php';
1043
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Week.php';
1044
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Timestamp.php';
1045
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Test.php';
1046
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/ObjectInitialisation.php';
1047
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Serialization.php';
1048
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/IntervalRounding.php';
1049
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Rounding.php';
1050
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Localization.php';
1051
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Options.php';
1052
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Cast.php';
1053
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Mutability.php';
1054
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Modifiers.php';
1055
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Mixin.php';
1056
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Macro.php';
1057
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Difference.php';
1058
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Creator.php';
1059
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Converter.php';
1060
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Comparison.php';
1061
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Boundaries.php';
1062
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Date.php';
1063
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/CarbonInterface.php';
1064
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Carbon.php';
1065
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/LdapRecord/DetectsErrors.php';
1066
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/LdapRecord/Connection.php';
1067
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/LdapRecord/LdapInterface.php';
1068
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/LdapRecord/HandlesConnection.php';
1069
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/LdapRecord/Ldap.php';
1070
    $ad = new SplClassLoader('LdapRecord', '../includes/libraries');
1071
    $ad->register();
1072
1073
    // Build ldap configuration array
1074
    $config = [
1075
        // Mandatory Configuration Options
1076
        'hosts' => [explode(',', $SETTINGS['ldap_hosts'])],
1077
        'base_dn' => $SETTINGS['ldap_bdn'],
1078
        'username' => $SETTINGS['ldap_username'],
1079
        'password' => $SETTINGS['ldap_password'],
1080
1081
        // Optional Configuration Options
1082
        'port' => $SETTINGS['ldap_port'],
1083
        'use_ssl' => (int) $SETTINGS['ldap_ssl'] === 1 ? true : false,
1084
        'use_tls' => (int) $SETTINGS['ldap_tls'] === 1 ? true : false,
1085
        'version' => 3,
1086
        'timeout' => 5,
1087
        'follow_referrals' => false,
1088
1089
        // Custom LDAP Options
1090
        'options' => [
1091
            // See: http://php.net/ldap_set_option
1092
            LDAP_OPT_X_TLS_REQUIRE_CERT => LDAP_OPT_X_TLS_HARD,
1093
        ],
1094
    ];
1095
    //prepare connection
1096
    $connection = new Connection($config);
1097
    // Connect to LDAP
1098
    try {
1099
        $connection->connect();
1100
    } catch (\LdapRecord\Auth\BindException $e) {
1101
        $error = $e->getDetailedError();
1102
        return [
1103
            'error' => true,
1104
            'message' => langHdl('error').' : '.$error->getErrorCode().' - '.$error->getErrorMessage(). '<br>'.$error->getDiagnosticMessage(),
1105
1106
        ];
1107
    }
1108
1109
    // Get user info from AD
1110
    // We want to isolate attribute ldap_user_attribute
1111
    try {
1112
        $userADInfos = $connection->query()
1113
            ->where((isset($SETTINGS['ldap_user_attribute']) ===true && empty($SETTINGS['ldap_user_attribute']) === false) ? $SETTINGS['ldap_user_attribute'] : 'distinguishedname', '=', $username)
1114
            ->firstOrFail();
1115
        //print_r($userADInfos);
1116
    } catch (\LdapRecord\LdapRecordException $e) {
1117
        $error = $e->getDetailedError();
1118
1119
        return [
1120
            'error' => true,
1121
            'message' => langHdl('error').' : '.$error->getErrorMessage(). '<br>'.$error->getDiagnosticMessage(),
1122
        ];
1123
    }
1124
    //echo "ici ";
1125
    try {
1126
        if ($SETTINGS['ldap_type'] === 'ActiveDirectory') {
1127
            $userAuthAttempt = $connection->auth()->attempt(
1128
                $userADInfos[(isset($SETTINGS['ldap_user_dn_attribute']) === true && empty($SETTINGS['ldap_user_dn_attribute']) === false) ? $SETTINGS['ldap_user_dn_attribute'] : 'cn'][0],
1129
                $passwordClear
1130
            );
1131
        } else {
1132
            $userAuthAttempt = $connection->auth()->attempt(
1133
                $userADInfos['dn'],
1134
                $passwordClear
1135
            );
1136
        }
1137
    } catch (\LdapRecord\LdapRecordException $e) {
1138
        $error = $e->getDetailedError();
1139
1140
        return [
1141
            'error' => true,
1142
            'message' => langHdl('error').' : '.$error->getErrorMessage(). '<br>'.$error->getDiagnosticMessage(),
1143
        ];
1144
    }
1145
1146
    if ($userAuthAttempt === true) {
1147
        return [
1148
            'error' => false,
1149
            'message' => "User is successfully authenticated",
1150
        ];
1151
    } else {
1152
        return [
1153
            'error' => true,
1154
            'message' => "Error : User could not be authentificated",
1155
        ];
1156
    }
1157
    
1158
    // Check shadowexpire attribute - if === 1 then user disabled
1159
    if (
0 ignored issues
show
Unused Code introduced by
IfNode is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
1160
        (isset($userADInfos['shadowexpire'][0]) === true && (int) $userADInfos['shadowexpire'][0] === 1)
1161
        ||
1162
        (isset($entry['accountexpires'][0]) === true && (int) $userADInfos['accountexpires'][0] < time())
1163
    ) {
1164
        return [
1165
            'error' => true,
1166
            'message' => langHdl('error_ad_user_expired'),
1167
        ];
1168
    }
1169
1170
    // load passwordLib library
1171
    $pwdlib = new SplClassLoader('PasswordLib', $SETTINGS['cpassman_dir'] . '/includes/libraries');
1172
    $pwdlib->register();
1173
    $pwdlib = new PasswordLib\PasswordLib();
1174
    $hashedPassword = $pwdlib->createPasswordHash($passwordClear);
1175
    //If user has never been connected then erase current pwd with the ldap's one
1176
    if (empty($userInfo['pw']) === true) {
1177
        // Password are similar in Teampass and AD
1178
        DB::update(
1179
            prefixTable('users'),
1180
            [
1181
                'pw' => $hashedPassword,
1182
            ],
1183
            'id = %i',
1184
            $userInfo['id']
1185
        );
1186
    } elseif ($userInfo['special'] === 'user_added_from_ldap') {
1187
        // Case where user has been added from LDAP and never being connected to TP
1188
        DB::update(
1189
            prefixTable('users'),
1190
            array(
1191
                'pw' => $hashedPassword,
1192
            ),
1193
            'id = %i',
1194
            $userInfo['id']
1195
        );
1196
    } elseif ($pwdlib->verifyPasswordHash($passwordClear, $userInfo['pw']) === false) {
1197
        // Case where user is auth by LDAP but his password in Teampass is not synchronized
1198
        // For example when user has changed his password in AD.
1199
        // So we need to update it in Teampass and ask for private key re-encryption
1200
        DB::update(
1201
            prefixTable('users'),
1202
            [
1203
                'pw' => $hashedPassword,
1204
                //'special' => 'auth-pwd-change',
1205
            ],
1206
            'id = %i',
1207
            $userInfo['id']
1208
        );
1209
    }
1210
1211
    $userInfo['pw'] = $hashedPassword;
1212
    return [
1213
        'error' => false,
1214
        'message' => '',
1215
    ];
1216
}
1217
1218
/**
1219
 * Undocumented function.
1220
 *
1221
 * @param string|array|resource $dataReceived Received data
1222
 * @param string                $userInfo     Result of query
1223
 * @param array                 $SETTINGS     Teampass settings
1224
 *
1225
 * @return array
1226
 */
1227
function yubicoMFACheck($dataReceived, string $userInfo, array $SETTINGS): array
1228
{
1229
    // Load superGlobals
1230
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
1231
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
1232
    $sessionAdmin = $superGlobal->get('user_admin', 'SESSION');
1233
    $sessionUrl = $superGlobal->get('initial_url', 'SESSION');
1234
    $sessionPwdAttempts = $superGlobal->get('pwd_attempts', 'SESSION');
1235
    // Init
1236
    $yubico_key = htmlspecialchars_decode($dataReceived['yubico_key']);
1237
    $yubico_user_key = htmlspecialchars_decode($dataReceived['yubico_user_key']);
1238
    $yubico_user_id = htmlspecialchars_decode($dataReceived['yubico_user_id']);
1239
    if (empty($yubico_user_key) === false && empty($yubico_user_id) === false) {
1240
        // save the new yubico in user's account
1241
        DB::update(
1242
            prefixTable('users'),
1243
            [
1244
                'yubico_user_key' => $yubico_user_key,
1245
                'yubico_user_id' => $yubico_user_id,
1246
            ],
1247
            'id=%i',
1248
            $userInfo['id']
1249
        );
1250
    } else {
1251
        // Check existing yubico credentials
1252
        if ($userInfo['yubico_user_key'] === 'none' || $userInfo['yubico_user_id'] === 'none') {
1253
            return [
1254
                'error' => true,
1255
                'value' => '',
1256
                'user_admin' => isset($sessionAdmin) ? (int) $sessionAdmin : '',
1257
                'initial_url' => isset($sessionUrl) === true ? $sessionUrl : '',
1258
                'pwd_attempts' => (int) $sessionPwdAttempts,
1259
                'error' => 'no_user_yubico_credentials',
1260
                'message' => '',
1261
                'proceedIdentification' => false,
1262
            ];
1263
        }
1264
        $yubico_user_key = $userInfo['yubico_user_key'];
1265
        $yubico_user_id = $userInfo['yubico_user_id'];
1266
    }
1267
1268
    // Now check yubico validity
1269
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Authentication/Yubico/Yubico.php';
1270
    $yubi = new Auth_Yubico($yubico_user_id, $yubico_user_key);
1271
    $auth = $yubi->verify($yubico_key);
1272
    //, null, null, null, 60
1273
1274
    if (PEAR::isError($auth)) {
1275
        return [
1276
            'error' => true,
1277
            'value' => '',
1278
            'user_admin' => isset($sessionAdmin) ? (int) $sessionAdmin : '',
1279
            'initial_url' => isset($sessionUrl) === true ? $sessionUrl : '',
1280
            'pwd_attempts' => (int) $sessionPwdAttempts,
1281
            'error' => 'bad_user_yubico_credentials',
1282
            'message' => langHdl('yubico_bad_code'),
1283
            'proceedIdentification' => false,
1284
        ];
1285
    }
1286
1287
    return [
1288
        'error' => false,
1289
        'message' => '',
1290
        'proceedIdentification' => true,
1291
    ];
1292
}
1293
1294
/**
1295
 * Undocumented function.
1296
 *
1297
 * @param string $username      User name
1298
 * @param string $passwordClear User password in clear
1299
 * @param string $retLDAP       Received data from LDAP
1300
 * @param array  $SETTINGS      Teampass settings
1301
 *
1302
 * @return array
1303
 */
1304
function ldapCreateUser(string $username, string $passwordClear, string $retLDAP, array $SETTINGS): array
1305
{
1306
    // Generate user keys pair
1307
    $userKeys = generateUserKeys($passwordClear);
1308
    // Insert user in DB
1309
    DB::insert(
1310
        prefixTable('users'),
1311
        [
1312
            'login' => $username,
1313
            'pw' => $retLDAP['hashedPassword'],
1314
            'email' => isset($retLDAP['user_info_from_ad'][0]['mail'][0]) === false ? '' : $retLDAP['user_info_from_ad'][0]['mail'][0],
1315
            'name' => $retLDAP['user_info_from_ad'][0]['givenname'][0],
1316
            'lastname' => $retLDAP['user_info_from_ad'][0]['sn'][0],
1317
            'admin' => '0',
1318
            'gestionnaire' => '0',
1319
            'can_manage_all_users' => '0',
1320
            'personal_folder' => $SETTINGS['enable_pf_feature'] === '1' ? '1' : '0',
1321
            'fonction_id' => (empty($retLDAP['user_info_from_ad'][0]['commonGroupsLdapVsTeampass']) === false ? $retLDAP['user_info_from_ad'][0]['commonGroupsLdapVsTeampass'] . ';' : '') . (isset($SETTINGS['ldap_new_user_role']) === true ? $SETTINGS['ldap_new_user_role'] : '0'),
1322
            'groupes_interdits' => '',
1323
            'groupes_visibles' => '',
1324
            'last_pw_change' => (int) time(),
1325
            'user_language' => $SETTINGS['default_language'],
1326
            'encrypted_psk' => '',
1327
            'isAdministratedByRole' => isset($SETTINGS['ldap_new_user_is_administrated_by']) === true && empty($SETTINGS['ldap_new_user_is_administrated_by']) === false ? $SETTINGS['ldap_new_user_is_administrated_by'] : 0,
1328
            'public_key' => $userKeys['public_key'],
1329
            'private_key' => $userKeys['private_key'],
1330
        ]
1331
    );
1332
    $newUserId = DB::insertId();
1333
    // Create personnal folder
1334
    if (isset($SETTINGS['enable_pf_feature']) === true && $SETTINGS['enable_pf_feature'] === '1') {
1335
        DB::insert(
1336
            prefixTable('nested_tree'),
1337
            [
1338
                'parent_id' => '0',
1339
                'title' => $newUserId,
1340
                'bloquer_creation' => '0',
1341
                'bloquer_modification' => '0',
1342
                'personal_folder' => '1',
1343
            ]
1344
        );
1345
        // Rebuild tree
1346
        $tree = new SplClassLoader('Tree\NestedTree', $SETTINGS['cpassman_dir'] . '/includes/libraries');
1347
        $tree->register();
1348
        $tree = new Tree\NestedTree\NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
1349
        $tree->rebuild();
1350
    }
1351
1352
    return [
1353
        'error' => false,
1354
        'message' => '',
1355
        'proceedIdentification' => true,
1356
        'user_initial_creation_through_ldap' => true,
1357
    ];
1358
}
1359
1360
/**
1361
 * Undocumented function.
1362
 *
1363
 * @param string                $username     Username
1364
 * @param array                 $userInfo     Result of query
1365
 * @param string|array|resource $dataReceived DataReceived
1366
 * @param array                 $SETTINGS     Teampass settings
1367
 *
1368
 * @return array
1369
 */
1370
function googleMFACheck(string $username, array $userInfo, $dataReceived, array $SETTINGS): array
1371
{
1372
    if (
1373
        isset($dataReceived['GACode']) === true
1374
        && empty($dataReceived['GACode']) === false
1375
    ) {
1376
        // Load superGlobals
1377
        include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
1378
        $superGlobal = new protect\SuperGlobal\SuperGlobal();
1379
        $sessionAdmin = $superGlobal->get('user_admin', 'SESSION');
1380
        $sessionUrl = $superGlobal->get('initial_url', 'SESSION');
1381
        $sessionPwdAttempts = $superGlobal->get('pwd_attempts', 'SESSION');
1382
        // load library
1383
        include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Authentication/TwoFactorAuth/TwoFactorAuth.php';
1384
        // create new instance
1385
        $tfa = new Authentication\TwoFactorAuth\TwoFactorAuth($SETTINGS['ga_website_name']);
1386
        // Init
1387
        $firstTime = [];
1388
        // now check if it is the 1st time the user is using 2FA
1389
        if ($userInfo['ga_temporary_code'] !== 'none' && $userInfo['ga_temporary_code'] !== 'done') {
1390
            if ($userInfo['ga_temporary_code'] !== $dataReceived['GACode']) {
1391
                return [
1392
                    'error' => true,
1393
                    'message' => langHdl('ga_bad_code'),
1394
                    'proceedIdentification' => false,
1395
                    'mfaStatus' => '',
1396
                ];
1397
            }
1398
1399
            // If first time with MFA code
1400
            $proceedIdentification = false;
1401
            $mfaStatus = 'ga_temporary_code_correct';
1402
            $mfaMessage = langHdl('ga_flash_qr_and_login');
1403
            // generate new QR
1404
            $new_2fa_qr = $tfa->getQRCodeImageAsDataUri(
1405
                'Teampass - ' . $username,
1406
                $userInfo['ga']
1407
            );
1408
            // clear temporary code from DB
1409
            DB::update(
1410
                prefixTable('users'),
1411
                [
1412
                    'ga_temporary_code' => 'done',
1413
                ],
1414
                'id=%i',
1415
                $userInfo['id']
1416
            );
1417
            $firstTime = [
1418
                'value' => '<img src="' . $new_2fa_qr . '">',
1419
                'user_admin' => isset($sessionAdmin) ? (int) $sessionAdmin : '',
1420
                'initial_url' => isset($sessionUrl) === true ? $sessionUrl : '',
1421
                'pwd_attempts' => (int) $sessionPwdAttempts,
1422
                'message' => $mfaMessage,
1423
                'mfaStatus' => $mfaStatus,
1424
            ];
1425
        } else {
1426
            // verify the user GA code
1427
            if ($tfa->verifyCode($userInfo['ga'], $dataReceived['GACode'])) {
1428
                $proceedIdentification = true;
1429
            } else {
1430
                return [
1431
                    'error' => true,
1432
                    'message' => langHdl('ga_bad_code'),
1433
                    'proceedIdentification' => false,
1434
                ];
1435
            }
1436
        }
1437
    } else {
1438
        return [
1439
            'error' => true,
1440
            'message' => langHdl('ga_bad_code'),
1441
            'proceedIdentification' => false,
1442
        ];
1443
    }
1444
1445
    return [
1446
        'error' => false,
1447
        'message' => '',
1448
        'proceedIdentification' => $proceedIdentification,
1449
        'firstTime' => $firstTime,
1450
    ];
1451
}
1452
1453
/**
1454
 * Undocumented function.
1455
 *
1456
 * @param string                $passwordClear Password in clear
1457
 * @param array|string          $userInfo      Array of user data
1458
 * @param array|string|resource $dataReceived  Received data
1459
 * @param string                $username      User name
1460
 * @param array                 $SETTINGS      Teampass settings
1461
 *
1462
 * @return bool
1463
 */
1464
function checkCredentials($passwordClear, $userInfo, $dataReceived, $username, $SETTINGS)
1465
{
1466
    // Set to false
1467
    $userPasswordVerified = false;
1468
    // load passwordLib library
1469
    include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php';
1470
    $pwdlib = new SplClassLoader('PasswordLib', $SETTINGS['cpassman_dir'] . '/includes/libraries');
1471
    $pwdlib->register();
1472
    $pwdlib = new PasswordLib\PasswordLib();
1473
    // Check if old encryption used
1474
    if (
1475
        crypt($passwordClear, $userInfo['pw']) === $userInfo['pw']
1476
        && empty($userInfo['pw']) === false
1477
    ) {
1478
        $userPasswordVerified = true;
1479
        //update user's password
1480
        $userInfo['pw'] = $pwdlib->createPasswordHash($passwordClear);
1481
        DB::update(
1482
            prefixTable('users'),
1483
            [
1484
                'pw' => $userInfo['pw'],
1485
            ],
1486
            'id=%i',
1487
            $userInfo['id']
1488
        );
1489
    }
1490
    //echo $passwordClear." - ".$userInfo['pw']." - ".$pwdlib->verifyPasswordHash($passwordClear, $userInfo['pw'])." ;; ";
1491
    // check the given password
1492
    if ($userPasswordVerified !== true) {
1493
        if ($pwdlib->verifyPasswordHash($passwordClear, $userInfo['pw']) === true) {
1494
            $userPasswordVerified = true;
1495
        } else {
1496
            // 2.1.27.24 - manage passwords
1497
            if ($pwdlib->verifyPasswordHash(htmlspecialchars_decode($dataReceived['pw']), $userInfo['pw']) === true) {
1498
                // then the auth is correct but needs to be adapted in DB since change of encoding
1499
                $userInfo['pw'] = $pwdlib->createPasswordHash($passwordClear);
1500
                DB::update(
1501
                    prefixTable('users'),
1502
                    [
1503
                        'pw' => $userInfo['pw'],
1504
                    ],
1505
                    'id=%i',
1506
                    $userInfo['id']
1507
                );
1508
                $userPasswordVerified = true;
1509
            } else {
1510
                $userPasswordVerified = false;
1511
                logEvents(
1512
                    $SETTINGS,
1513
                    'failed_auth',
1514
                    'password_is_not_correct',
1515
                    '',
1516
                    '',
1517
                    stripslashes($username)
1518
                );
1519
            }
1520
        }
1521
    }
1522
1523
    return $userPasswordVerified;
1524
}
1525
1526
/**
1527
 * Undocumented function.
1528
 *
1529
 * @param bool   $enabled text1
1530
 * @param string $dbgFile text2
1531
 * @param string $text    text3
1532
 */
1533
function debugIdentify(bool $enabled, string $dbgFile, string $text): void
1534
{
1535
    if ($enabled === true) {
1536
        $fp = fopen($dbgFile, 'a');
1537
        if ($fp !== false) {
1538
            fwrite(
1539
                $fp,
1540
                $text
1541
            );
1542
        }
1543
    }
1544
}
1545
1546
1547
1548
function identifyGetUserCredentials(
1549
    array $SETTINGS,
1550
    string $serverPHPAuthUser,
1551
    string $serverPHPAuthPw,
1552
    string $userPassword,
1553
    string $userLogin
1554
): array
1555
{
1556
    if ((int) $SETTINGS['enable_http_request_login'] === 1
1557
        && $serverPHPAuthUser !== null
1558
        && (int) $SETTINGS['maintenance_mode'] === 1
1559
    ) {
1560
        if (strpos($serverPHPAuthUser, '@') !== false) {
1561
            return [
1562
                'username' => explode('@', $serverPHPAuthUser)[0],
1563
                'passwordClear' => $serverPHPAuthPw
1564
            ];
1565
        }
1566
        
1567
        if (strpos($serverPHPAuthUser, '\\') !== false) {
1568
            return [
1569
                'username' => explode('\\', $serverPHPAuthUser)[1],
1570
                'passwordClear' => $serverPHPAuthPw
1571
            ];
1572
        }
1573
1574
        return [
1575
            'username' => $serverPHPAuthPw,
1576
            'passwordClear' => $serverPHPAuthPw
1577
        ];
1578
    }
1579
    
1580
    return [
1581
        'username' => $userLogin,
1582
        'passwordClear' => $userPassword
1583
    ];
1584
}
1585
1586
1587
    
1588
1589
function identifyDoInitialChecks(
1590
    $SETTINGS,
1591
    int $sessionPwdAttempts,
1592
    string $username,
1593
    int $sessionAdmin,
1594
    string $sessionUrl,
1595
    string $user_2fa_selection
1596
): array
1597
{
1598
    // Brute force management
1599
    if ($sessionPwdAttempts > 3) {
1600
        // Load superGlobals
1601
        include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
1602
        $superGlobal = new protect\SuperGlobal\SuperGlobal();
1603
        $superGlobal->put('next_possible_pwd_attempts', time() + 10, 'SESSION');
1604
        $superGlobal->put('pwd_attempts', 0, 'SESSION');
1605
1606
        logEvents($SETTINGS, 'failed_auth', 'user_not_exists', '', stripslashes($username), stripslashes($username));
1607
1608
        return [
1609
            'error' => true,
1610
            'array' => [
1611
                'value' => 'bruteforce_wait',
1612
                'user_admin' => isset($sessionAdmin) ? (int) $sessionAdmin : 0,
1613
                'initial_url' => isset($sessionUrl) === true ? $sessionUrl : '',
1614
                'pwd_attempts' => 0,
1615
                'error' => true,
1616
                'message' => langHdl('error_bad_credentials_more_than_3_times'),
1617
            ]
1618
        ];
1619
    }
1620
1621
    // Check 2FA
1622
    if ((((int) $SETTINGS['yubico_authentication'] === 1 && empty($user_2fa_selection) === true)
1623
        || ((int) $SETTINGS['google_authentication'] === 1 && empty($user_2fa_selection) === true)
1624
        || ((int) $SETTINGS['duo'] === 1 && empty($user_2fa_selection) === true))
1625
        && ($username !== 'admin' || ((int) $SETTINGS['admin_2fa_required'] === 1 && $username === 'admin'))
1626
    ) {
1627
        return [
1628
            'error' => true,
1629
            'array' => [
1630
                'value' => '2fa_not_set',
1631
                'user_admin' => isset($sessionAdmin) ? (int) $sessionAdmin : 0,
1632
                'initial_url' => isset($sessionUrl) === true ? $sessionUrl : '',
1633
                'pwd_attempts' => (int) $sessionPwdAttempts,
1634
                'error' => '2fa_not_set',
1635
                'message' => langHdl('2fa_credential_not_correct'),
1636
            ]
1637
        ];
1638
    }
1639
1640
    // Check if user exists
1641
    $userInfo = DB::queryFirstRow(
1642
        'SELECT *
1643
        FROM ' . prefixTable('users') . ' WHERE login=%s',
1644
        $username
1645
    );
1646
    
1647
    // User doesn't exist then stop
1648
    if (DB::count() === 0) {
1649
        logEvents($SETTINGS, 'failed_auth', 'user_not_exists', '', stripslashes($username), stripslashes($username));
1650
1651
        return [
1652
            'error' => true,
1653
            'array' => [
1654
                'error' => 'user_not_exists',
1655
                'message' => langHdl('error_bad_credentials'),
1656
                'pwd_attempts' => (int) $sessionPwdAttempts,
1657
                'user_admin' => isset($sessionAdmin) ? (int) $sessionAdmin : 0,
1658
                'initial_url' => isset($sessionUrl) === true ? $sessionUrl : '',
1659
            ]
1660
        ];
1661
    }
1662
1663
    // has user to be auth with mfa?
1664
    $userInfo['fonction_id'] = is_null($userInfo['fonction_id']) === true ? false : $userInfo['fonction_id'];
1665
    
1666
    // user should use MFA?
1667
    $userInfo['mfa_auth_requested'] = mfa_auth_requested(
1668
        (string) $userInfo['fonction_id'],
1669
        is_null($SETTINGS['mfa_for_roles']) === true ? '' : (string) $SETTINGS['mfa_for_roles']
1670
    );
1671
1672
    // Manage Maintenance mode
1673
    if ((int) $SETTINGS['maintenance_mode'] === 1 && (int) $userInfo['admin'] === 0) {
1674
        return [
1675
            'error' => true,
1676
            'array' => [
1677
                'value' => '',
1678
                'user_admin' => (int) $userInfo['admin'],
1679
                'initial_url' => '',
1680
                'pwd_attempts' => '',
1681
                'error' => 'maintenance_mode_enabled',
1682
                'message' => '',
1683
            ]
1684
        ];
1685
    }
1686
1687
    // If admin user then check if folder install exists
1688
    // if yes then refuse connection
1689
    if ((int) $userInfo['admin'] === 1 && is_dir('../install') === true) {
1690
        return [
1691
            'error' => true,
1692
            'array' => [
1693
                'value' => '',
1694
                'user_admin' => isset($sessionAdmin) ? (int) $sessionAdmin : 0,
1695
                'initial_url' => isset($sessionUrl) === true ? $sessionUrl : '',
1696
                'pwd_attempts' => (int) $sessionPwdAttempts,
1697
                'error' => true,
1698
                'message' => langHdl('remove_install_folder'),
1699
            ]
1700
        ];
1701
    }
1702
1703
    // Return some usefull information about user
1704
    return [
1705
        'error' => false,
1706
        'user_mfa_mode' => $user_2fa_selection,
1707
        'userInfo' => $userInfo,
1708
    ];
1709
}
1710
1711
function identifyDoLDAPChecks(
1712
    $SETTINGS,
1713
    $userInfo,
1714
    string $username,
1715
    string $passwordClear,
1716
    int $sessionAdmin,
1717
    string $sessionUrl,
1718
    int $sessionPwdAttempts
1719
): array
1720
{
1721
    // Prepare LDAP connection if set up
1722
    if ((int) $SETTINGS['ldap_mode'] === 1
1723
        && $username !== 'admin'
1724
        && (string) $userInfo['auth_type'] === 'ldap'
1725
    ) {
1726
        $retLDAP = authenticateThroughAD(
1727
            $username,
1728
            $userInfo,
1729
            $passwordClear,
1730
            $SETTINGS
1731
        );
1732
        if ($retLDAP['error'] === true) {
1733
            return [
1734
                'error' => true,
1735
                'array' => [
1736
                    'value' => '',
1737
                    'user_admin' => isset($sessionAdmin) ? (int) $sessionAdmin : 0,
1738
                    'initial_url' => isset($sessionUrl) === true ? $sessionUrl : '',
1739
                    'pwd_attempts' => (int) $sessionPwdAttempts,
1740
                    'error' => true,
1741
                    'message' => "LDAP error: ".$retLDAP['message'],
1742
                ]
1743
            ];
1744
        }
1745
        return [
1746
            'error' => false,
1747
            'retLDAP' => $retLDAP,
1748
            'ldapConnection' => true,
1749
            'userPasswordVerified' => true,
1750
        ];
1751
    }
1752
1753
    // return if no addmin
1754
    return [
1755
        'error' => false,
1756
        'retLDAP' => [],
1757
        'ldapConnection' => false,
1758
        'userPasswordVerified' => false,
1759
    ];
1760
}
1761
1762
1763
function identifyDoMFAChecks(
1764
    $SETTINGS,
1765
    $userInfo,
1766
    $dataReceived,
1767
    $userInitialData,
1768
    string $username
1769
): array
1770
{    
1771
    if (
1772
        // ---------
1773
        // check GA code
1774
        // ---------
1775
        (int) $SETTINGS['google_authentication'] === 1
1776
        && ($username !== 'admin' || ((int) $SETTINGS['admin_2fa_required'] === 1 && $username === 'admin'))
1777
        && $userInitialData['user_mfa_mode'] === 'google'
1778
        && $userInfo['mfa_auth_requested'] === true
1779
    ) {
1780
        $ret = googleMFACheck(
1781
            $username,
1782
            $userInfo,
1783
            $dataReceived,
1784
            $SETTINGS
1785
        );
1786
        if ($ret['error'] !== false) {
1787
            logEvents($SETTINGS, 'failed_auth', 'wrong_mfa_code', '', stripslashes($username), stripslashes($username));
1788
            
1789
            return [
1790
                'error' => true,
1791
                'mfaData' => $ret,
1792
            ];
1793
            // ---
1794
        }
1795
        return [
1796
            'error' => false,
1797
            'mfaData' => $ret,
1798
        ];
1799
1800
        // ---
1801
        // ---
1802
    } else if (
1803
        // ---------
1804
        // Check Yubico
1805
        // ---------
1806
        isset($SETTINGS['yubico_authentication']) === true
1807
        && (int) $SETTINGS['yubico_authentication'] === 1
1808
        && ((int) $userInfo['admin'] !== 1 || ((int) $SETTINGS['admin_2fa_required'] === 1 && (int) $userInfo['admin'] === 1))
1809
        && $userInitialData['user_mfa_mode'] === 'yubico'
1810
        && $userInfo['mfa_auth_requested'] === true
1811
    ) {
1812
        $ret = yubicoMFACheck(
1813
            $dataReceived,
1814
            $userInfo,
1815
            $SETTINGS
1816
        );
1817
        if ($ret['error'] !== '') {
1818
            return [
1819
                'error' => true,
1820
                'mfaData' => $ret,
1821
            ];
1822
        }
1823
        
1824
        // ---
1825
        // ---
1826
    }
1827
1828
    return [
1829
        'error' => false,
1830
        'user_initial_creation_through_ldap' => $ret['user_initial_creation_through_ldap'],
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $ret does not seem to be defined for all execution paths leading up to this point.
Loading history...
1831
    ];
1832
}
1833