Passed
Push — teampass_3.0 ( 99aa1e...94902e )
by Nils
04:22
created

identifyDoInitialChecks()   D

Complexity

Conditions 22
Paths 16

Size

Total Lines 105
Code Lines 63

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 22
eloc 63
c 1
b 0
f 0
nc 16
nop 6
dl 0
loc 105
rs 4.1666

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-2021 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
    // Get DUO keys
96
    $duoData = DB::query(
97
        'SELECT intitule, valeur
98
        FROM ' . prefixTable('misc') . '
99
        WHERE type = %s',
100
        'duoSecurity'
101
    );
102
    $_GLOBALS = [];
103
    foreach ($duoData as $value) {
104
        $_GLOBALS[strtoupper($value['intitule'])] = $value['valeur'];
105
    }
106
107
    // load library
108
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Authentication/DuoSecurity/Duo.php';
109
    $sig_request = Duo::signRequest(
110
        $_GLOBALS['IKEY'],
111
        $_GLOBALS['SKEY'],
112
        $_GLOBALS['AKEY'],
113
        $post_login
114
    );
115
    if (DEBUGDUO === true) {
116
        debugIdentify(
117
            DEBUGDUO,
118
            DEBUGDUOFILE,
119
            "\n\n-----\n\n" .
120
                'sig request : ' . $post_login . "\n" .
121
                'resp : ' . $sig_request . "\n"
122
        );
123
    }
124
125
    // load csrfprotector
126
    $csrfp_config = include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/csrfp/libs/csrfp.config.php';
127
    // return result
128
    echo '[{"sig_request" : "' . $sig_request . '" , "csrfp_token" : "' . $csrfp_config['CSRFP_TOKEN'] . '" , "csrfp_key" : "' . filter_var($_COOKIE[$csrfp_config['CSRFP_TOKEN']], FILTER_SANITIZE_STRING) . '"}]';
129
// ---
130
    // ---
131
} elseif ($post_type === 'identify_duo_user_check') {
132
    //--------
133
    // DUO AUTHENTICATION
134
    // this step is verifying the response received from the server
135
    //--------
136
137
    // load library
138
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Authentication/DuoSecurity/Duo.php';
139
    $authenticated_username = Duo::verifyResponse(
140
        $SETTINGS['duo_ikey'],
141
        $SETTINGS['duo_skey'],
142
        $SETTINGS['duo_akey'],
143
        $post_sig_response
144
    );
145
    if (DEBUGDUO === true) {
146
        debugIdentify(
147
            DEBUGDUO,
148
            DEBUGDUOFILE,
149
            "\n\n-----\n\n" .
150
                'sig response : ' . $post_sig_response . "\n" .
151
                'resp : ' . $authenticated_username . "\n"
152
        );
153
    }
154
155
    // return the response (which should be the user name)
156
    if ($authenticated_username === $post_login) {
157
        // Check if this account exists in Teampass or only in LDAP
158
        if (isset($SETTINGS['ldap_mode']) === true && (int) $SETTINGS['ldap_mode'] === 1) {
159
            // is user in Teampass?
160
            DB::queryfirstrow(
161
                'SELECT id
162
                FROM ' . prefixTable('users') . '
163
                WHERE login = %s',
164
                $post_login
165
            );
166
            if (DB::count() === 0) {
167
                // Ask your administrator to create your account in Teampass
168
                echo '[{"authenticated_username" : "ERR|This user is not yet imported inside Teampass."}]';
169
            }
170
        }
171
172
        echo '[{"authenticated_username" : "' . $authenticated_username . '"}]';
173
    } else {
174
        echo '[{"authenticated_username" : "' . $authenticated_username . '"}]';
175
    }
176
    // ---
177
    // ---
178
} elseif ($post_type === 'identify_user') {
179
    //--------
180
    // NORMAL IDENTICATION STEP
181
    //--------
182
183
    // Load superGlobals
184
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
185
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
186
    // Prepare GET variables
187
    $sessionPwdAttempts = $superGlobal->get('pwd_attempts', 'SESSION');
188
    // increment counter of login attempts
189
    if ($sessionPwdAttempts === '') {
190
        $sessionPwdAttempts = 1;
191
    } else {
192
        ++$sessionPwdAttempts;
193
    }
194
195
    $superGlobal->put('pwd_attempts', $sessionPwdAttempts, 'SESSION');
196
    // manage brute force
197
    if ($sessionPwdAttempts <= 3) {
198
        $sessionPwdAttempts = 0;
199
        // identify the user through Teampass process
200
        identifyUser(
201
            $post_data,
202
            $SETTINGS
203
        );
204
    } elseif (isset($_SESSION['next_possible_pwd_attempts']) && time() > $_SESSION['next_possible_pwd_attempts'] && $sessionPwdAttempts > 3) {
205
        $sessionPwdAttempts = 0;
206
        // identify the user through Teampass process
207
        identifyUser(
208
            $post_data,
209
            $SETTINGS
210
        );
211
    } else {
212
        $_SESSION['next_possible_pwd_attempts'] = time() + 10;
213
        // Encrypt data to return
214
        echo prepareExchangedData(
215
            [
216
                'value' => 'bruteforce_wait',
217
                'user_admin' => isset($sessionAdmin) ? (int) $sessionAdmin : 0,
218
                'initial_url' => isset($sessionUrl) === true ? $sessionUrl : '',
219
                'pwd_attempts' => (int) $sessionPwdAttempts,
220
                'error' => true,
221
                'message' => langHdl('error_bad_credentials_more_than_3_times'),
222
            ],
223
            'encode'
224
        );
225
        return false;
226
    }
227
    // ---
228
    // ---
229
    // ---
230
} elseif ($post_type === 'get2FAMethods') {
231
    //--------
232
    // Get MFA methods
233
    //--------
234
    //
235
236
    // Encrypt data to return
237
    echo prepareExchangedData(
238
        [
239
            'agses' => isset($SETTINGS['agses_authentication_enabled']) === true
240
                && (int) $SETTINGS['agses_authentication_enabled'] === 1 ? true : false,
241
            'google' => isset($SETTINGS['google_authentication']) === true
242
                && (int) $SETTINGS['google_authentication'] === 1 ? true : false,
243
            'yubico' => isset($SETTINGS['yubico_authentication']) === true
244
                && (int) $SETTINGS['yubico_authentication'] === 1 ? true : false,
245
            'duo' => isset($SETTINGS['duo']) === true
246
                && (int) $SETTINGS['duo'] === 1 ? true : false,
247
        ],
248
        'encode'
249
    );
250
    return false;
251
}
252
253
/**
254
 * Complete authentication of user through Teampass
255
 *
256
 * @param string $sentData Credentials
257
 * @param array $SETTINGS Teampass settings
258
 *
259
 * @return bool
260
 */
