Passed
Push — master ( 123665...6c557a )
by Nils
04:55
created

findTpConfigFile()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 13
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

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

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

903
    /** @scrutinizer ignore-unused */ $key,

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

Loading history...
904
    $SETTINGS
905
) : bool 
906
{
907
    $userIsLocked = false;
908
    $nbAttempts++;
909
    if (
910
        (int) $SETTINGS['nb_bad_authentication'] > 0
911
        && (int) $SETTINGS['nb_bad_authentication'] < $nbAttempts
912
    ) {
913
        // User is now locked as too many attempts
914
        $userIsLocked = true;
915
916
        // log it
917
        if (isKeyExistingAndEqual('log_connections', 1, $SETTINGS) === true) {
918
            logEvents($SETTINGS, 'user_locked', 'connection', (string) $userId, stripslashes($username));
919
        }
920
    }
921
    
922
    DB::update(
923
        prefixTable('users'),
924
        [
925
            'key_tempo' => $superGlobal->get('key', 'SESSION'),
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $superGlobal seems to be never defined.
Loading history...
926
            'disabled' => $userIsLocked,
927
            'no_bad_attempts' => $nbAttempts,
928
        ],
929
        'id=%i',
930
        $userId
931
    );
932
933
    return $userIsLocked;
934
}
935
936
937
/**
938
 * 
939
 * Prepare user keys
940
 * 
941
 * @param array $userInfo   User account information
942
 * @param string $passwordClear
943
 *
944
 * @return array
945
 */
946
function prepareUserEncryptionKeys($userInfo, $passwordClear) : array
947
{
948
    if (is_null($userInfo['private_key']) === true || empty($userInfo['private_key']) === true || $userInfo['private_key'] === 'none') {
949
        // No keys have been generated yet
950
        // Create them
951
        $userKeys = generateUserKeys($passwordClear);
952
953
        return [
954
            'public_key' => $userKeys['public_key'],
955
            'private_key_clear' => $userKeys['private_key_clear'],
956
            'update_keys_in_db' => [
957
                'public_key' => $userKeys['public_key'],
958
                'private_key' => $userKeys['private_key'],
959
            ],
960
        ];
961
    } 
962
    
963
    if ($userInfo['special'] === 'generate-keys') {
964
        return [
965
            'public_key' => $userInfo['public_key'],
966
            'private_key_clear' => '',
967
            'update_keys_in_db' => [],
968
        ];
969
    }
970
    
971
    // Don't perform this in case of special login action
972
    if ($userInfo['special'] === 'otc_is_required_on_next_login' || $userInfo['special'] === 'user_added_from_ldap') {
973
        return [
974
            'public_key' => $userInfo['public_key'],
975
            'private_key_clear' => '',
976
            'update_keys_in_db' => [],
977
        ];
978
    }
979
    
980
    // Uncrypt private key
981
    return [
982
        'public_key' => $userInfo['public_key'],
983
        'private_key_clear' => decryptPrivateKey($passwordClear, $userInfo['private_key']),
984
        'update_keys_in_db' => [],
985
    ];
986
}
987
988
989
/**
990
 * CHECK PASSWORD VALIDITY
991
 * Don't take into consideration if LDAP in use
992
 * 
993
 * @param array $userInfo                       User account information
994
 * @param int $numDaysBeforePwExpiration
995
 * @param int $lastPwChange
996
 * @param array $SETTINGS                       Teampass settings
997
 *
998
 * @return array
999
 */
1000
function checkUserPasswordValidity($userInfo, $numDaysBeforePwExpiration, $lastPwChange, $SETTINGS)
1001
{
1002
    if (isKeyExistingAndEqual('ldap_mode', 1, $SETTINGS) === true) {
1003
        return [
1004
            'validite_pw' => true,
1005
            'last_pw_change' => true,
1006
            'user_force_relog' => '',
1007
            'numDaysBeforePwExpiration' => '',
1008
        ];
1009
    }
1010
1011
    if (isset($userInfo['last_pw_change']) === true) {
1012
        if ((int) $SETTINGS['pw_life_duration'] === 0) {
1013
            return [
1014
                'validite_pw' => true,
1015
                'last_pw_change' => '',
1016
                'user_force_relog' => 'infinite',
1017
                'numDaysBeforePwExpiration' => '',
1018
            ];
1019
        }
1020
        
1021
        return [
1022
            'validite_pw' => $numDaysBeforePwExpiration <= 0 ? false : true,
1023
            'last_pw_change' => '',
1024
            'user_force_relog' => 'infinite',
1025
            'numDaysBeforePwExpiration' => $SETTINGS['pw_life_duration'] - round(
1026
                (mktime(0, 0, 0, (int) date('m'), (int) date('d'), (int) date('y')) - $lastPwChange) / (24 * 60 * 60)),
1027
        ];
1028
    } else {
1029
        return [
1030
            'validite_pw' => false,
1031
            'last_pw_change' => '',
1032
            'user_force_relog' => '',
1033
            'numDaysBeforePwExpiration' => '',
1034
        ];
1035
    }
1036
}
1037
1038
1039
/**
1040
 * Authenticate a user through AD.
1041
 *
1042
 * @param string $username      Username
1043
 * @param array $userInfo       User account information
1044
 * @param string $passwordClear Password
1045
 * @param array $SETTINGS       Teampass settings
1046
 *
1047
 * @return array
1048
 */
