Passed
Push — teampass_3.0 ( 94902e...9fc4c0 )
by Nils
04:18
created

identifyDoLDAPChecks()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 44
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

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