Passed
Push — teampass_3.0 ( 2e6bd1...4c0075 )
by Nils
13:44 queued 05:04
created

checkUserPasswordValidity()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 34
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

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