1049
function authenticateThroughAD(string $username, array $userInfo, string $passwordClear, array $SETTINGS): array
1050
{
1051
    // Load expected libraries
1052
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Illuminate/Contracts/Auth/Authenticatable.php';
1053
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Tightenco/Collect/Support/Traits/EnumeratesValues.php';
1054
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Tightenco/Collect/Support/Traits/Macroable.php';
1055
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Tightenco/Collect/Support/helpers.php';
1056
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Tightenco/Collect/Support/Arr.php';
1057
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Tightenco/Collect/Contracts/Support/Jsonable.php';
1058
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Tightenco/Collect/Contracts/Support/Arrayable.php';
1059
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Tightenco/Collect/Support/Enumerable.php';
1060
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Tightenco/Collect/Support/Collection.php';
1061
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/CarbonTimeZone.php';
1062
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Units.php';
1063
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Week.php';
1064
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Timestamp.php';
1065
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Test.php';
1066
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/ObjectInitialisation.php';
1067
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Serialization.php';
1068
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/IntervalRounding.php';
1069
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Rounding.php';
1070
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Localization.php';
1071
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Options.php';
1072
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Cast.php';
1073
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Mutability.php';
1074
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Modifiers.php';
1075
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Mixin.php';
1076
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Macro.php';
1077
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Difference.php';
1078
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Creator.php';
1079
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Converter.php';
1080
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Comparison.php';
1081
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Boundaries.php';
1082
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Traits/Date.php';
1083
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/CarbonInterface.php';
1084
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Carbon/Carbon.php';
1085
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/LdapRecord/DetectsErrors.php';
1086
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/LdapRecord/Connection.php';
1087
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/LdapRecord/LdapInterface.php';
1088
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/LdapRecord/HandlesConnection.php';
1089
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/LdapRecord/Ldap.php';
1090
    $ad = new SplClassLoader('LdapRecord', '../includes/libraries');
1091
    $ad->register();
1092
1093
    // Build ldap configuration array
1094
    $config = [
1095
        // Mandatory Configuration Options
1096
        'hosts' => [explode(',', $SETTINGS['ldap_hosts'])],
1097
        'base_dn' => $SETTINGS['ldap_bdn'],
1098
        'username' => $SETTINGS['ldap_username'],
1099
        'password' => $SETTINGS['ldap_password'],
1100
1101
        // Optional Configuration Options
1102
        'port' => $SETTINGS['ldap_port'],
1103
        'use_ssl' => (int) $SETTINGS['ldap_ssl'] === 1 ? true : false,
1104
        'use_tls' => (int) $SETTINGS['ldap_tls'] === 1 ? true : false,
1105
        'version' => 3,
1106
        'timeout' => 5,
1107
        'follow_referrals' => false,
1108
1109
        // Custom LDAP Options
1110
        'options' => [
1111
            // See: http://php.net/ldap_set_option
1112
            LDAP_OPT_X_TLS_REQUIRE_CERT => LDAP_OPT_X_TLS_HARD,
1113
        ],
1114
    ];
1115
    //prepare connection
1116
    $connection = new Connection($config);
1117
    
1118
    try {
1119
        // Connect to LDAP
1120
        $connection->connect();
1121
1122
        // Get user info from AD
1123
        // We want to isolate attribute ldap_user_attribute
1124
        $userADInfos = $connection->query()
1125
            ->where((isset($SETTINGS['ldap_user_attribute']) ===true && empty($SETTINGS['ldap_user_attribute']) === false) ? $SETTINGS['ldap_user_attribute'] : 'distinguishedname', '=', $username)
1126
            ->firstOrFail();
1127
1128
        // Check shadowexpire attribute - if === 1 then user disabled
1129
        if (
1130
            (isset($userADInfos['shadowexpire'][0]) === true && (int) $userADInfos['shadowexpire'][0] === 1)
1131
            ||
1132
            (isset($userADInfos['accountexpires'][0]) === true && (int) $userADInfos['accountexpires'][0] < time())
1133
        ) {
1134
            return [
1135
                'error' => true,
1136
                'message' => langHdl('error_ad_user_expired'),
1137
            ];
1138
        }
1139
1140
        // User auth attempt
1141
        if ($SETTINGS['ldap_type'] === 'ActiveDirectory') {
1142
            $userAuthAttempt = $connection->auth()->attempt(
1143
                $userADInfos[(isset($SETTINGS['ldap_user_dn_attribute']) === true && empty($SETTINGS['ldap_user_dn_attribute']) === false) ? $SETTINGS['ldap_user_dn_attribute'] : 'cn'][0],
1144
                $passwordClear
1145
            );
1146
        } else {
1147
            $userAuthAttempt = $connection->auth()->attempt(
1148
                $userADInfos['dn'],
1149
                $passwordClear
1150
            );
1151
        }
1152
1153
    } catch (\LdapRecord\Auth\BindException $e) {
1154
        $error = $e->getDetailedError();
1155
        return [
1156
            'error' => true,
1157
            'message' => langHdl('error').' : '.$error->getErrorCode().' - '.$error->getErrorMessage(). '<br>'.$error->getDiagnosticMessage(),
1158
1159
        ];
1160
    }
1161
1162
    // User is not auth then return error
1163
    if ($userAuthAttempt === false) {
1164
        return [
1165
            'error' => true,
1166
            'message' => "Error : User could not be authentificated",
1167
        ];
1168
    }
1169
1170
    // load passwordLib library
1171
    $pwdlib = new SplClassLoader('PasswordLib', $SETTINGS['cpassman_dir'] . '/includes/libraries');
1172
    $pwdlib->register();
1173
    $pwdlib = new PasswordLib\PasswordLib();
1174
    $hashedPassword = $pwdlib->createPasswordHash($passwordClear);
1175
    //If user has never been connected then erase current pwd with the ldap's one
1176
    if (empty($userInfo['pw']) === true) {
1177
        // Password are similar in Teampass and AD
1178
        DB::update(
1179
            prefixTable('users'),
1180
            [
1181
                'pw' => $hashedPassword,
1182
            ],
1183
            'id = %i',
1184
            $userInfo['id']
1185
        );
1186
    } elseif ($userInfo['special'] === 'user_added_from_ldap') {
1187
        // Case where user has been added from LDAP and never being connected to TP
1188
        DB::update(
1189
            prefixTable('users'),
1190
            array(
1191
                'pw' => $hashedPassword,
1192
            ),
1193
            'id = %i',
1194
            $userInfo['id']
1195
        );
1196
    } elseif ($pwdlib->verifyPasswordHash($passwordClear, $userInfo['pw']) === false) {
1197
        // Case where user is auth by LDAP but his password in Teampass is not synchronized
1198
        // For example when user has changed his password in AD.
1199
        // So we need to update it in Teampass and ask for private key re-encryption
1200
        DB::update(
1201
            prefixTable('users'),
1202
            [
1203
                'pw' => $hashedPassword,
1204
                //'special' => 'auth-pwd-change',
1205
            ],
1206
            'id = %i',
1207
            $userInfo['id']
1208
        );
1209
    }
1210
1211
    $userInfo['pw'] = $hashedPassword;
1212
    return [
1213
        'error' => false,
1214
        'message' => '',
1215
    ];
1216
}
1217
1218
/**
1219
 * Undocumented function.
1220
 *
1221
 * @param string|array|resource $dataReceived Received data
1222
 * @param string                $userInfo     Result of query
1223
 * @param array                 $SETTINGS     Teampass settings
1224
 *
1225
 * @return array
1226
 */