261
function identifyUser(string $sentData, array $SETTINGS): bool
262
{
263
    // Load config
264
    if (file_exists('../includes/config/tp.config.php')) {
265
        include_once '../includes/config/tp.config.php';
266
    } elseif (file_exists('./includes/config/tp.config.php')) {
267
        include_once './includes/config/tp.config.php';
268
    } else {
269
        throw new Exception("Error file '/includes/config/tp.config.php' not exists", 1);
270
    }
271
    include_once $SETTINGS['cpassman_dir'] . '/includes/config/settings.php';
272
    include_once $SETTINGS['cpassman_dir'] . '/sources/main.functions.php';
273
    include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php';
274
    
275
    header('Content-type: text/html; charset=utf-8');
276
    error_reporting(E_ERROR);
277
278
    // Load AntiXSS
279
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/voku/helper/AntiXSS.php';
280
    $antiXss = new voku\helper\AntiXSS();
281
282
    // Load superGlobals
283
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
284
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
285
286
    // Prepare GET variables
287
    $sessionUserLanguage = $superGlobal->get('user_language', 'SESSION');
288
    $sessionKey = $superGlobal->get('key', 'SESSION');
289
    $sessionAdmin = $superGlobal->get('user_admin', 'SESSION');
290
    $sessionPwdAttempts = $superGlobal->get('pwd_attempts', 'SESSION');
291
    $sessionUrl = $superGlobal->get('initial_url', 'SESSION');
292
    $server = [];
293
    $server['PHP_AUTH_USER'] = $superGlobal->get('PHP_AUTH_USER', 'SERVER');
294
    $server['PHP_AUTH_PW'] = $superGlobal->get('PHP_AUTH_PW', 'SERVER');
295
296
    // connect to the server
297
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
298
    DB::$host = DB_HOST;
299
    DB::$user = DB_USER;
300
    DB::$password = defined('DB_PASSWD_CLEAR') === false ? defuseReturnDecrypted(DB_PASSWD, $SETTINGS) : DB_PASSWD_CLEAR;
301
    DB::$dbName = DB_NAME;
302
    DB::$port = DB_PORT;
303
    DB::$encoding = DB_ENCODING;
304
    // User's language loading
305
    include_once $SETTINGS['cpassman_dir'] . '/includes/language/' . $sessionUserLanguage . '.php';
306
    
307
    // decrypt and retreive data in JSON format
308
    if (empty($sessionKey) === true) {
309
        $dataReceived = $sentData;
310
    } else {
311
        $dataReceived = prepareExchangedData($sentData, 'decode', $sessionKey);
312
        $superGlobal->put('key', $sessionKey, 'SESSION');
313
    }
314
315
    // prepare variables    
316
    $userCredentials = identifyGetUserCredentials(
317
        $SETTINGS,
318
        (string) $server['PHP_AUTH_USER'],
319
        (string) $server['PHP_AUTH_PW'],
320
        (string) filter_var($dataReceived['pw'], FILTER_SANITIZE_STRING),
321
        (string) filter_var($dataReceived['login'], FILTER_SANITIZE_STRING)
322
    );
323
    $username = $userCredentials['username'];
324
    $passwordClear = $userCredentials['passwordClear'];
325
326
    // DO initial checks
327
    $userInitialData = identifyDoInitialChecks(
328
        $SETTINGS,
329
        (int) $sessionPwdAttempts,
330
        (string) $username,
331
        (int) $sessionAdmin,
332
        (string) $sessionUrl,
333
        (string) filter_var($dataReceived['user_2fa_selection'], FILTER_SANITIZE_STRING)
334
    );
335
    if ($userInitialData['error'] === true) {
336
        echo prepareExchangedData(
337
            $userInitialData['array'],
338
            'encode'
339
        );
340
        return false;
341
    }
342
343
    $userInfo = $userInitialData['userInfo'];
344
345
    $user_initial_creation_through_ldap = false;
346
    $userPasswordVerified = false;
347
    $ldapConnection = false;
348
    $return = '';
349
    // Prepare LDAP connection if set up
350
    if (
351
        isset($SETTINGS['ldap_mode']) === true
352
        && (int) $SETTINGS['ldap_mode'] === 1
353
        && $username !== 'admin'
354
        && $userInfo['auth_type'] === 'ldap'
355
    ) {
356
        $ldapConnection = true;
357
        $retLDAP = authenticateThroughAD(
358
            $username,
359
            $userInfo,
360
            $passwordClear,
361
            $SETTINGS
362
        );
363
        if ($retLDAP['error'] === true) {
364
            echo prepareExchangedData(
365
                [
366
                    'value' => '',
367
                    'user_admin' => isset($sessionAdmin) ? (int) $sessionAdmin : 0,
368
                    'initial_url' => isset($sessionUrl) === true ? $sessionUrl : '',
369
                    'pwd_attempts' => (int) $sessionPwdAttempts,
370
                    'error' => true,
371
                    'message' => $retLDAP['message'],
372
                ],
373
                'encode'
374
            );
375
            return false;
376
        }
377
        $userPasswordVerified = true;
378
    }
379
380
    // Check Yubico
381
    if (
382
        isset($SETTINGS['yubico_authentication']) === true
383
        && (int) $SETTINGS['yubico_authentication'] === 1
384
        && ((int) $userInfo['admin'] !== 1 || ((int) $SETTINGS['admin_2fa_required'] === 1 && (int) $userInfo['admin'] === 1))
385
        && $userInitialData['user_mfa_mode'] === 'yubico'
386
        && $userInfo['mfa_auth_requested'] === true
387
    ) {
388
        $ret = yubicoMFACheck(
389
            $dataReceived,
390
            $userInfo,
391
            $SETTINGS
392
        );
393
        if ($ret['error'] !== '') {
394
            echo prepareExchangedData(
395
                $ret,
396
                'encode'
397
            );
398
            return false;
399
        }
400
    }
401
402
    // check GA code
403
    if (
404
        isset($SETTINGS['google_authentication']) === true
405
        && (int) $SETTINGS['google_authentication'] === 1
406
        && ($username !== 'admin' || ((int) $SETTINGS['admin_2fa_required'] === 1 && $username === 'admin'))
407
        && $userInitialData['user_mfa_mode'] === 'google'
408
        && $userInfo['mfa_auth_requested'] === true
409
    ) {
410
        $ret = googleMFACheck(
411
            $username,
412
            $userInfo,
413
            $dataReceived,
414
            $SETTINGS
415
        );
416
        if ($ret['error'] !== false) {
417
            logEvents($SETTINGS, 'failed_auth', 'wrong_mfa_code', '', stripslashes($username), stripslashes($username));
418
            echo prepareExchangedData(
419
                $ret,
420
                'encode'
421
            );
422
            return false;
423
            // ---
424
        }
425
        $user_initial_creation_through_ldap = $ret['user_initial_creation_through_ldap'];
426
        // Manage 1st usage of Google MFA
427
        if (count($ret['firstTime']) > 0) {
428
            echo prepareExchangedData(
429
                $ret['firstTime'],
430
                'encode'
431
            );
432
            return false;
433
        }
434
    }
435
436
    // If admin user then check if folder install exists
437
    // if yes then refuse connection
438
    if ((int) $userInfo['admin'] === 1 && is_dir('../install') === true) {
439
        echo prepareExchangedData(
440
            [
441
                'value' => '',
442
                'user_admin' => isset($sessionAdmin) ? (int) $sessionAdmin : 0,
443
                'initial_url' => isset($sessionUrl) === true ? $sessionUrl : '',
444
                'pwd_attempts' => (int) $sessionPwdAttempts,
445
                'error' => true,
446
                'message' => langHdl('remove_install_folder'),
447
            ],
448
            'encode'
449
        );
450
        return false;
451
    }
452
453
    // Check user and password
454
    if ($userPasswordVerified === false && (int) checkCredentials($passwordClear, $userInfo, $dataReceived, $username, $SETTINGS) !== 1) {
455
        echo prepareExchangedData(
456
            [
457
                'value' => '',
458
                'user_admin' => isset($sessionAdmin) ? (int) $sessionAdmin : 0,
459
                'initial_url' => isset($sessionUrl) === true ? $sessionUrl : '',
460
                'pwd_attempts' => (int) $sessionPwdAttempts,
461
                'error' => 'user_not_exists2',
462
                'message' => langHdl('error_bad_credentials'),
463
            ],
464
            'encode'
465
        );
466
        return false;
467
    }
468
469
    // Can connect if
470
    // 1- no LDAP mode + user enabled + pw ok
471
    // 2- LDAP mode + user enabled + ldap connection ok + user is not admin
472
    // 3-  LDAP mode + user enabled + pw ok + usre is admin
473
    // This in order to allow admin by default to connect even if LDAP is activated
474
    if ((isset($SETTINGS['ldap_mode']) === true && (int) $SETTINGS['ldap_mode'] === 0
475
            && (int) $userInfo['disabled'] === 0)
476
        || (isset($SETTINGS['ldap_mode']) === true && (int) $SETTINGS['ldap_mode'] === 1
477
            && $ldapConnection === true && (int) $userInfo['disabled'] === 0 && $username !== 'admin')
478
        || (isset($SETTINGS['ldap_mode']) === true && (int) $SETTINGS['ldap_mode'] === 2
479
            && $ldapConnection === true && (int) $userInfo['disabled'] === 0 && $username !== 'admin')
480
        || (isset($SETTINGS['ldap_mode']) === true && (int) $SETTINGS['ldap_mode'] === 1
481
            && $username === 'admin' && (int) $userInfo['disabled'] === 0)
482
        || (isset($SETTINGS['ldap_and_local_authentication']) === true && (int) $SETTINGS['ldap_and_local_authentication'] === 1
483
            && isset($SETTINGS['ldap_mode']) === true && in_array($SETTINGS['ldap_mode'], ['1', '2']) === true
484
            && (int) $userInfo['disabled'] === 0)
485
    ) {
486
        $superGlobal->put('autoriser', true, 'SESSION');
487
        $superGlobal->put('pwd_attempts', 0, 'SESSION');
488
        /*// Debug
489
                debugIdentify(
490
                    DEBUGDUO,
491
                    DEBUGDUOFILE,
492
                    "User's token: " . $key . "\n"
493
                );*/
494
495
        // Check if any unsuccessfull login tries exist
496
        //$arrAttempts = array();
497
        $rows = DB::query(
498
            'SELECT date
499
            FROM ' . prefixTable('log_system') . "
500
            WHERE field_1 = %s
501
            AND type = 'failed_auth'
502
            AND label = 'user_password_not_correct'
503
            AND date >= %s AND date < %s",
504
            $userInfo['login'],
505
            $userInfo['last_connexion'],
506
            time()
507
        );
508
        //$arrAttempts['nb'] = DB::count();
509
        //$arrAttempts['shown'] = DB::count() === 0 ? true : false;
510
        $arrAttempts = [];
511
        if (DB::count() > 0) {
512
            foreach ($rows as $record) {
513
                array_push(
514
                    $arrAttempts,
515
                    date($SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'], (int) $record['date'])
516
                );
517
            }
518
        }
519
        $superGlobal->put('unsuccessfull_login_attempts_list', $arrAttempts, 'SESSION', 'user');
520
        $superGlobal->put('unsuccessfull_login_attempts_shown', DB::count() === 0 ? true : false, 'SESSION', 'user');
521
        $superGlobal->put('unsuccessfull_login_attempts_nb', DB::count(), 'SESSION', 'user');
522
        // Log into DB the user's connection
523
        if (
524
            isset($SETTINGS['log_connections']) === true
525
            && (int) $SETTINGS['log_connections'] === 1
526
        ) {
527
            logEvents($SETTINGS, 'user_connection', 'connection', (string) $userInfo['id'], stripslashes($username));
528
        }
529
        // Save account in SESSION
530
        $superGlobal->put('login', stripslashes($username), 'SESSION');
531
        $superGlobal->put('name', stripslashes($userInfo['name']), 'SESSION');
532
        $superGlobal->put('lastname', stripslashes($userInfo['lastname']), 'SESSION');
533
        $superGlobal->put('user_id', (int) $userInfo['id'], 'SESSION');
534
        $superGlobal->put('user_pwd', $passwordClear, 'SESSION');
535
        $superGlobal->put('admin', $userInfo['admin'], 'SESSION');
536
        $superGlobal->put('user_manager', $userInfo['gestionnaire'], 'SESSION');
537
        $superGlobal->put('user_can_manage_all_users', $userInfo['can_manage_all_users'], 'SESSION');
538
        $superGlobal->put('user_read_only', $userInfo['read_only'], 'SESSION');
539
        $superGlobal->put('last_pw_change', (int) $userInfo['last_pw_change'], 'SESSION');
540
        $superGlobal->put('last_pw', $userInfo['last_pw'], 'SESSION');
541
        $superGlobal->put('can_create_root_folder', $userInfo['can_create_root_folder'], 'SESSION');
542
        $superGlobal->put('personal_folder', $userInfo['personal_folder'], 'SESSION');
543
        $superGlobal->put('user_language', $userInfo['user_language'], 'SESSION');
544
        $superGlobal->put('user_email', $userInfo['email'], 'SESSION');
545
        $superGlobal->put('user_ga', $userInfo['ga'], 'SESSION');
546
        $superGlobal->put('user_avatar', $userInfo['avatar'], 'SESSION');
547
        $superGlobal->put('user_avatar_thumb', $userInfo['avatar_thumb'], 'SESSION');
548
        $superGlobal->put('user_upgrade_needed', $userInfo['upgrade_needed'], 'SESSION');
549
        $superGlobal->put('user_force_relog', $userInfo['force-relog'], 'SESSION');
550
        // get personal settings
551
        if (! isset($userInfo['treeloadstrategy']) || empty($userInfo['treeloadstrategy'])) {
552
            $userInfo['treeloadstrategy'] = 'full';
553
        }
554
        $superGlobal->put('treeloadstrategy', $userInfo['treeloadstrategy'], 'SESSION', 'user');
555
        $superGlobal->put('agses-usercardid', $userInfo['agses-usercardid'], 'SESSION', 'user');
556
        $superGlobal->put('user_language', $userInfo['user_language'], 'SESSION', 'user');
557
        $superGlobal->put('usertimezone', $userInfo['usertimezone'], 'SESSION', 'user');
558
        $superGlobal->put('session_duration', $dataReceived['duree_session'] * 60, 'SESSION', 'user');
559
        $superGlobal->put('api-key', $userInfo['user_api_key'], 'SESSION', 'user');
560
        $superGlobal->put('special', $userInfo['special'], 'SESSION', 'user');
561
        $superGlobal->put('auth_type', $userInfo['auth_type'], 'SESSION', 'user');
562
        // manage session expiration
563
        $superGlobal->put('sessionDuration', (int) (time() + ($dataReceived['duree_session'] * 60)), 'SESSION');
564
565
        /*
566
        * CHECK PASSWORD VALIDITY
567
        * Don't take into consideration if LDAP in use
568
        */
569
        if (isset($SETTINGS['ldap_mode']) === true && (int) $SETTINGS['ldap_mode'] === 1) {
570
            $superGlobal->put('validite_pw', true, 'SESSION');
571
            $superGlobal->put('last_pw_change', true, 'SESSION');
572
        } else {
573
            if (isset($userInfo['last_pw_change']) === true) {
574
                if ((int) $SETTINGS['pw_life_duration'] === 0) {
575
                    $superGlobal->put('user_force_relog', 'infinite', 'SESSION');
576
                    $superGlobal->put('validite_pw', true, 'SESSION');
577
                } else {
578
                    $superGlobal->put(
579
                        'numDaysBeforePwExpiration',
580
                        $SETTINGS['pw_life_duration'] - round(
581
                            (mktime(0, 0, 0, (int) date('m'), (int) date('d'), (int) date('y')) - $superGlobal->get('last_pw_change', 'SESSION')) / (24 * 60 * 60)
582
                        ),
583
                        'SESSION'
584
                    );
585
                    if ($superGlobal->get('numDaysBeforePwExpiration', 'SESSION') <= 0) {
586
                        $superGlobal->put('validite_pw', false, 'SESSION');
587
                    } else {
588
                        $superGlobal->put('validite_pw', true, 'SESSION');
589
                    }
590
                }
591
            } else {
592
                $superGlobal->put('validite_pw', false, 'SESSION');
593
            }
594
        }
595
596
        if (empty($userInfo['last_connexion'])) {
597
            $superGlobal->put('last_connection', (int) time(), 'SESSION');
598
        } else {
599
            $superGlobal->put('last_connection', (int) $userInfo['last_connexion'], 'SESSION');
600
        }
601
602
        if (empty($userInfo['latest_items']) === false) {
603
            $superGlobal->put('latest_items', explode(';', $userInfo['latest_items']), 'SESSION');
604
        } else {
605
            $superGlobal->put('latest_items', [], 'SESSION');
606
        }
607
        if (empty($userInfo['favourites']) === false) {
608
            $superGlobal->put('favourites', explode(';', $userInfo['favourites']), 'SESSION');
609
        } else {
610
            $superGlobal->put('favourites', [], 'SESSION');
611
        }
612
613
        if (empty($userInfo['groupes_visibles']) === false) {
614
            $superGlobal->put(
615
                'groupes_visibles',
616
                explode(';', $userInfo['groupes_visibles']),
617
                'SESSION'
618
            );
619
        } else {
620
            $superGlobal->put('groupes_visibles', [], 'SESSION');
621
        }
622
        if (empty($userInfo['groupes_interdits']) === false) {
623
            $superGlobal->put(
624
                'no_access_folders',
625
                explode(';', $userInfo['groupes_visibles']),
626
                'SESSION'
627
            );
628
        } else {
629
            $superGlobal->put('no_access_folders', [], 'SESSION');
630
        }
631
        
632
        // User's roles
633
        if (strpos($userInfo['fonction_id'] !== NULL ? (string) $userInfo['fonction_id'] : '', ',') !== -1) {
634
            // Convert , to ;
635
            $userInfo['fonction_id'] = str_replace(',', ';', $userInfo['fonction_id']);
636
            DB::update(
637
                prefixTable('users'),
638
                [
639
                    'fonction_id' => $userInfo['fonction_id'],
640
                ],
641
                'id = %i',
642
                $superGlobal->get('user_id', 'SESSION')
643
            );
644
        }
645
        $superGlobal->put('fonction_id', $userInfo['fonction_id'], 'SESSION');
646
        $superGlobal->put('user_roles', array_filter(explode(';', $userInfo['fonction_id'])), 'SESSION');
647
        // build array of roles
648
        $superGlobal->put('user_pw_complexity', 0, 'SESSION');
649
        $superGlobal->put('arr_roles', [], 'SESSION');
650
        foreach ($superGlobal->get('user_roles', 'SESSION') as $role) {
651
            $resRoles = DB::queryFirstRow(
652
                'SELECT title, complexity
653
                FROM ' . prefixTable('roles_title') . '
654
                WHERE id=%i',
655
                $role
656
            );
657
            $superGlobal->put(
658
                $role,
659
                [
660
                    'id' => $role,
661
                    'title' => $resRoles['title'],
662
                ],
663
                'SESSION',
664
                'arr_roles'
665
            );
666
            // get highest complexity
667
            if (intval($superGlobal->get('user_pw_complexity', 'SESSION')) < intval($resRoles['complexity'])) {
668
                $superGlobal->put('user_pw_complexity', $resRoles['complexity'], 'SESSION');
669
            }
670
        }
671
672
        // build complete array of roles
673
        $superGlobal->put('arr_roles_full', [], 'SESSION');
674
        $rows = DB::query('SELECT id, title FROM ' . prefixTable('roles_title') . ' ORDER BY title ASC');
675
        foreach ($rows as $record) {
676
            $superGlobal->put(
677
                $record['id'],
678
                [
679
                    'id' => $record['id'],
680
                    'title' => $record['title'],
681
                ],
682
                'SESSION',
683
                'arr_roles_full'
684
            );
685
        }
686
        // Set some settings
687
        $SETTINGS['update_needed'] = '';
688
        // User signature keys
689
        $superGlobal->put('public_key', $userInfo['public_key'], 'SESSION', 'user');
690
        if (is_null($userInfo['private_key']) === true || empty($userInfo['private_key']) === true || $userInfo['private_key'] === 'none') {
691
            // No keys have been generated yet
692
            // Create them
693
            $userKeys = generateUserKeys($passwordClear);
694
            $superGlobal->put('private_key', $userKeys['private_key_clear'], 'SESSION', 'user');
695
            $arrayUserKeys = [
696
                'public_key' => $userKeys['public_key'],
697
                'private_key' => $userKeys['private_key'],
698
            ];
699
        } elseif ($userInfo['special'] === 'generate-keys') {
700
            $arrayUserKeys = [];
701
        } else {
702
            // Uncrypt private key
703
            $superGlobal->put('private_key', decryptPrivateKey($passwordClear, $userInfo['private_key']), 'SESSION', 'user');
704
            $arrayUserKeys = [];
705
        }
706
707
        // Update table
708
        DB::update(
709
            prefixTable('users'),
710
            array_merge(
711
                [
712
                    'key_tempo' => $superGlobal->get('key', 'SESSION'),
713
                    'last_connexion' => time(),
714
                    'timestamp' => time(),
715
                    'disabled' => 0,
716
                    'no_bad_attempts' => 0,
717
                    'session_end' => $superGlobal->get('sessionDuration', 'SESSION'),
718
                    'user_ip' => $dataReceived['client'],
719
                ],
720
                $arrayUserKeys
721
            ),
722
            'id=%i',
723
            $userInfo['id']
724
        );
725
        // Debug
726
        debugIdentify(
727
            DEBUGDUO,
728
            DEBUGDUOFILE,
729
            "Preparing to identify the user rights\n"
730
        );
731
        // Get user's rights
732
        if ($user_initial_creation_through_ldap === false) {
733
            identifyUserRights(
734
                $userInfo['groupes_visibles'],
735
                $superGlobal->get('no_access_folders', 'SESSION'),
736
                $userInfo['admin'],
737
                $userInfo['fonction_id'],
738
                $SETTINGS
739
            );
740
        } else {
741
            // is new LDAP user. Show only his personal folder
742
            if ($SETTINGS['enable_pf_feature'] === '1') {
743
                $superGlobal->put('personal_visible_groups', [$userInfo['id']], 'SESSION');
744
                $superGlobal->put('personal_folders', [$userInfo['id']], 'SESSION');
745
            } else {
746
                $superGlobal->put('personal_visible_groups', [], 'SESSION');
747
                $superGlobal->put('personal_folders', [], 'SESSION');
748
            }
749
            $superGlobal->put('all_non_personal_folders', [], 'SESSION');
750
            $superGlobal->put('groupes_visibles', [], 'SESSION');
751
            $superGlobal->put('read_only_folders', [], 'SESSION');
752
            $superGlobal->put('list_folders_limited', '', 'SESSION');
753
            $superGlobal->put('list_folders_editable_by_role', [], 'SESSION');
754
            $superGlobal->put('list_restricted_folders_for_items', [], 'SESSION');
755
            $superGlobal->put('nb_folders', 1, 'SESSION');
756
            $superGlobal->put('nb_roles', 0, 'SESSION');
757
        }
758
        // Get some more elements
759
        $superGlobal->put('screenHeight', $dataReceived['screenHeight'], 'SESSION');
760
        // Get last seen items
761
        $superGlobal->put('latest_items_tab', [], 'SESSION');
762
        $superGlobal->put('nb_roles', 0, 'SESSION');
763
        foreach ($superGlobal->get('latest_items', 'SESSION') as $item) {
764
            if (! empty($item)) {
765
                $dataLastItems = DB::queryFirstRow(
766
                    'SELECT id,label,id_tree
767
                    FROM ' . prefixTable('items') . '
768
                    WHERE id=%i',
769
                    $item
770
                );
771
                $superGlobal->put(
772
                    $item,
773
                    [
774
                        'id' => $item,
775
                        'label' => $dataLastItems['label'],
776
                        'url' => 'index.php?page=items&amp;group=' . $dataLastItems['id_tree'] . '&amp;id=' . $item,
777
                    ],
778
                    'SESSION',
779
                    'latest_items_tab'
780
                );
781
            }
782
        }
783
        // send back the random key
784
        $return = $dataReceived['randomstring'];
785
        // Send email
786
        if (
787
            isset($SETTINGS['enable_send_email_on_user_login'])
788
            && (int) $SETTINGS['enable_send_email_on_user_login'] === 1
789
            && (int) $sessionAdmin !== 1
790
        ) {
791
            // get all Admin users
792
            $receivers = '';
793
            $rows = DB::query('SELECT email FROM ' . prefixTable('users') . " WHERE admin = %i and email != ''", 1);
794
            foreach ($rows as $record) {
795
                if (empty($receivers)) {
796
                    $receivers = $record['email'];
797
                } else {
798
                    $receivers = ',' . $record['email'];
799
                }
800
            }
801
            // Add email to table
802
            DB::insert(
803
                prefixTable('emails'),
804
                [
805
                    'timestamp' => time(),
806
                    'subject' => langHdl('email_subject_on_user_login'),
807
                    'body' => str_replace(
808
                        [
809
                            '#tp_user#',
810
                            '#tp_date#',
811
                            '#tp_time#',
812
                        ],
813
                        [
814
                            ' ' . $superGlobal->get('login', 'SESSION') . ' (IP: ' . getClientIpServer() . ')',
815
                            date($SETTINGS['date_format'], (int) $superGlobal->get('last_connection', 'SESSION')),
816
                            date($SETTINGS['time_format'], (int) $superGlobal->get('last_connection', 'SESSION')),
817
                        ],
818
                        langHdl('email_body_on_user_login')
819
                    ),
820
                    'receivers' => $receivers,
821
                    'status' => 'not_sent',
822
                ]
823
            );
824
        }
825
826
        // Ensure Complexity levels are translated
827
        if (defined('TP_PW_COMPLEXITY') === false) {
828
            define(
829
                'TP_PW_COMPLEXITY',
830
                [
831
                    0 => [0, langHdl('complex_level0'), 'fas fa-bolt text-danger'],
832
                    25 => [25, langHdl('complex_level1'), 'fas fa-thermometer-empty text-danger'],
833
                    50 => [50, langHdl('complex_level2'), 'fas fa-thermometer-quarter text-warning'],
834
                    60 => [60, langHdl('complex_level3'), 'fas fa-thermometer-half text-warning'],
835
                    70 => [70, langHdl('complex_level4'), 'fas fa-thermometer-three-quarters text-success'],
836
                    80 => [80, langHdl('complex_level5'), 'fas fa-thermometer-full text-success'],
837
                    90 => [90, langHdl('complex_level6'), 'far fa-gem text-success'],
838
                ]
839
            );
840
        }
841
842
        /*
843
        $sessionUrl = '';
844
        if ($SETTINGS['cpassman_dir'] === '..') {
845
            $SETTINGS['cpassman_dir'] = '.';
846
        }
847
        */
848
    } elseif ((int) $userInfo['disabled'] === 1) {
849
        // User and password is okay but account is locked
850
        echo prepareExchangedData(
851
            [
852
                'value' => $return,
853
                'user_id' => $superGlobal->get('user_id', 'SESSION') !== null ? (int) $superGlobal->get('user_id', 'SESSION') : '',
854
                'user_admin' => $superGlobal->get('admin', 'SESSION') !== null ? (int) $superGlobal->get('admin', 'SESSION') : 0,
855
                'initial_url' => isset($sessionUrl) === true ? $sessionUrl : '',
856
                'pwd_attempts' => 0,
857
                'error' => 'user_is_locked',
858
                'message' => langHdl('account_is_locked'),
859
                'first_connection' => $superGlobal->get('validite_pw', 'SESSION') === false ? true : false,
860
                'password_complexity' => TP_PW_COMPLEXITY[$superGlobal->get('user_pw_complexity', 'SESSION')][1],
861
                'password_change_expected' => $userInfo['special'] === 'password_change_expected' ? true : false,
862
                'private_key_conform' => $superGlobal->get('private_key', 'SESSION', 'user') !== null
863
                    && empty($superGlobal->get('private_key', 'SESSION', 'user')) === false
864
                    && $superGlobal->get('private_key', 'SESSION', 'user') !== 'none' ? true : false,
865
                'session_key' => $superGlobal->get('key', 'SESSION'),
866
                //'has_psk' => empty($superGlobal->get('encrypted_psk', 'SESSION', 'user')) === false ? true : false,
867
                'can_create_root_folder' => $superGlobal->get('can_create_root_folder', 'SESSION') !== null ? (int) $superGlobal->get('can_create_root_folder', 'SESSION') : '',
868
                'shown_warning_unsuccessful_login' => $superGlobal->get('unsuccessfull_login_attempts_shown', 'SESSION', 'user'),
869
                'nb_unsuccessful_logins' => $superGlobal->get('unsuccessfull_login_attempts_nb', 'SESSION', 'user'),
870
            ],
871
            'encode'
872
        );
873
        return false;
874
    } else {
875
        // User exists in the DB but Password is false
876
        // check if user is locked
877
        $userIsLocked = false;
878
        $nbAttempts = intval($userInfo['no_bad_attempts'] + 1);
879
        if (
880
            $SETTINGS['nb_bad_authentication'] > 0
881
            && intval($SETTINGS['nb_bad_authentication']) < $nbAttempts
882
        ) {
883
            $userIsLocked = true;
884
            // log it
885
            if (
886
                isset($SETTINGS['log_connections']) === true
887
                && (int) $SETTINGS['log_connections'] === 1
888
            ) {
889
                logEvents($SETTINGS, 'user_locked', 'connection', (string) $userInfo['id'], stripslashes($username));
890
            }
891
        }
892
        DB::update(
893
            prefixTable('users'),
894
            [
895
                'key_tempo' => $superGlobal->get('key', 'SESSION'),
896
                'disabled' => $userIsLocked,
897
                'no_bad_attempts' => $nbAttempts,
898
            ],
899
            'id=%i',
900
            $userInfo['id']
901
        );
902
        // What return shoulb we do
903
        if ($userIsLocked === true) {
904
            echo prepareExchangedData(
905
                [
906
                    'value' => $return,
907
                    'user_id' => $superGlobal->get('user_id', 'SESSION') !== null ? (int) $superGlobal->get('user_id', 'SESSION') : '',
908
                    'user_admin' => $superGlobal->get('admin', 'SESSION') !== null ? (int) $superGlobal->get('admin', 'SESSION') : 0,
909
                    'initial_url' => isset($sessionUrl) === true ? $sessionUrl : '',
910
                    'pwd_attempts' => 0,
911
                    'error' => 'user_is_locked',
912
                    'message' => langHdl('account_is_locked'),
913
                    'first_connection' => $superGlobal->get('validite_pw', 'SESSION') === false ? true : false,
914
                    'password_complexity' => TP_PW_COMPLEXITY[$superGlobal->get('user_pw_complexity', 'SESSION')][1],
915
                    'password_change_expected' => $userInfo['special'] === 'password_change_expected' ? true : false,
916
                    'private_key_conform' => $superGlobal->get('user_id', 'SESSION') !== null
917
                        && empty($superGlobal->get('private_key', 'SESSION', 'user')) === false
918
                        && $superGlobal->get('private_key', 'SESSION', 'user') !== 'none' ? true : false,
919
                    'session_key' => $superGlobal->get('key', 'SESSION'),
920
                    //'has_psk' => empty($superGlobal->get('encrypted_psk', 'SESSION', 'user')) === false ? true : false,
921
                    'can_create_root_folder' => $superGlobal->get('can_create_root_folder', 'SESSION') !== null ? (int) $superGlobal->get('can_create_root_folder', 'SESSION') : '',
922
                    'shown_warning_unsuccessful_login' => $superGlobal->get('unsuccessfull_login_attempts_shown', 'SESSION', 'user'),
923
                    'nb_unsuccessful_logins' => $superGlobal->get('unsuccessfull_login_attempts_nb', 'SESSION', 'user'),
924
                ],
925
                'encode'
926
            );
927
            return false;
928
        }
929
        echo prepareExchangedData(
930
            [
931
                'value' => $return,
932
                'user_id' => $superGlobal->get('user_id', 'SESSION') !== null ? (int) $superGlobal->get('user_id', 'SESSION') : '',
933
                'user_admin' => $superGlobal->get('admin', 'SESSION') !== null ? (int) $superGlobal->get('admin', 'SESSION') : 0,
934
                'initial_url' => isset($sessionUrl) === true ? $sessionUrl : '',
935
                'pwd_attempts' => (int) $sessionPwdAttempts,
936
                'error' => 'user_not_exists3',
937
                'message' => langHdl('error_bad_credentials'),
938
                'first_connection' => $superGlobal->get('validite_pw', 'SESSION') === false ? true : false,
939
                'password_complexity' => TP_PW_COMPLEXITY[$superGlobal->get('user_pw_complexity', 'SESSION')][1],
940
                'password_change_expected' => $userInfo['special'] === 'password_change_expected' ? true : false,
941
                'private_key_conform' => $superGlobal->get('user_id', 'SESSION') !== null
942
                        && empty($superGlobal->get('private_key', 'SESSION', 'user')) === false
943
                        && $superGlobal->get('private_key', 'SESSION', 'user') !== 'none' ? true : false,
944
                'session_key' => $superGlobal->get('key', 'SESSION'),
945
                    //'has_psk' => empty($superGlobal->get('encrypted_psk', 'SESSION', 'user')) === false ? true : false,
946
                'can_create_root_folder' => $superGlobal->get('can_create_root_folder', 'SESSION') !== null ? (int) $superGlobal->get('can_create_root_folder', 'SESSION') : '',
947
                'shown_warning_unsuccessful_login' => $superGlobal->get('unsuccessfull_login_attempts_shown', 'SESSION', 'user'),
948
                'nb_unsuccessful_logins' => $superGlobal->get('unsuccessfull_login_attempts_nb', 'SESSION', 'user'),
949
            ],
950
            'encode'
951
        );
952
        return false;
953
    }
954
955
    // Debug
956
    debugIdentify(
957
        DEBUGDUO,
958
        DEBUGDUOFILE,
959
        "\n\n----\n" .
960
            'Identified : ' . filter_var($return, FILTER_SANITIZE_STRING) . "\n\n"
961
    );
962
    echo prepareExchangedData(
963
        [
964
            'value' => $return,
965
            'user_id' => $superGlobal->get('user_id', 'SESSION') !== null ? (int) $superGlobal->get('user_id', 'SESSION') : '',
966
            'user_admin' => $superGlobal->get('admin', 'SESSION') !== null ? (int) $superGlobal->get('admin', 'SESSION') : 0,
967
            'initial_url' => $antiXss->xss_clean($sessionUrl),
968
            'pwd_attempts' => 0,
969
            'error' => false,
970
            'message' => $superGlobal->get('user_upgrade_needed', 'SESSION', 'user') !== null && (int) $superGlobal->get('user_upgrade_needed', 'SESSION', 'user') === 1 ? 'ask_for_otc' : '',
971
            'first_connection' => $superGlobal->get('validite_pw', 'SESSION') === false ? true : false,
972
            'password_complexity' => TP_PW_COMPLEXITY[$superGlobal->get('user_pw_complexity', 'SESSION')][1],
973
            'password_change_expected' => $userInfo['special'] === 'password_change_expected' ? true : false,
974
            'private_key_conform' => $superGlobal->get('user_id', 'SESSION') !== null
975
                && empty($superGlobal->get('private_key', 'SESSION', 'user')) === false
976
                && $superGlobal->get('private_key', 'SESSION', 'user') !== 'none' ? true : false,
977
            'session_key' => $superGlobal->get('key', 'SESSION'),
978
            //'has_psk' => empty($superGlobal->get('encrypted_psk', 'SESSION', 'user')) === false ? true : false,
979
            'can_create_root_folder' => $superGlobal->get('can_create_root_folder', 'SESSION') !== null ? (int) $superGlobal->get('can_create_root_folder', 'SESSION') : '',
980
            'shown_warning_unsuccessful_login' => $superGlobal->get('unsuccessfull_login_attempts_shown', 'SESSION', 'user'),
981
            'nb_unsuccessful_logins' => $superGlobal->get('unsuccessfull_login_attempts_nb', 'SESSION', 'user'),
982
            'upgrade_needed' => isset($userInfo['upgrade_needed']) === true ? (int) $userInfo['upgrade_needed'] : 0,
983
            'special' => isset($userInfo['special']) === true ? (int) $userInfo['special'] : 0,
984
        ],
985
        'encode'
986
    );
987
988
    return true;
989
}
990
991
/**
992
 * Authenticate a user through AD.
993
 *
994
 * @param string $username      Username
995
 * @param array $userInfo       User account information
996
 * @param string $passwordClear Password
997
 * @param array $SETTINGS       Teampass settings
998
 *
999
 * @return array
1000
 */
1001
function authenticateThroughAD(string $username, array $userInfo, string $passwordClear, array $SETTINGS): array
1002
{
1003
    // Build ldap configuration array
1004
    $config = [
1005
        // Mandatory Configuration Options
1006
        'hosts' => [explode(',', $SETTINGS['ldap_hosts'])],
1007
        'base_dn' => $SETTINGS['ldap_bdn'],
1008
        'username' => $SETTINGS['ldap_user_attribute'].'='.$username.','.(isset($SETTINGS['ldap_dn_additional_user_dn']) ? $SETTINGS['ldap_dn_additional_user_dn'].',' : '').$SETTINGS['ldap_bdn'],
1009
        'password' => $passwordClear,
1010
1011
        // Optional Configuration Options
1012
        'port' => $SETTINGS['ldap_port'],
1013
        'use_ssl' => $SETTINGS['ldap_ssl'] === 1 ? true : false,
1014
        'use_tls' => $SETTINGS['ldap_tls'] === 1 ? true : false,
1015
        'version' => 3,
1016
        'timeout' => 5,
1017
        'follow_referrals' => false,
1018
1019
        // Custom LDAP Options
1020
        'options' => [
1021
            // See: http://php.net/ldap_set_option
1022
            LDAP_OPT_X_TLS_REQUIRE_CERT => LDAP_OPT_X_TLS_HARD,
1023
        ],
1024
    ];
1025
    // Load expected libraries
1026
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Illuminate/Contracts/Auth/Authenticatable.php';
1027
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Tightenco/Collect/Support/Traits/EnumeratesValues.php';
1028
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Tightenco/Collect/Support/Traits/Macroable.php';
1029
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Tightenco/Collect/Support/helpers.php';
1030
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Tightenco/Collect/Support/Arr.php';
1031
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Tightenco/Collect/Contracts/Support/Jsonable.php';
1032
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Tightenco/Collect/Contracts/Support/Arrayable.php';
1033
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Tightenco/Collect/Support/Enumerable.php';
1034
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Tightenco/Collect/Support/Collection.php';
1035
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/CarbonTimeZone.php';
1036
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Units.php';
1037
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Week.php';
1038
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Timestamp.php';
1039
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Test.php';
1040
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/ObjectInitialisation.php';
1041
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Serialization.php';
1042
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/IntervalRounding.php';
1043
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Rounding.php';
1044
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Localization.php';
1045
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Options.php';
1046
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Cast.php';
1047
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Mutability.php';
1048
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Modifiers.php';
1049
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Mixin.php';
1050
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Macro.php';
1051
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Difference.php';
1052
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Creator.php';
1053
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Converter.php';
1054
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Comparison.php';
1055
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Boundaries.php';
1056
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Date.php';
1057
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/CarbonInterface.php';
1058
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Carbon.php';
1059
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/LdapRecord/DetectsErrors.php';
1060
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/LdapRecord/Connection.php';
1061
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/LdapRecord/LdapInterface.php';
1062
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/LdapRecord/LdapBase.php';
1063
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/LdapRecord/Ldap.php';
1064
    $ad = new SplClassLoader('LdapRecord', '../includes/libraries');
1065
    $ad->register();
1066
    $connection = new Connection($config);
1067
    // COnnect to LDAP
1068
    try {
1069
        $connection->connect();
1070
        Container::addConnection($connection);
1071
    } catch (\LdapRecord\Auth\BindException $e) {
1072
        $error = $e->getDetailedError();
1073
        return [
1074
            'error' => true,
1075
            'message' => langHdl('error').' : '.$error->getErrorCode().' - '.$error->getErrorMessage(). '<br>'.$error->getDiagnosticMessage().' '.$config['username'],
1076
1077
        ];
1078
    }
1079
1080
    $query = $connection->query();
1081
    try {
1082
        $entry = $query->findBy(
1083
            $SETTINGS['ldap_user_attribute'],
1084
            $username
1085
        );
1086
    } catch (\LdapRecord\Models\ModelNotFoundException $ex) {
1087
        // Not found.
1088
        return [
1089
            'error' => true,
1090
            'message' => langHdl('error_no_user_in_ad'),
1091
        ];
1092
    }
1093
1094
    // Check shadowexpire attribute - if === 1 then user disabled
1095
    if (isset($entry['shadowexpire'][0]) === true && (int) $entry['shadowexpire'][0] === 1) {
1096
        return [
1097
            'error' => true,
1098
            'message' => langHdl('error_ad_user_expired'),
1099
        ];
1100
    }
1101
1102
    // load passwordLib library
1103
    $pwdlib = new SplClassLoader('PasswordLib', $SETTINGS['cpassman_dir'] . '/includes/libraries');
1104
    $pwdlib->register();
1105
    $pwdlib = new PasswordLib\PasswordLib();
1106
    $hashedPassword = $pwdlib->createPasswordHash($passwordClear);
1107
    //If user has never been connected then erase current pwd with the ldap's one
1108
    if (empty($userInfo['pw']) === true) {
1109
        // Password are similar in Teampass and AD
1110
        DB::update(
1111
            prefixTable('users'),
1112
            [
1113
                'pw' => $hashedPassword,
1114
            ],
1115
            'id = %i',
1116
            $userInfo['id']
1117
        );
1118
    /*} elseif ($userInfo['special'] === 'user_added_from_ldap') {
1119
        // Case where user has been added from LDAP and never being connected to TP
1120
        // We need to create his keys
1121
        $userKeys = generateUserKeys($passwordClear);
1122
1123
        // NOw update
1124
        DB::update(
1125
            prefixTable('users'),
1126
            array(
1127
                'pw' => $hashedPassword,
1128
                'public_key' => $userKeys['public_key'],
1129
                'private_key' => $userKeys['private_key'],
1130
            ),
1131
            'id = %i',
1132
            $userInfo['id']
1133
        );*/
1134
    } elseif ($pwdlib->verifyPasswordHash($passwordClear, $userInfo['pw']) === false) {
1135
        // Case where user is auth by LDAP but his password in Teampass is not synchronized
1136
        // For example when user has changed his password in AD.
1137
        // So we need to update it in Teampass and ask for private key re-encryption
1138
        DB::update(
1139
            prefixTable('users'),
1140
            [
1141
                'pw' => $hashedPassword,
1142
                'special' => 'auth-pwd-change',
1143
            ],
1144
            'id = %i',
1145
            $userInfo['id']
1146
        );
1147
    }
1148
1149
    $userInfo['pw'] = $hashedPassword;
1150
    return [
1151
        'error' => false,
1152
        'message' => '',
1153
    ];
1154
}
1155
1156
/**
1157
 * Undocumented function.
1158
 *
1159
 * @param string|array|resource $dataReceived Received data
1160
 * @param string                $userInfo     Result of query
1161
 * @param array                 $SETTINGS     Teampass settings
1162
 *
1163
 * @return array
1164
 */
1165
function yubicoMFACheck($dataReceived, string $userInfo, array $SETTINGS): array
1166
{
1167
    // Load superGlobals
1168
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
1169
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
1170
    $sessionAdmin = $superGlobal->get('user_admin', 'SESSION');
1171
    $sessionUrl = $superGlobal->get('initial_url', 'SESSION');
1172
    $sessionPwdAttempts = $superGlobal->get('pwd_attempts', 'SESSION');
1173
    // Init
1174
    $yubico_key = htmlspecialchars_decode($dataReceived['yubico_key']);
1175
    $yubico_user_key = htmlspecialchars_decode($dataReceived['yubico_user_key']);
1176
    $yubico_user_id = htmlspecialchars_decode($dataReceived['yubico_user_id']);
1177
    if (empty($yubico_user_key) === false && empty($yubico_user_id) === false) {
1178
        // save the new yubico in user's account
1179
        DB::update(
1180
            prefixTable('users'),
1181
            [
1182
                'yubico_user_key' => $yubico_user_key,
1183
                'yubico_user_id' => $yubico_user_id,
1184
            ],
1185
            'id=%i',
1186
            $userInfo['id']
1187
        );
1188
    } else {
1189
        // Check existing yubico credentials
1190
        if ($userInfo['yubico_user_key'] === 'none' || $userInfo['yubico_user_id'] === 'none') {
1191
            return [
1192
                'error' => true,
1193
                'value' => '',
1194
                'user_admin' => isset($sessionAdmin) ? (int) $sessionAdmin : '',
1195
                'initial_url' => isset($sessionUrl) === true ? $sessionUrl : '',
1196
                'pwd_attempts' => (int) $sessionPwdAttempts,
1197
                'error' => 'no_user_yubico_credentials',
1198
                'message' => '',
1199
                'proceedIdentification' => false,
1200
            ];
1201
        }
1202
        $yubico_user_key = $userInfo['yubico_user_key'];
1203
        $yubico_user_id = $userInfo['yubico_user_id'];
1204
    }
1205
1206
    // Now check yubico validity
1207
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Authentication/Yubico/Yubico.php';
1208
    $yubi = new Auth_Yubico($yubico_user_id, $yubico_user_key);
1209
    $auth = $yubi->verify($yubico_key);
1210
    //, null, null, null, 60
1211
1212
    if (PEAR::isError($auth)) {
1213
        return [
1214
            'error' => true,
1215
            'value' => '',
1216
            'user_admin' => isset($sessionAdmin) ? (int) $sessionAdmin : '',
1217
            'initial_url' => isset($sessionUrl) === true ? $sessionUrl : '',
1218
            'pwd_attempts' => (int) $sessionPwdAttempts,
1219
            'error' => 'bad_user_yubico_credentials',
1220
            'message' => langHdl('yubico_bad_code'),
1221
            'proceedIdentification' => false,
1222
        ];
1223
    }
1224
1225
    return [
1226
        'error' => false,
1227
        'message' => '',
1228
        'proceedIdentification' => true,
1229
    ];
1230
}
1231
1232
/**
1233
 * Undocumented function.
1234
 *
1235
 * @param string $username      User name
1236
 * @param string $passwordClear User password in clear
1237
 * @param string $retLDAP       Received data from LDAP
1238
 * @param array  $SETTINGS      Teampass settings
1239
 *
1240
 * @return array
1241
 */
1242
function ldapCreateUser(string $username, string $passwordClear, string $retLDAP, array $SETTINGS): array
1243
{
1244
    // Generate user keys pair
1245
    $userKeys = generateUserKeys($passwordClear);
1246
    // Insert user in DB
1247
    DB::insert(
1248
        prefixTable('users'),
1249
        [
1250
            'login' => $username,
1251
            'pw' => $retLDAP['hashedPassword'],
1252
            'email' => isset($retLDAP['user_info_from_ad'][0]['mail'][0]) === false ? '' : $retLDAP['user_info_from_ad'][0]['mail'][0],
1253
            'name' => $retLDAP['user_info_from_ad'][0]['givenname'][0],
1254
            'lastname' => $retLDAP['user_info_from_ad'][0]['sn'][0],
1255
            'admin' => '0',
1256
            'gestionnaire' => '0',
1257
            'can_manage_all_users' => '0',
1258
            'personal_folder' => $SETTINGS['enable_pf_feature'] === '1' ? '1' : '0',
1259
            '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'),
1260
            'groupes_interdits' => '',
1261
            'groupes_visibles' => '',
1262
            'last_pw_change' => (int) time(),
1263
            'user_language' => $SETTINGS['default_language'],
1264
            'encrypted_psk' => '',
1265
            '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,
1266
            'public_key' => $userKeys['public_key'],
1267
            'private_key' => $userKeys['private_key'],
1268
        ]
1269
    );
1270
    $newUserId = DB::insertId();
1271
    // Create personnal folder
1272
    if (isset($SETTINGS['enable_pf_feature']) === true && $SETTINGS['enable_pf_feature'] === '1') {
1273
        DB::insert(
1274
            prefixTable('nested_tree'),
1275
            [
1276
                'parent_id' => '0',
1277
                'title' => $newUserId,
1278
                'bloquer_creation' => '0',
1279
                'bloquer_modification' => '0',
1280
                'personal_folder' => '1',
1281
            ]
1282
        );
1283
        // Rebuild tree
1284
        $tree = new SplClassLoader('Tree\NestedTree', $SETTINGS['cpassman_dir'] . '/includes/libraries');
1285
        $tree->register();
1286
        $tree = new Tree\NestedTree\NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
1287
        $tree->rebuild();
1288
    }
1289
1290
    return [
1291
        'error' => false,
1292
        'message' => '',
1293
        'proceedIdentification' => true,
1294
        'user_initial_creation_through_ldap' => true,
1295
    ];
1296
}
1297
1298
/**
1299
 * Undocumented function.
1300
 *
1301
 * @param string                $username     Username
1302
 * @param string                $userInfo     Result of query
1303
 * @param string|array|resource $dataReceived DataReceived
1304
 * @param array                 $SETTINGS     Teampass settings
1305
 *
1306
 * @return array
1307
 */
