Passed
Push — master ( 892856...f66902 )
by Nils
04:49
created

authenticateThroughAD()   D

Complexity

Conditions 17
Paths 72

Size

Total Lines 166
Code Lines 110

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 17
eloc 110
c 2
b 0
f 0
nc 72
nop 4
dl 0
loc 166
rs 4.1733

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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