1227
function yubicoMFACheck($dataReceived, string $userInfo, array $SETTINGS): array
1228
{
1229
    // Load superGlobals
1230
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
1231
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
1232
    $sessionAdmin = $superGlobal->get('user_admin', 'SESSION');
1233
    $sessionUrl = $superGlobal->get('initial_url', 'SESSION');
1234
    $sessionPwdAttempts = $superGlobal->get('pwd_attempts', 'SESSION');
1235
    // Init
1236
    $yubico_key = htmlspecialchars_decode($dataReceived['yubico_key']);
1237
    $yubico_user_key = htmlspecialchars_decode($dataReceived['yubico_user_key']);
1238
    $yubico_user_id = htmlspecialchars_decode($dataReceived['yubico_user_id']);
1239
    if (empty($yubico_user_key) === false && empty($yubico_user_id) === false) {
1240
        // save the new yubico in user's account
1241
        DB::update(
1242
            prefixTable('users'),
1243
            [
1244
                'yubico_user_key' => $yubico_user_key,
1245
                'yubico_user_id' => $yubico_user_id,
1246
            ],
1247
            'id=%i',
1248
            $userInfo['id']
1249
        );
1250
    } else {
1251
        // Check existing yubico credentials
1252
        if ($userInfo['yubico_user_key'] === 'none' || $userInfo['yubico_user_id'] === 'none') {
1253
            return [
1254
                'error' => true,
1255
                'value' => '',
1256
                'user_admin' => isset($sessionAdmin) ? (int) $sessionAdmin : '',
1257
                'initial_url' => isset($sessionUrl) === true ? $sessionUrl : '',
1258
                'pwd_attempts' => (int) $sessionPwdAttempts,
1259
                'error' => 'no_user_yubico_credentials',
1260
                'message' => '',
1261
                'proceedIdentification' => false,
1262
            ];
1263
        }
1264
        $yubico_user_key = $userInfo['yubico_user_key'];
1265
        $yubico_user_id = $userInfo['yubico_user_id'];
1266
    }
1267
1268
    // Now check yubico validity
1269
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Authentication/Yubico/Yubico.php';
1270
    $yubi = new Auth_Yubico($yubico_user_id, $yubico_user_key);
1271
    $auth = $yubi->verify($yubico_key);
1272
    //, null, null, null, 60
1273
1274
    if (PEAR::isError($auth)) {
1275
        return [
1276
            'error' => true,
1277
            'value' => '',
1278
            'user_admin' => isset($sessionAdmin) ? (int) $sessionAdmin : '',
1279
            'initial_url' => isset($sessionUrl) === true ? $sessionUrl : '',
1280
            'pwd_attempts' => (int) $sessionPwdAttempts,
1281
            'error' => 'bad_user_yubico_credentials',
1282
            'message' => langHdl('yubico_bad_code'),
1283
            'proceedIdentification' => false,
1284
        ];
1285
    }
1286
1287
    return [
1288
        'error' => false,
1289
        'message' => '',
1290
        'proceedIdentification' => true,
1291
    ];
1292
}
1293
1294
/**
1295
 * Undocumented function.
1296
 *
1297
 * @param string $username      User name
1298
 * @param string $passwordClear User password in clear
1299
 * @param string $retLDAP       Received data from LDAP
1300
 * @param array  $SETTINGS      Teampass settings
1301
 *
1302
 * @return array
1303
 */