1308
function googleMFACheck(string $username, string $userInfo, $dataReceived, array $SETTINGS): array
1309
{
1310
    if (
1311
        isset($dataReceived['GACode']) === true
1312
        && empty($dataReceived['GACode']) === false
1313
    ) {
1314
        // Load superGlobals
1315
        include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
1316
        $superGlobal = new protect\SuperGlobal\SuperGlobal();
1317
        $sessionAdmin = $superGlobal->get('user_admin', 'SESSION');
1318
        $sessionUrl = $superGlobal->get('initial_url', 'SESSION');
1319
        $sessionPwdAttempts = $superGlobal->get('pwd_attempts', 'SESSION');
1320
        // load library
1321
        include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Authentication/TwoFactorAuth/TwoFactorAuth.php';
1322
        // create new instance
1323
        $tfa = new Authentication\TwoFactorAuth\TwoFactorAuth($SETTINGS['ga_website_name']);
1324
        // Init
1325
        $firstTime = [];
1326
        // now check if it is the 1st time the user is using 2FA
1327
        if ($userInfo['ga_temporary_code'] !== 'none' && $userInfo['ga_temporary_code'] !== 'done') {
1328
            if ($userInfo['ga_temporary_code'] !== $dataReceived['GACode']) {
1329
                return [
1330
                    'error' => true,
1331
                    'message' => langHdl('ga_bad_code'),
1332
                    'proceedIdentification' => false,
1333
                    'mfaStatus' => '',
1334
                ];
1335
            }
1336
1337
            // If first time with MFA code
1338
            $proceedIdentification = false;
1339
            $mfaStatus = 'ga_temporary_code_correct';
1340
            $mfaMessage = langHdl('ga_flash_qr_and_login');
1341
            // generate new QR
1342
            $new_2fa_qr = $tfa->getQRCodeImageAsDataUri(
1343
                'Teampass - ' . $username,
1344
                $userInfo['ga']
1345
            );
1346
            // clear temporary code from DB
1347
            DB::update(
1348
                prefixTable('users'),
1349
                [
1350
                    'ga_temporary_code' => 'done',
1351
                ],
1352
                'id=%i',
1353
                $userInfo['id']
1354
            );
1355
            $firstTime = [
1356
                'value' => '<img src="' . $new_2fa_qr . '">',
1357
                'user_admin' => isset($sessionAdmin) ? (int) $sessionAdmin : '',
1358
                'initial_url' => isset($sessionUrl) === true ? $sessionUrl : '',
1359
                'pwd_attempts' => (int) $sessionPwdAttempts,
1360
                'error' => false,
1361
                'message' => $mfaMessage,
1362
                'mfaStatus' => $mfaStatus,
1363
            ];
1364
        } else {
1365
            // verify the user GA code
1366
            if ($tfa->verifyCode($userInfo['ga'], $dataReceived['GACode'])) {
1367
                $proceedIdentification = true;
1368
            } else {
1369
                return [
1370
                    'error' => true,
1371
                    'message' => langHdl('ga_bad_code'),
1372
                    'proceedIdentification' => false,
1373
                ];
1374
            }
1375
        }
1376
    } else {
1377
        return [
1378
            'error' => true,
1379
            'message' => langHdl('ga_bad_code'),
1380
            'proceedIdentification' => false,
1381
        ];
1382
    }
1383
1384
    return [
1385
        'error' => false,
1386
        'message' => '',
1387
        'proceedIdentification' => $proceedIdentification,
1388
        'firstTime' => $firstTime,
1389
    ];
1390
}
1391
1392
/**
1393
 * Undocumented function.
1394
 *
1395
 * @param string                $passwordClear Password in clear
1396
 * @param array|string          $userInfo      Array of user data
1397
 * @param array|string|resource $dataReceived  Received data
1398
 * @param string                $username      User name
1399
 * @param array                 $SETTINGS      Teampass settings
1400
 *
1401
 * @return bool
1402
 */
