Passed
Push — teampass_3.0 ( 6d5809...2c08f5 )
by Nils
08:42 queued 04:08
created

authenticateThroughAD()   D

Complexity

Conditions 13
Paths 256

Size

Total Lines 156
Code Lines 108

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 13
eloc 108
nc 256
nop 4
dl 0
loc 156
rs 4.0666
c 0
b 0
f 0

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