1304
function ldapCreateUser(string $username, string $passwordClear, string $retLDAP, array $SETTINGS): array
1305
{
1306
    // Generate user keys pair
1307
    $userKeys = generateUserKeys($passwordClear);
1308
    // Insert user in DB
1309
    DB::insert(
1310
        prefixTable('users'),
1311
        [
1312
            'login' => $username,
1313
            'pw' => $retLDAP['hashedPassword'],
1314
            'email' => isset($retLDAP['user_info_from_ad'][0]['mail'][0]) === false ? '' : $retLDAP['user_info_from_ad'][0]['mail'][0],
1315
            'name' => $retLDAP['user_info_from_ad'][0]['givenname'][0],
1316
            'lastname' => $retLDAP['user_info_from_ad'][0]['sn'][0],
1317
            'admin' => '0',
1318
            'gestionnaire' => '0',
1319
            'can_manage_all_users' => '0',
1320
            'personal_folder' => $SETTINGS['enable_pf_feature'] === '1' ? '1' : '0',
1321
            'fonction_id' => (empty($retLDAP['user_info_from_ad'][0]['commonGroupsLdapVsTeampass']) === false ? $retLDAP['user_info_from_ad'][0]['commonGroupsLdapVsTeampass'] . ';' : '') . (isset($SETTINGS['ldap_new_user_role']) === true ? $SETTINGS['ldap_new_user_role'] : '0'),
1322
            'groupes_interdits' => '',
1323
            'groupes_visibles' => '',
1324
            'last_pw_change' => (int) time(),
1325
            'user_language' => $SETTINGS['default_language'],
1326
            'encrypted_psk' => '',
1327
            'isAdministratedByRole' => isset($SETTINGS['ldap_new_user_is_administrated_by']) === true && empty($SETTINGS['ldap_new_user_is_administrated_by']) === false ? $SETTINGS['ldap_new_user_is_administrated_by'] : 0,
1328
            'public_key' => $userKeys['public_key'],
1329
            'private_key' => $userKeys['private_key'],
1330
        ]
1331
    );
1332
    $newUserId = DB::insertId();
1333
    // Create personnal folder
1334
    if (isKeyExistingAndEqual('enable_pf_feature', 1, $SETTINGS) === true) {
1335
        DB::insert(
1336
            prefixTable('nested_tree'),
1337
            [
1338
                'parent_id' => '0',
1339
                'title' => $newUserId,
1340
                'bloquer_creation' => '0',
1341
                'bloquer_modification' => '0',
1342
                'personal_folder' => '1',
1343
            ]
1344
        );
1345
        // Rebuild tree
1346
        $tree = new SplClassLoader('Tree\NestedTree', $SETTINGS['cpassman_dir'] . '/includes/libraries');
1347
        $tree->register();
1348
        $tree = new Tree\NestedTree\NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
1349
        $tree->rebuild();
1350
    }
1351
1352
    return [
1353
        'error' => false,
1354
        'message' => '',
1355
        'proceedIdentification' => true,
1356
        'user_initial_creation_through_ldap' => true,
1357
    ];
1358
}
1359
1360
/**
1361
 * Undocumented function.
1362
 *
1363
 * @param string                $username     Username
1364
 * @param array                 $userInfo     Result of query
1365
 * @param string|array|resource $dataReceived DataReceived
1366
 * @param array                 $SETTINGS     Teampass settings
1367
 *
1368
 * @return array
1369
 */