1403
function checkCredentials($passwordClear, $userInfo, $dataReceived, $username, $SETTINGS)
1404
{
1405
    // Set to false
1406
    $userPasswordVerified = false;
1407
    // load passwordLib library
1408
    include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php';
1409
    $pwdlib = new SplClassLoader('PasswordLib', $SETTINGS['cpassman_dir'] . '/includes/libraries');
1410
    $pwdlib->register();
1411
    $pwdlib = new PasswordLib\PasswordLib();
1412
    // Check if old encryption used
1413
    if (
1414
        crypt($passwordClear, $userInfo['pw']) === $userInfo['pw']
1415
        && empty($userInfo['pw']) === false
1416
    ) {
1417
        $userPasswordVerified = true;
1418
        //update user's password
1419
        $userInfo['pw'] = $pwdlib->createPasswordHash($passwordClear);
1420
        DB::update(
1421
            prefixTable('users'),
1422
            [
1423
                'pw' => $userInfo['pw'],
1424
            ],
1425
            'id=%i',
1426
            $userInfo['id']
1427
        );
1428
    }
1429
    //echo $passwordClear." - ".$userInfo['pw']." - ".$pwdlib->verifyPasswordHash($passwordClear, $userInfo['pw'])." ;; ";
1430
    // check the given password
1431
    if ($userPasswordVerified !== true) {
1432
        if ($pwdlib->verifyPasswordHash($passwordClear, $userInfo['pw']) === true) {
1433
            $userPasswordVerified = true;
1434
        } else {
1435
            // 2.1.27.24 - manage passwords
1436
            if ($pwdlib->verifyPasswordHash(htmlspecialchars_decode($dataReceived['pw']), $userInfo['pw']) === true) {
1437
                // then the auth is correct but needs to be adapted in DB since change of encoding
1438
                $userInfo['pw'] = $pwdlib->createPasswordHash($passwordClear);
1439
                DB::update(
1440
                    prefixTable('users'),
1441
                    [
1442
                        'pw' => $userInfo['pw'],
1443
                    ],
1444
                    'id=%i',
1445
                    $userInfo['id']
1446
                );
1447
                $userPasswordVerified = true;
1448
            } else {
1449
                $userPasswordVerified = false;
1450
                logEvents(
1451
                    $SETTINGS,
1452
                    'failed_auth',
1453
                    'user_password_not_correct',
1454
                    '',
1455
                    '',
1456
                    stripslashes($username)
1457
                );
1458
            }
1459
        }
1460
    }
1461
1462
    return $userPasswordVerified;
1463
}
1464
1465
/**
1466
 * Undocumented function.
1467
 *
1468
 * @param bool   $enabled text1
1469
 * @param string $dbgFile text2
1470
 * @param string $text    text3
1471
 */