1370
function googleMFACheck(string $username, array $userInfo, $dataReceived, array $SETTINGS): array
1371
{
1372
    if (
1373
        isset($dataReceived['GACode']) === true
1374
        && empty($dataReceived['GACode']) === false
1375
    ) {
1376
        // Load superGlobals
1377
        include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
1378
        $superGlobal = new protect\SuperGlobal\SuperGlobal();
1379
        $sessionAdmin = $superGlobal->get('user_admin', 'SESSION');
1380
        $sessionUrl = $superGlobal->get('initial_url', 'SESSION');
1381
        $sessionPwdAttempts = $superGlobal->get('pwd_attempts', 'SESSION');
1382
        // load library
1383
        include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Authentication/TwoFactorAuth/TwoFactorAuth.php';
1384
        // create new instance
1385
        $tfa = new Authentication\TwoFactorAuth\TwoFactorAuth($SETTINGS['ga_website_name']);
1386
        // Init
1387
        $firstTime = [];
1388
        // now check if it is the 1st time the user is using 2FA
1389
        if ($userInfo['ga_temporary_code'] !== 'none' && $userInfo['ga_temporary_code'] !== 'done') {
1390
            if ($userInfo['ga_temporary_code'] !== $dataReceived['GACode']) {
1391
                return [
1392
                    'error' => true,
1393
                    'message' => langHdl('ga_bad_code'),
1394
                    'proceedIdentification' => false,
1395
                    'mfaStatus' => '',
1396
                ];
1397
            }
1398
1399
            // If first time with MFA code
1400
            $proceedIdentification = false;
1401
            $mfaStatus = 'ga_temporary_code_correct';
1402
            $mfaMessage = langHdl('ga_flash_qr_and_login');
1403
            // generate new QR
1404
            $new_2fa_qr = $tfa->getQRCodeImageAsDataUri(
1405
                'Teampass - ' . $username,
1406
                $userInfo['ga']
1407
            );
1408
            // clear temporary code from DB
1409
            DB::update(
1410
                prefixTable('users'),
1411
                [
1412
                    'ga_temporary_code' => 'done',
1413
                ],
1414
                'id=%i',
1415
                $userInfo['id']
1416
            );
1417
            $firstTime = [
1418
                'value' => '<img src="' . $new_2fa_qr . '">',
1419
                'user_admin' => isset($sessionAdmin) ? (int) $sessionAdmin : '',
1420
                'initial_url' => isset($sessionUrl) === true ? $sessionUrl : '',
1421
                'pwd_attempts' => (int) $sessionPwdAttempts,
1422
                'message' => $mfaMessage,
1423
                'mfaStatus' => $mfaStatus,
1424
            ];
1425
        } else {
1426
            // verify the user GA code
1427
            if ($tfa->verifyCode($userInfo['ga'], $dataReceived['GACode'])) {
1428
                $proceedIdentification = true;
1429
            } else {
1430
                return [
1431
                    'error' => true,
1432
                    'message' => langHdl('ga_bad_code'),
1433
                    'proceedIdentification' => false,
1434
                ];
1435
            }
1436
        }
1437
    } else {
1438
        return [
1439
            'error' => true,
1440
            'message' => langHdl('ga_bad_code'),
1441
            'proceedIdentification' => false,
1442
        ];
1443
    }
1444
1445
    return [
1446
        'error' => false,
1447
        'message' => '',
1448
        'proceedIdentification' => $proceedIdentification,
1449
        'firstTime' => $firstTime,
1450
    ];
1451
}
1452
1453
/**
1454
 * Undocumented function.
1455
 *
1456
 * @param string                $passwordClear Password in clear
1457
 * @param array|string          $userInfo      Array of user data
1458
 * @param array|string|resource $dataReceived  Received data
1459
 * @param string                $username      User name
1460
 * @param array                 $SETTINGS      Teampass settings
1461
 *
1462
 * @return bool
1463
 */