1472
function debugIdentify(bool $enabled, string $dbgFile, string $text): void
1473
{
1474
    if ($enabled === true) {
1475
        $fp = fopen($dbgFile, 'a');
1476
        if ($fp !== false) {
1477
            fwrite(
1478
                $fp,
1479
                $text
1480
            );
1481
        }
1482
    }
1483
}
1484
1485
1486
1487
function identifyGetUserCredentials(
1488
    array $SETTINGS,
1489
    string $serverPHPAuthUser,
1490
    string $serverPHPAuthPw,
1491
    string $userPassword,
1492
    string $userLogin
1493
): array
1494
{
1495
    if ((int) $SETTINGS['enable_http_request_login'] === 1
1496
        && $serverPHPAuthUser !== null
1497
        && (int) $SETTINGS['maintenance_mode'] === 1
1498
    ) {
1499
        if (strpos($serverPHPAuthUser, '@') !== false) {
1500
            return [
1501
                'username' => explode('@', $serverPHPAuthUser)[0],
1502
                'passwordClear' => $serverPHPAuthPw
1503
            ];
1504
        }
1505
        
1506
        if (strpos($serverPHPAuthUser, '\\') !== false) {
1507
            return [
1508
                'username' => explode('\\', $serverPHPAuthUser)[1],
1509
                'passwordClear' => $serverPHPAuthPw
1510
            ];
1511
        }
1512
1513
        return [
1514
            'username' => $serverPHPAuthPw,
1515
            'passwordClear' => $serverPHPAuthPw
1516
        ];
1517
    }
1518
    
1519
    return [
1520
        'username' => $userLogin,
1521
        'passwordClear' => $userPassword
1522
    ];
1523
}
1524
1525
1526
    