1464
function checkCredentials($passwordClear, $userInfo, $dataReceived, $username, $SETTINGS)
1465
{
1466
    // Set to false
1467
    $userPasswordVerified = false;
1468
    // load passwordLib library
1469
    include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php';
1470
    $pwdlib = new SplClassLoader('PasswordLib', $SETTINGS['cpassman_dir'] . '/includes/libraries');
1471
    $pwdlib->register();
1472
    $pwdlib = new PasswordLib\PasswordLib();
1473
    // Check if old encryption used
1474
    if (
1475
        crypt($passwordClear, $userInfo['pw']) === $userInfo['pw']
1476
        && empty($userInfo['pw']) === false
1477
    ) {
1478
        $userPasswordVerified = true;
1479
        //update user's password
1480
        $userInfo['pw'] = $pwdlib->createPasswordHash($passwordClear);
1481
        DB::update(
1482
            prefixTable('users'),
1483
            [
1484
                'pw' => $userInfo['pw'],
1485
            ],
1486
            'id=%i',
1487
            $userInfo['id']
1488
        );
1489
    }
1490
    //echo $passwordClear." - ".$userInfo['pw']." - ".$pwdlib->verifyPasswordHash($passwordClear, $userInfo['pw'])." ;; ";
1491
    // check the given password
1492
    if ($userPasswordVerified !== true) {
1493
        if ($pwdlib->verifyPasswordHash($passwordClear, $userInfo['pw']) === true) {
1494
            $userPasswordVerified = true;
1495
        } else {
1496
            // 2.1.27.24 - manage passwords
1497
            if ($pwdlib->verifyPasswordHash(htmlspecialchars_decode($dataReceived['pw']), $userInfo['pw']) === true) {
1498
                // then the auth is correct but needs to be adapted in DB since change of encoding
1499
                $userInfo['pw'] = $pwdlib->createPasswordHash($passwordClear);
1500
                DB::update(
1501
                    prefixTable('users'),
1502
                    [
1503
                        'pw' => $userInfo['pw'],
1504
                    ],
1505
                    'id=%i',
1506
                    $userInfo['id']
1507
                );
1508
                $userPasswordVerified = true;
1509
            } else {
1510
                $userPasswordVerified = false;
1511
                logEvents(
1512
                    $SETTINGS,
1513
                    'failed_auth',
1514
                    'password_is_not_correct',
1515
                    '',
1516
                    '',
1517
                    stripslashes($username)
1518
                );
1519
            }
1520
        }
1521
    }
1522
1523
    return $userPasswordVerified;
1524
}
1525
1526
/**
1527
 * Undocumented function.
1528
 *
1529
 * @param bool   $enabled text1
1530
 * @param string $dbgFile text2
1531
 * @param string $text    text3
1532
 */
1533
function debugIdentify(bool $enabled, string $dbgFile, string $text): void
1534
{
1535
    if ($enabled === true) {
1536
        $fp = fopen($dbgFile, 'a');
1537
        if ($fp !== false) {
1538
            fwrite(
1539
                $fp,
1540
                $text
1541
            );
1542
        }
1543
    }
1544
}
1545
1546
1547
1548
function identifyGetUserCredentials(
1549
    array $SETTINGS,
1550
    string $serverPHPAuthUser,
1551
    string $serverPHPAuthPw,
1552
    string $userPassword,
1553
    string $userLogin
1554
): array
1555
{
1556
    if ((int) $SETTINGS['enable_http_request_login'] === 1
1557
        && $serverPHPAuthUser !== null
1558
        && (int) $SETTINGS['maintenance_mode'] === 1
1559
    ) {
1560
        if (strpos($serverPHPAuthUser, '@') !== false) {
1561
            return [
1562
                'username' => explode('@', $serverPHPAuthUser)[0],
1563
                'passwordClear' => $serverPHPAuthPw
1564
            ];
1565
        }
1566
        
1567
        if (strpos($serverPHPAuthUser, '\\') !== false) {
1568
            return [
1569
                'username' => explode('\\', $serverPHPAuthUser)[1],
1570
                'passwordClear' => $serverPHPAuthPw
1571
            ];
1572
        }
1573
1574
        return [
1575
            'username' => $serverPHPAuthPw,
1576
            'passwordClear' => $serverPHPAuthPw
1577
        ];
1578
    }
1579
    
1580
    return [
1581
        'username' => $userLogin,
1582
        'passwordClear' => $userPassword
1583
    ];
1584
}
1585
1586
1587
    