1527
1528
function identifyDoInitialChecks(
1529
    $SETTINGS,
1530
    int $sessionPwdAttempts,
1531
    string $username,
1532
    int $sessionAdmin,
1533
    string $sessionUrl,
1534
    string $user_2fa_selection
1535
): array
1536
{
1537
    // Brute force management
1538
    if ($sessionPwdAttempts > 3) {
1539
        // Load superGlobals
1540
        include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
1541
        $superGlobal = new protect\SuperGlobal\SuperGlobal();
1542
        $superGlobal->put('next_possible_pwd_attempts', time() + 10, 'SESSION');
1543
        $superGlobal->put('pwd_attempts', 0, 'SESSION');
1544
1545
        logEvents($SETTINGS, 'failed_auth', 'user_not_exists', '', stripslashes($username), stripslashes($username));
1546
        
1547
        return [
1548
            'error' => true,
1549
            'array' => [
1550
                'value' => 'bruteforce_wait',
1551
                'user_admin' => isset($sessionAdmin) ? (int) $sessionAdmin : 0,
1552
                'initial_url' => isset($sessionUrl) === true ? $sessionUrl : '',
1553
                'pwd_attempts' => 0,
1554
                'error' => true,
1555
                'message' => langHdl('error_bad_credentials_more_than_3_times'),
1556
            ]
1557
        ];
1558
    }
1559
1560
    // Check 2FA
1561
    if ((((int) $SETTINGS['yubico_authentication'] === 1 && empty($user_2fa_selection) === true)
1562
        || ((int) $SETTINGS['google_authentication'] === 1 && empty($user_2fa_selection) === true)
1563
        || ((int) $SETTINGS['duo'] === 1 && empty($user_2fa_selection) === true))
1564
        && ($username !== 'admin' || ((int) $SETTINGS['admin_2fa_required'] === 1 && $username === 'admin'))
1565
    ) {
1566
        return [
1567
            'error' => true,
1568
            'array' => [
1569
                'value' => '2fa_not_set',
1570
                'user_admin' => isset($sessionAdmin) ? (int) $sessionAdmin : 0,
1571
                'initial_url' => isset($sessionUrl) === true ? $sessionUrl : '',
1572
                'pwd_attempts' => (int) $sessionPwdAttempts,
1573
                'error' => '2fa_not_set',
1574
                'message' => langHdl('2fa_credential_not_correct'),
1575
            ]
1576
        ];
1577
    }
1578
1579
    // Check if user exists
1580
    $userInfo = DB::queryFirstRow(
1581
        'SELECT *
1582
        FROM ' . prefixTable('users') . ' WHERE login=%s',
1583
        $username
1584
    );
1585
    $counter = DB::count();
1586
    // User doesn't exist then stop
1587
    if ($counter === 0) {
1588
        logEvents($SETTINGS, 'failed_auth', 'user_not_exists', '', stripslashes($username), stripslashes($username));
1589
1590
        return [
1591
            'error' => true,
1592
            'array' => [
1593
                'error' => 'user_not_exists',
1594
                'message' => langHdl('error_bad_credentials'),
1595
                'pwd_attempts' => (int) $sessionPwdAttempts,
1596
                'user_admin' => isset($sessionAdmin) ? (int) $sessionAdmin : 0,
1597
                'initial_url' => isset($sessionUrl) === true ? $sessionUrl : '',
1598
            ]
1599
        ];
1600
    }
1601
1602
    // has user to be auth with mfa?
1603
    if (is_null($userInfo['fonction_id']) === true) {
1604
        $userInfo['fonction_id'] = false;
1605
    } else {
1606
        $userInfo['mfa_auth_requested'] = mfa_auth_requested(
1607
            $userInfo['fonction_id'],
1608
            is_null($SETTINGS['mfa_for_roles']) === true ? '' : $SETTINGS['mfa_for_roles']
1609
        );
1610
    }
1611
1612
    // Manage Maintenance mode
1613
    if ((int) $SETTINGS['maintenance_mode'] === 1 && (int) $userInfo['admin'] === 0
1614
    ) {
1615
        return [
1616
            'error' => true,
1617
            'array' => [
1618
                'value' => '',
1619
                'user_admin' => (int) $userInfo['admin'],
1620
                'initial_url' => '',
1621
                'pwd_attempts' => '',
1622
                'error' => 'maintenance_mode_enabled',
1623
                'message' => '',
1624
            ]
1625
        ];
1626
    }
1627
1628
    // Return some usefull information about user
1629
    return [
1630
        'error' => false,
1631
        'user_mfa_mode' => $user_2fa_selection,
1632
        'userInfo' => $userInfo,
1633
    ];
1634
}