1588
1589
function identifyDoInitialChecks(
1590
    $SETTINGS,
1591
    int $sessionPwdAttempts,
1592
    string $username,
1593
    int $sessionAdmin,
1594
    string $sessionUrl,
1595
    string $user_2fa_selection
1596
): array
1597
{
1598
    // Brute force management
1599
    if ($sessionPwdAttempts > 3) {
1600
        // Load superGlobals
1601
        include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
1602
        $superGlobal = new protect\SuperGlobal\SuperGlobal();
1603
        $superGlobal->put('next_possible_pwd_attempts', time() + 10, 'SESSION');
1604
        $superGlobal->put('pwd_attempts', 0, 'SESSION');
1605
1606
        logEvents($SETTINGS, 'failed_auth', 'user_not_exists', '', stripslashes($username), stripslashes($username));
1607
1608
        return [
1609
            'error' => true,
1610
            'array' => [
1611
                'value' => 'bruteforce_wait',
1612
                'user_admin' => isset($sessionAdmin) ? (int) $sessionAdmin : 0,
1613
                'initial_url' => isset($sessionUrl) === true ? $sessionUrl : '',
1614
                'pwd_attempts' => 0,
1615
                'error' => true,
1616
                'message' => langHdl('error_bad_credentials_more_than_3_times'),
1617
            ]
1618
        ];
1619
    }
1620
1621
    // Check 2FA
1622
    if ((empty($user_2fa_selection) === true &&
1623
            isOneVarOfArrayEqualToValue(
1624
                [
1625
                    (int) $SETTINGS['yubico_authentication'],
1626
                    (int) $SETTINGS['google_authentication'],
1627
                    (int) $SETTINGS['duo']
1628
                ],
1629
                1
1630
            ) === true)
1631
        && ($username !== 'admin' || ((int) $SETTINGS['admin_2fa_required'] === 1 && $username === 'admin'))
1632
    ) {
1633
        return [
1634
            'error' => true,
1635
            'array' => [
1636
                'value' => '2fa_not_set',
1637
                'user_admin' => isset($sessionAdmin) ? (int) $sessionAdmin : 0,
1638
                'initial_url' => isset($sessionUrl) === true ? $sessionUrl : '',
1639
                'pwd_attempts' => (int) $sessionPwdAttempts,
1640
                'error' => '2fa_not_set',
1641
                'message' => langHdl('2fa_credential_not_correct'),
1642
            ]
1643
        ];
1644
    }
1645
1646
    // Check if user exists
1647
    $userInfo = DB::queryFirstRow(
1648
        'SELECT *
1649
        FROM ' . prefixTable('users') . ' WHERE login=%s',
1650
        $username
1651
    );
1652
    
1653
    // User doesn't exist then stop
1654
    if (DB::count() === 0) {
1655
        logEvents($SETTINGS, 'failed_auth', 'user_not_exists', '', stripslashes($username), stripslashes($username));
1656
1657
        return [
1658
            'error' => true,
1659
            'array' => [
1660
                'error' => 'user_not_exists',
1661
                'message' => langHdl('error_bad_credentials'),
1662
                'pwd_attempts' => (int) $sessionPwdAttempts,
1663
                'user_admin' => isset($sessionAdmin) ? (int) $sessionAdmin : 0,
1664
                'initial_url' => isset($sessionUrl) === true ? $sessionUrl : '',
1665
            ]
1666
        ];
1667
    }
1668
1669
    // has user to be auth with mfa?
1670
    $userInfo['fonction_id'] = is_null($userInfo['fonction_id']) === true ? false : $userInfo['fonction_id'];
1671
    
1672
    // user should use MFA?
1673
    $userInfo['mfa_auth_requested'] = mfa_auth_requested(
1674
        (string) $userInfo['fonction_id'],
1675
        is_null($SETTINGS['mfa_for_roles']) === true ? '' : (string) $SETTINGS['mfa_for_roles']
1676
    );
1677
1678
    // Manage Maintenance mode
1679
    if ((int) $SETTINGS['maintenance_mode'] === 1 && (int) $userInfo['admin'] === 0) {
1680
        return [
1681
            'error' => true,
1682
            'array' => [
1683
                'value' => '',
1684
                'user_admin' => (int) $userInfo['admin'],
1685
                'initial_url' => '',
1686
                'pwd_attempts' => '',
1687
                'error' => 'maintenance_mode_enabled',
1688
                'message' => '',
1689
            ]
1690
        ];
1691
    }
1692
1693
    // If admin user then check if folder install exists
1694
    // if yes then refuse connection
1695
    if ((int) $userInfo['admin'] === 1 && is_dir('../install') === true) {
1696
        return [
1697
            'error' => true,
1698
            'array' => [
1699
                'value' => '',
1700
                'user_admin' => isset($sessionAdmin) ? (int) $sessionAdmin : 0,
1701
                'initial_url' => isset($sessionUrl) === true ? $sessionUrl : '',
1702
                'pwd_attempts' => (int) $sessionPwdAttempts,
1703
                'error' => true,
1704
                'message' => langHdl('remove_install_folder'),
1705
            ]
1706
        ];
1707
    }
1708
1709
    // Return some usefull information about user
1710
    return [
1711
        'error' => false,
1712
        'user_mfa_mode' => $user_2fa_selection,
1713
        'userInfo' => $userInfo,
1714
    ];
1715
}
1716
1717
function identifyDoLDAPChecks(
1718
    $SETTINGS,
1719
    $userInfo,
1720
    string $username,
1721
    string $passwordClear,
1722
    int $sessionAdmin,
1723
    string $sessionUrl,
1724
    int $sessionPwdAttempts
1725
): array
1726
{
1727
    // Prepare LDAP connection if set up
1728
    if ((int) $SETTINGS['ldap_mode'] === 1
1729
        && $username !== 'admin'
1730
        && (string) $userInfo['auth_type'] === 'ldap'
1731
    ) {
1732
        $retLDAP = authenticateThroughAD(
1733
            $username,
1734
            $userInfo,
1735
            $passwordClear,
1736
            $SETTINGS
1737
        );
1738
        if ($retLDAP['error'] === true) {
1739
            return [
1740
                'error' => true,
1741
                'array' => [
1742
                    'value' => '',
1743
                    'user_admin' => isset($sessionAdmin) ? (int) $sessionAdmin : 0,
1744
                    'initial_url' => isset($sessionUrl) === true ? $sessionUrl : '',
1745
                    'pwd_attempts' => (int) $sessionPwdAttempts,
1746
                    'error' => true,
1747
                    'message' => "LDAP error: ".$retLDAP['message'],
1748
                ]
1749
            ];
1750
        }
1751
        return [
1752
            'error' => false,
1753
            'retLDAP' => $retLDAP,
1754
            'ldapConnection' => true,
1755
            'userPasswordVerified' => true,
1756
        ];
1757
    }
1758
1759
    // return if no addmin
1760
    return [
1761
        'error' => false,
1762
        'retLDAP' => [],
1763
        'ldapConnection' => false,
1764
        'userPasswordVerified' => false,
1765
    ];
1766
}
1767
1768
1769
function identifyDoMFAChecks(
1770
    $SETTINGS,
1771
    $userInfo,
1772
    $dataReceived,
1773
    $userInitialData,
1774
    string $username
1775
): array
1776
{    
1777
    if (
1778
        // ---------
1779
        // check GA code
1780
        // ---------
1781
        (int) $SETTINGS['google_authentication'] === 1
1782
        && ($username !== 'admin' || ((int) $SETTINGS['admin_2fa_required'] === 1 && $username === 'admin'))
1783
        && $userInitialData['user_mfa_mode'] === 'google'
1784
        && $userInfo['mfa_auth_requested'] === true
1785
    ) {
1786
        $ret = googleMFACheck(
1787
            $username,
1788
            $userInfo,
1789
            $dataReceived,
1790
            $SETTINGS
1791
        );
1792
        if ($ret['error'] !== false) {
1793
            logEvents($SETTINGS, 'failed_auth', 'wrong_mfa_code', '', stripslashes($username), stripslashes($username));
1794
            
1795
            return [
1796
                'error' => true,
1797
                'mfaData' => $ret,
1798
            ];
1799
            // ---
1800
        }
1801
        return [
1802
            'error' => false,
1803
            'mfaData' => $ret,
1804
        ];
1805
1806
        // ---
1807
        // ---
1808
    } else if (
1809
        // ---------
1810
        // Check Yubico
1811
        // ---------
1812
        isKeyExistingAndEqual('yubico_authentication', 1, $SETTINGS) === true
1813
        && ((int) $userInfo['admin'] !== 1 || ((int) $SETTINGS['admin_2fa_required'] === 1 && (int) $userInfo['admin'] === 1))
1814
        && $userInitialData['user_mfa_mode'] === 'yubico'
1815
        && $userInfo['mfa_auth_requested'] === true
1816
    ) {
1817
        $ret = yubicoMFACheck(
1818
            $dataReceived,
1819
            $userInfo,
1820
            $SETTINGS
1821
        );
1822
        if ($ret['error'] !== '') {
1823
            return [
1824
                'error' => true,
1825
                'mfaData' => $ret,
1826
            ];
1827
        }
1828
        
1829
        // ---
1830
        // ---
1831
    }
1832
1833
    return [
1834
        'error' => false,
1835
        '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...
1836
    ];
1837
}
1838