Passed
Push — teampass_3.0 ( c59e58...722670 )
by Nils
04:12
created

migrateTo3_DoUserPersonalItemsEncryption()   C

Complexity

Conditions 11
Paths 12

Size

Total Lines 178
Code Lines 100

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 100
c 0
b 0
f 0
nc 12
nop 5
dl 0
loc 178
rs 5.8533

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
 * @project   Teampass
13
 * @file      main.queries.php
14
 * ---
15
 * @author    Nils Laumaillé ([email protected])
16
 * @copyright 2009-2021 Teampass.net
17
 * @license   https://spdx.org/licenses/GPL-3.0-only.html#licenseText GPL-3.0
18
 * ---
19
 * @see       https://www.teampass.net
20
 */
21
22
if (isset($_SESSION) === false) {
23
    include_once 'SecureHandler.php';
24
    session_name('teampass_session');
25
    session_start();
26
    $_SESSION['CPM'] = 1;
27
}
28
29
if (isset($_SESSION['CPM']) === false || $_SESSION['CPM'] !== 1) {
30
    $_SESSION['error']['code'] = '1004';
31
    include '../error.php';
32
    exit();
33
}
34
35
// Load config
36
if (file_exists('../includes/config/tp.config.php')) {
37
    include '../includes/config/tp.config.php';
38
} elseif (file_exists('./includes/config/tp.config.php')) {
39
    include './includes/config/tp.config.php';
40
} else {
41
    throw new Exception("Error file '/includes/config/tp.config.php' not exists", 1);
42
}
43
44
// DO CHECKS
45
require_once $SETTINGS['cpassman_dir'] . '/includes/config/include.php';
46
require_once $SETTINGS['cpassman_dir'] . '/sources/checks.php';
47
$post_type = filter_input(INPUT_POST, 'type', FILTER_SANITIZE_STRING);
48
if (
49
    isset($post_type) === true
50
    && ($post_type === 'ga_generate_qr'
51
        //|| $post_type === 'recovery_send_pw_by_email'
52
        //|| $post_type === 'recovery_generate_new_password'
53
        || $post_type === 'get_teampass_settings')
54
) {
55
    // continue
56
    mainQuery($SETTINGS);
57
} elseif (
58
    isset($_SESSION['user_id']) === true
59
    && checkUser($_SESSION['user_id'], $_SESSION['key'], 'home', $SETTINGS) === false
60
) {
61
    $_SESSION['error']['code'] = ERR_NOT_ALLOWED; //not allowed page
62
    include $SETTINGS['cpassman_dir'] . '/error.php';
63
    exit();
64
} elseif ((isset($_SESSION['user_id']) === true
65
        && isset($_SESSION['key'])) === true
66
    || (isset($post_type) === true
67
        //&& $post_type === 'change_user_language'
68
        && null !== filter_input(INPUT_POST, 'data', FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES))
69
) {
70
    // continue
71
    mainQuery($SETTINGS);
72
} else {
73
    $_SESSION['error']['code'] = ERR_NOT_ALLOWED; //not allowed page
74
    include $SETTINGS['cpassman_dir'] . '/error.php';
75
    exit();
76
}
77
78
/**
79
 * Undocumented function.
80
 */
81
function mainQuery($SETTINGS)
82
{
83
    header('Content-type: text/html; charset=utf-8');
84
    header('Cache-Control: no-cache, must-revalidate');
85
    error_reporting(E_ERROR);
86
87
    // Load config
88
    if (file_exists('../includes/config/tp.config.php')) {
89
        include '../includes/config/tp.config.php';
90
    } elseif (file_exists('./includes/config/tp.config.php')) {
91
        include './includes/config/tp.config.php';
92
    } else {
93
        throw new Exception("Error file '/includes/config/tp.config.php' not exists", 1);
94
    }
95
96
    /*
97
    * Define Timezone
98
    **/
99
    if (isset($SETTINGS['timezone']) === true) {
100
        date_default_timezone_set($SETTINGS['timezone']);
101
    } else {
102
        date_default_timezone_set('UTC');
103
    }
104
105
    include_once $SETTINGS['cpassman_dir'] . '/includes/language/' . $_SESSION['user_language'] . '.php';
106
    include_once $SETTINGS['cpassman_dir'] . '/includes/config/settings.php';
107
108
    // Includes
109
    include_once $SETTINGS['cpassman_dir'] . '/sources/main.functions.php';
110
    include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php';
111
112
    // Connect to mysql server
113
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
114
    if (defined('DB_PASSWD_CLEAR') === false) {
115
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
116
    }
117
    DB::$host = DB_HOST;
118
    DB::$user = DB_USER;
119
    DB::$password = DB_PASSWD_CLEAR;
120
    DB::$dbName = DB_NAME;
121
    DB::$port = DB_PORT;
122
    DB::$encoding = DB_ENCODING;
123
124
    // User's language loading
125
    include_once $SETTINGS['cpassman_dir'] . '/includes/language/' . $_SESSION['user_language'] . '.php';
126
127
    // Prepare post variables
128
    $post_key = filter_input(INPUT_POST, 'key', FILTER_SANITIZE_STRING);
129
    $post_type = filter_input(INPUT_POST, 'type', FILTER_SANITIZE_STRING);
130
    $post_data = filter_input(INPUT_POST, 'data', FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES);
131
132
    // Ensure Complexity levels are translated
133
    if (defined('TP_PW_COMPLEXITY') === false) {
134
        define(
135
            'TP_PW_COMPLEXITY',
136
            array(
137
                0 => array(0, langHdl('complex_level0'), 'fas fa-bolt text-danger'),
138
                25 => array(25, langHdl('complex_level1'), 'fas fa-thermometer-empty text-danger'),
139
                50 => array(50, langHdl('complex_level2'), 'fas fa-thermometer-quarter text-warning'),
140
                60 => array(60, langHdl('complex_level3'), 'fas fa-thermometer-half text-warning'),
141
                70 => array(70, langHdl('complex_level4'), 'fas fa-thermometer-three-quarters text-success'),
142
                80 => array(80, langHdl('complex_level5'), 'fas fa-thermometer-full text-success'),
143
                90 => array(90, langHdl('complex_level6'), 'far fa-gem text-success'),
144
            )
145
        );
146
    }
147
148
    // Check KEY
149
    if (isset($post_key) === false || empty($post_key) === true) {
150
        echo prepareExchangedData(
151
            array(
152
                'error' => true,
153
                'message' => langHdl('key_is_not_correct'),
154
            ),
155
            'encode'
156
        );
157
        return false;
158
    }
159
160
    // decrypt and retreive data in JSON format
161
    $dataReceived = prepareExchangedData(
162
        $post_data,
163
        'decode'
164
    );
165
166
    // Manage type of action asked
167
    switch ($post_type) {
168
        case 'change_pw':
169
            $return = changePassword(
170
                (string) filter_var($dataReceived['new_pw'], FILTER_SANITIZE_STRING),
171
                isset($dataReceived['current_pw']) === true ? (string) filter_var($dataReceived['current_pw'], FILTER_SANITIZE_STRING) : '',
172
                (int) filter_var($dataReceived['complexity'], FILTER_SANITIZE_NUMBER_INT),
173
                (string) filter_var($dataReceived['change_request'], FILTER_SANITIZE_STRING),
174
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
175
                $SETTINGS
176
            );
177
178
            echo $return;
179
            
180
            break;
181
182
        /*
183
         * This will generate the QR Google Authenticator
184
         */
185
        case 'ga_generate_qr':
186
            $return = generateQRCode(
187
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
188
                (string) filter_var($dataReceived['demand_origin'], FILTER_SANITIZE_STRING),
189
                (string) filter_var($dataReceived['send_email'], FILTER_SANITIZE_STRING),
190
                (string) filter_var($dataReceived['login'], FILTER_SANITIZE_STRING),
191
                (string) filter_var($dataReceived['pwd'], FILTER_SANITIZE_STRING),
192
                $SETTINGS
193
            );
194
195
            echo $return;
196
197
            break;
198
        
199
            /*
200
         * Increase the session time of User
201
         */
202
        case 'increase_session_time':
203
            // check if session is not already expired.
204
            if ($_SESSION['sessionDuration'] > time()) {
205
                // Calculate end of session
206
                $_SESSION['sessionDuration'] = (int) ($_SESSION['sessionDuration'] + filter_input(INPUT_POST, 'duration', FILTER_SANITIZE_NUMBER_INT));
207
                // Update table
208
                DB::update(
209
                    prefixTable('users'),
210
                    array(
211
                        'session_end' => $_SESSION['sessionDuration'],
212
                    ),
213
                    'id = %i',
214
                    $_SESSION['user_id']
215
                );
216
                // Return data
217
                echo '[{"new_value":"' . $_SESSION['sessionDuration'] . '"}]';
218
219
                break;
220
            }
221
            
222
            echo '[{"new_value":"expired"}]';
223
224
            break;
225
        
226
            /*
227
         * Hide maintenance message
228
         */
229
        case 'hide_maintenance':
230
            $_SESSION['hide_maintenance'] = 1;
231
232
            break;
233
        
234
        /*
235
         * Send emails not sent
236
         */
237
        case 'send_waiting_emails':
238
            sendEmailsNotSent(
239
                $SETTINGS
240
            );
241
            
242
            break;
243
244
        /*
245
         * Generate a password generic
246
         */
247
        case 'generate_password':
248
            $return = generateGenericPassword(
249
                (int) filter_input(INPUT_POST, 'size', FILTER_SANITIZE_NUMBER_INT),
250
                (bool) filter_input(INPUT_POST, 'secure_pwd', FILTER_SANITIZE_STRING),
251
                (bool) filter_input(INPUT_POST, 'lowercase', FILTER_SANITIZE_STRING),
252
                (bool) filter_input(INPUT_POST, 'capitalize', FILTER_SANITIZE_STRING),
253
                (bool) filter_input(INPUT_POST, 'numerals', FILTER_SANITIZE_STRING),
254
                (bool) filter_input(INPUT_POST, 'symbols', FILTER_SANITIZE_NUMBER_INT),
255
                $SETTINGS
256
            );
257
258
            echo $return;
259
            
260
            break;
261
262
        /*
263
         * Refresh list of last items seen
264
         */
265
        case 'refresh_list_items_seen':
266
            $return = refreshUserItemsSeenList(
267
                $SETTINGS
268
            );
269
270
            echo $return;
271
272
            break;
273
274
        /*
275
         * Generates a KEY with CRYPT
276
         */
277
        case 'generate_new_key':
278
            // load passwordLib library
279
            $pwdlib = new SplClassLoader('PasswordLib', '../includes/libraries');
280
            $pwdlib->register();
281
            $pwdlib = new PasswordLib\PasswordLib();
282
            // generate key
283
            $key = $pwdlib->getRandomToken(filter_input(INPUT_POST, 'size', FILTER_SANITIZE_NUMBER_INT));
284
            echo '[{"key" : "' . htmlentities($key, ENT_QUOTES) . '"}]';
285
            break;
286
287
        /*
288
         * Generates a TOKEN with CRYPT
289
         */
290
        case 'save_token':
291
            $token = GenerateCryptKey(
292
                null !== filter_input(INPUT_POST, 'size', FILTER_SANITIZE_NUMBER_INT) ? (int) filter_input(INPUT_POST, 'size', FILTER_SANITIZE_NUMBER_INT) : 20,
293
                null !== filter_input(INPUT_POST, 'secure', FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) ? filter_input(INPUT_POST, 'secure', FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) : false,
294
                null !== filter_input(INPUT_POST, 'numeric', FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) ? filter_input(INPUT_POST, 'numeric', FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) : false,
295
                null !== filter_input(INPUT_POST, 'capital', FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) ? filter_input(INPUT_POST, 'capital', FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) : false,
296
                null !== filter_input(INPUT_POST, 'symbols', FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) ? filter_input(INPUT_POST, 'symbols', FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) : false,
297
                null !== filter_input(INPUT_POST, 'lowercase', FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) ? filter_input(INPUT_POST, 'lowercase', FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) : false,
298
                $SETTINGS
299
            );
300
301
            // store in DB
302
            DB::insert(
303
                prefixTable('tokens'),
304
                array(
305
                    'user_id' => (int) $_SESSION['user_id'],
306
                    'token' => $token,
307
                    'reason' => filter_input(INPUT_POST, 'reason', FILTER_SANITIZE_STRING),
308
                    'creation_timestamp' => time(),
309
                    'end_timestamp' => time() + filter_input(INPUT_POST, 'duration', FILTER_SANITIZE_NUMBER_INT), // in secs
310
                )
311
            );
312
313
            echo '[{"token" : "' . $token . '"}]';
314
            break;
315
316
317
        /*
318
         * TODO Check if suggestions are existing
319
         */
320
        /*
321
        case 'is_existings_suggestions':
322
            if ($_SESSION['user_manager'] === '1' || $_SESSION['is_admin'] === '1') {
323
                $count = 0;
324
                DB::query('SELECT * FROM ' . prefixTable('items_change'));
325
                $count += DB::count();
326
                DB::query('SELECT * FROM ' . prefixTable('suggestion'));
327
                $count += DB::count();
328
329
                echo '[ { "error" : "" , "count" : "' . $count . '" , "show_sug_in_menu" : "0"} ]';
330
                break;
331
            }
332
            
333
            if (isset($_SESSION['nb_item_change_proposals']) && $_SESSION['nb_item_change_proposals'] > 0) {
334
                echo '[ { "error" : "" , "count" : "' . $_SESSION['nb_item_change_proposals'] . '" , "show_sug_in_menu" : "1"} ]';
335
                break;
336
            }
337
            
338
            echo '[ { "error" : "" , "count" : "" , "show_sug_in_menu" : "0"} ]';
339
340
            break;
341
        */
342
343
        /*
344
         * Sending statistics
345
         */
346
        case 'sending_statistics':
347
            sendingStatistics(
348
                $SETTINGS
349
            );
350
351
            break;
352
353
        /*
354
         * delete a file
355
         */
356
        case 'file_deletion':
357
            fileDelete(filter_input(INPUT_POST, 'filename', FILTER_SANITIZE_STRING), $SETTINGS);
358
359
            break;
360
361
        /*
362
         * Generate BUG report
363
         */
364
        case 'generate_bug_report':
365
            $return = generateBugReport(
366
                (string) filter_var(INPUT_POST, 'data', FILTER_SANITIZE_URL),
0 ignored issues
show
Bug introduced by
'data' of type string is incompatible with the type integer expected by parameter $filter of filter_var(). ( Ignorable by Annotation )

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

366
                (string) filter_var(INPUT_POST, /** @scrutinizer ignore-type */ 'data', FILTER_SANITIZE_URL),
Loading history...
367
                $SETTINGS
368
            );
369
370
            echo $return;
371
        
372
            break;
373
374
        /**
375
         * 
376
         */
377
        case 'update_user_field':
378
            // decrypt and retreive data in JSON format
379
            $dataReceived = prepareExchangedData(
380
                filter_input(INPUT_POST, 'data', FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES),
381
                'decode'
382
            );
383
384
            // Prepare variables
385
            $field = noHTML(htmlspecialchars_decode($dataReceived['field']));
386
            $new_value = noHTML(htmlspecialchars_decode($dataReceived['new_value']));
387
            $user_id = (htmlspecialchars_decode($dataReceived['user_id']));
388
389
            DB::update(
390
                prefixTable('users'),
391
                array(
392
                    $field => $new_value,
393
                ),
394
                'id = %i',
395
                $user_id
396
            );
397
398
            // Update session
399
            if ($field === 'user_api_key') {
400
                $_SESSION['user']['api-key'] = $new_value;
401
            }
402
            break;
403
404
        /*
405
        * get_teampass_settings
406
        */
407
        case 'get_teampass_settings':
408
            if (filter_input(INPUT_POST, 'key', FILTER_SANITIZE_STRING) !== $_SESSION['key']) {
409
                echo prepareExchangedData(
410
                    array(
411
                        'error' => true,
412
                        'message' => langHdl('key_is_not_correct'),
413
                    ),
414
                    'encode'
415
                );
416
                break;
417
            }
418
419
            // Encrypt data to return
420
            echo prepareExchangedData($SETTINGS, 'encode');
421
422
            break;
423
424
425
        /*
426
        * test_current_user_password_is_correct
427
        */
428
        case 'test_current_user_password_is_correct':
429
            $return = isUserPasswordCorrect(
430
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
431
                (string) filter_var($dataReceived['password'], FILTER_SANITIZE_STRING),
432
                $SETTINGS
433
            );
434
435
            echo $return;
436
437
            break;
438
439
        /*
440
         * User's public/private keys change
441
         */
442
        case 'change_private_key_encryption_password':
443
            $return = changePrivateKeyEncryptionPassword(
444
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
445
                (string) filter_var($dataReceived['current_code'], FILTER_SANITIZE_STRING),
446
                (string) filter_var($dataReceived['new_code'], FILTER_SANITIZE_STRING),
447
                (string) filter_var($dataReceived['action_type'], FILTER_SANITIZE_STRING),
448
                $SETTINGS
449
            );
450
451
            echo $return;
452
        
453
            break;
454
455
        /*
456
         * User's password has to be initialized
457
         */
458
        case 'initialize_user_password':
459
            $return = initializeUserPassword(
460
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
461
                (string) filter_var($dataReceived['special'], FILTER_SANITIZE_STRING),
462
                (string) filter_var($dataReceived['password'], FILTER_SANITIZE_STRING),
463
                (string) filter_var($dataReceived['self_change'], FILTER_SANITIZE_STRING),
464
                $SETTINGS
465
            );
466
467
            echo $return;
468
469
            break;
470
471
        /*
472
        * CASE
473
        * Send email
474
        */
475
        case 'mail_me':
476
            $return = sendMailToUser(
477
                filter_var($dataReceived['receipt'], FILTER_SANITIZE_NUMBER_INT),
478
                $dataReceived['body'],
479
                (string) filter_var($dataReceived['subject'], FILTER_SANITIZE_STRING),
480
                (string) filter_var($dataReceived['pre_replace'], FILTER_SANITIZE_STRING),
481
                $SETTINGS
482
            );
483
484
            echo $return;
485
486
            break;
487
488
        /*
489
        * Generate a temporary encryption key for user
490
        */
491
        case 'generate_temporary_encryption_key':
492
            $return = generateOneTimeCode(
493
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
494
                $SETTINGS
495
            );
496
            
497
            echo $return;
498
499
            break;
500
501
        /*
502
        * user_sharekeys_reencryption_start
503
        */
504
        case 'user_sharekeys_reencryption_start':
505
            $return = startReEncryptingUserSharekeys(
506
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
507
                (string) filter_var($dataReceived['self_change'], FILTER_SANITIZE_STRING),
508
                $SETTINGS
509
            );
510
            
511
            echo $return;
512
513
            break;
514
515
        /*
516
        * user_sharekeys_reencryption_next
517
        */
518
        case 'user_sharekeys_reencryption_next':
519
            $return = continueReEncryptingUserSharekeys(
520
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
521
                (string) filter_var($dataReceived['self_change'], FILTER_SANITIZE_STRING),
522
                (string) filter_var($dataReceived['action'], FILTER_SANITIZE_STRING),
523
                (int) filter_var($dataReceived['start'], FILTER_SANITIZE_NUMBER_INT),
524
                (int) filter_var($dataReceived['length'], FILTER_SANITIZE_NUMBER_INT),
525
                $SETTINGS
526
            );
527
            
528
            echo $return;
529
530
            break;
531
532
        /*
533
        * user_psk_reencryption
534
        */
535
        case 'user_psk_reencryption':
536
            $return = migrateTo3_DoUserPersonalItemsEncryption(
537
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
538
                (int) filter_var($dataReceived['start'], FILTER_SANITIZE_NUMBER_INT),
539
                (int) filter_var($dataReceived['length'], FILTER_SANITIZE_NUMBER_INT),
540
                (string) filter_var($dataReceived['userPsk'], FILTER_SANITIZE_STRING),
541
                $SETTINGS
542
            );
543
            
544
            echo $return;
545
        
546
            break;
547
548
        /*
549
        * Get info 
550
        */
551
        case 'get_user_info':
552
            $return = getUserInfo(
553
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
554
                (string) filter_var($dataReceived['fields'], FILTER_SANITIZE_STRING),
555
                $SETTINGS
556
            );
557
            
558
            echo $return;
559
560
            break;
561
562
        /*
563
        * Change user's authenticataion password
564
        */
565
        case 'change_user_auth_password':
566
            $return = changeUserAuthenticationPassword(
567
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
568
                (string) filter_var($dataReceived['old_password'], FILTER_SANITIZE_STRING),
569
                (string) filter_var($dataReceived['new_password'], FILTER_SANITIZE_STRING),
570
                $SETTINGS
571
            );
572
            
573
            echo $return;
574
575
            break;
576
577
578
        /*
579
        * User's authenticataion password in LDAP has changed
580
        */
581
        case 'change_user_ldap_auth_password':
582
            $return = changeUserLDAPAuthenticationPassword(
583
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
584
                filter_var($dataReceived['previous_password'], FILTER_SANITIZE_STRING),
585
                filter_var($dataReceived['current_password'], FILTER_SANITIZE_STRING),
586
                $SETTINGS
0 ignored issues
show
Unused Code introduced by
The call to changeUserLDAPAuthenticationPassword() has too many arguments starting with $SETTINGS. ( Ignorable by Annotation )

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

586
            $return = /** @scrutinizer ignore-call */ changeUserLDAPAuthenticationPassword(

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
587
            );
588
            
589
            echo $return;
590
591
            break;
592
    }
593
}
594
595
596
/**
597
 * 
598
 */
599
function changePassword(
600
    string $post_new_password,
601
    string $post_current_password,
602
    int $post_password_complexity,
603
    string $post_change_request,
604
    int $post_user_id,
605
    array $SETTINGS
606
): string
607
{
608
    // load passwordLib library
609
    $pwdlib = new SplClassLoader('PasswordLib', '../includes/libraries');
610
    $pwdlib->register();
611
    $pwdlib = new PasswordLib\PasswordLib();
612
613
    // Prepare variables
614
    $post_new_password_hashed = $pwdlib->createPasswordHash($post_new_password);
615
616
    // User has decided to change is PW
617
    if (
618
        $post_change_request === 'reset_user_password_expected'
619
        || $post_change_request === 'user_decides_to_change_password'
620
    ) {
621
        // Check that current user is correct
622
        if ((int) $post_user_id !== (int) $_SESSION['user_id']) {
623
            return prepareExchangedData(
624
                array(
625
                    'error' => true,
626
                    'message' => '<div style="margin:10px 0 10px 15px;">' . langHdl('error_not_allowed_to') . '</div>',
627
                ),
628
                'encode'
629
            );
630
        }
631
632
        // check if expected security level is reached
633
        $dataUser = DB::queryfirstrow(
634
            'SELECT *
635
            FROM ' . prefixTable('users') . '
636
            WHERE id = %i',
637
            $post_user_id
638
        );
639
640
        // check if badly written
641
        $dataUser['fonction_id'] = array_filter(
642
            explode(',', str_replace(';', ',', $dataUser['fonction_id']))
643
        );
644
        $dataUser['fonction_id'] = implode(',', $dataUser['fonction_id']);
645
        DB::update(
646
            prefixTable('users'),
647
            array(
648
                'fonction_id' => $dataUser['fonction_id'],
649
            ),
650
            'id = %i',
651
            $post_user_id
652
        );
653
654
        if (empty($dataUser['fonction_id']) === false) {
655
            $data = DB::queryFirstRow(
656
                'SELECT complexity
657
                FROM ' . prefixTable('roles_title') . '
658
                WHERE id IN (' . $dataUser['fonction_id'] . ')
659
                ORDER BY complexity DESC'
660
            );
661
        } else {
662
            // In case user has no roles yet
663
            $data = array();
664
            $data['complexity'] = 0;
665
        }
666
667
        if ((int) $post_password_complexity < (int) $data['complexity']) {
668
            return prepareExchangedData(
669
                array(
670
                    'error' => true,
671
                    'message' => '<div style="margin:10px 0 10px 15px;">' . langHdl('complexity_level_not_reached') . '.<br>' .
672
                        langHdl('expected_complexity_level') . ': <b>' . TP_PW_COMPLEXITY[$data['complexity']][1] . '</b></div>',
673
                ),
674
                'encode'
675
            );
676
        }
677
678
        // Check that the 2 passwords are differents
679
        if ($post_current_password === $post_new_password) {
680
            return prepareExchangedData(
681
                array(
682
                    'error' => true,
683
                    'message' => langHdl('password_already_used'),
684
                ),
685
                'encode'
686
            );
687
        }
688
689
        // update sessions
690
        $_SESSION['last_pw_change'] = mktime(0, 0, 0, (int) date('m'), (int) date('d'), (int) date('y'));
691
        $_SESSION['validite_pw'] = true;
692
693
        // BEfore updating, check that the pwd is correct
694
        if ($pwdlib->verifyPasswordHash($post_new_password, $post_new_password_hashed) === true) {
695
            $special_action = 'none';
696
            if ($post_change_request === 'reset_user_password_expected') {
697
                $_SESSION['user']['private_key'] = decryptPrivateKey($post_current_password, $dataUser['private_key']);
698
            }
699
700
            // update DB
701
            DB::update(
702
                prefixTable('users'),
703
                array(
704
                    'pw' => $post_new_password_hashed,
705
                    'last_pw_change' => mktime(0, 0, 0, (int) date('m'), (int) date('d'), (int) date('y')),
706
                    'last_pw' => $post_current_password,
707
                    'special' => $special_action,
708
                    'private_key' => encryptPrivateKey($post_new_password, $_SESSION['user']['private_key']),
709
                ),
710
                'id = %i',
711
                $post_user_id
712
            );
713
            // update LOG
714
            logEvents($SETTINGS, 'user_mngt', 'at_user_pwd_changed', (string) $_SESSION['user_id'], $_SESSION['login'], $post_user_id);
715
716
            // Send back
717
            return prepareExchangedData(
718
                array(
719
                    'error' => false,
720
                    'message' => '',
721
                ),
722
                'encode'
723
            );
724
        }
725
        // Send back
726
        return prepareExchangedData(
727
            array(
728
                'error' => true,
729
                'message' => langHdl('pwd_hash_not_correct'),
730
            ),
731
            'encode'
732
        );
733
    }
0 ignored issues
show
Bug Best Practice introduced by
The function implicitly returns null when the if condition on line 618 is false. This is incompatible with the type-hinted return string. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
734
}
735
736
737
738
function generateQRCode(
739
    $post_id,
740
    $post_demand_origin,
741
    $post_send_mail,
742
    $post_login,
743
    $post_pwd,
744
    array $SETTINGS
745
): string
746
{
747
    // is this allowed by setting
748
    if ((isset($SETTINGS['ga_reset_by_user']) === false || (int) $SETTINGS['ga_reset_by_user'] !== 1)
749
        && (null === $post_demand_origin || $post_demand_origin !== 'users_management_list')
750
    ) {
751
        // User cannot ask for a new code
752
        return prepareExchangedData(
753
            array(
754
                'error' => true,
755
                'message' => langHdl('error_not_allowed_to'),
756
            ),
757
            'encode'
758
        );
759
    }
760
761
    // Check if user exists
762
    if (null === $post_id || empty($post_id) === true) {
763
        // Get data about user
764
        $data = DB::queryfirstrow(
765
            'SELECT id, email, pw
766
            FROM ' . prefixTable('users') . '
767
            WHERE login = %s',
768
            $post_login
769
        );
770
    } else {
771
        $data = DB::queryfirstrow(
772
            'SELECT id, login, email, pw
773
            FROM ' . prefixTable('users') . '
774
            WHERE id = %i',
775
            $post_id
776
        );
777
        $post_login = $data['login'];
778
    }
779
    // Get number of returned users
780
    $counter = DB::count();
781
782
    // load passwordLib library
783
    $pwdlib = new SplClassLoader('PasswordLib', $SETTINGS['cpassman_dir'] . '/includes/libraries');
784
    $pwdlib->register();
785
    $pwdlib = new PasswordLib\PasswordLib();
786
787
    // Do treatment
788
    if ($counter === 0) {
789
        // Not a registered user !
790
        logEvents($SETTINGS, 'failed_auth', 'user_not_exists', '', stripslashes($post_login), stripslashes($post_login));
791
        return prepareExchangedData(
792
            array(
793
                'error' => true,
794
                'message' => langHdl('no_user'),
795
                'tst' => 1,
796
            ),
797
            'encode'
798
        );
799
    }
800
801
    if (
802
        isset($post_pwd) === true
803
        && isset($data['pw']) === true
804
        && $pwdlib->verifyPasswordHash($post_pwd, $data['pw']) === false
805
        && $post_demand_origin !== 'users_management_list'
806
    ) {
807
        // checked the given password
808
        logEvents($SETTINGS, 'failed_auth', 'user_password_not_correct', '', stripslashes($post_login), stripslashes($post_login));
809
        return prepareExchangedData(
810
            array(
811
                'error' => true,
812
                'message' => langHdl('no_user'),
813
                'tst' => $post_demand_origin,
814
            ),
815
            'encode'
816
        );
817
    }
818
    
819
    if (empty($data['email']) === true) {
820
        return prepareExchangedData(
821
            array(
822
                'error' => true,
823
                'message' => langHdl('no_email'),
824
            ),
825
            'encode'
826
        );
827
    }
828
    
829
    // generate new GA user code
830
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Authentication/TwoFactorAuth/TwoFactorAuth.php';
831
    $tfa = new Authentication\TwoFactorAuth\TwoFactorAuth($SETTINGS['ga_website_name']);
832
    $gaSecretKey = $tfa->createSecret();
833
    $gaTemporaryCode = GenerateCryptKey(12, false, true, true, false, true, $SETTINGS);
834
835
    DB::update(
836
        prefixTable('users'),
837
        [
838
            'ga' => $gaSecretKey,
839
            'ga_temporary_code' => $gaTemporaryCode,
840
        ],
841
        'id = %i',
842
        $data['id']
843
    );
844
845
    // Log event
846
    logEvents($SETTINGS, 'user_connection', 'at_2fa_google_code_send_by_email', (string) $data['id'], stripslashes($post_login), stripslashes($post_login));
847
848
    // send mail?
849
    if ((int) $post_send_mail === 1) {
850
        json_decode(
851
            sendEmail(
852
                langHdl('email_ga_subject'),
853
                str_replace(
854
                    '#2FACode#',
855
                    $gaTemporaryCode,
856
                    langHdl('email_ga_text')
857
                ),
858
                $data['email'],
859
                $SETTINGS
860
            ),
861
            true
862
        );
863
864
        // send back
865
        return prepareExchangedData(
866
            array(
867
                'error' => false,
868
                'message' => $post_send_mail,
869
                'email' => $data['email'],
870
                'email_result' => str_replace(
871
                    '#email#',
872
                    '<b>' . obfuscateEmail($data['email']) . '</b>',
873
                    addslashes(langHdl('admin_email_result_ok'))
874
                ),
875
            ),
876
            'encode'
877
        );
878
    }
879
    
880
    // send back
881
    return prepareExchangedData(
882
        array(
883
            'error' => false,
884
            'message' => '',
885
            'email' => $data['email'],
886
            'email_result' => str_replace(
887
                '#email#',
888
                '<b>' . obfuscateEmail($data['email']) . '</b>',
889
                addslashes(langHdl('admin_email_result_ok'))
890
            ),
891
        ),
892
        'encode'
893
    );
894
}
895
896
function sendEmailsNotSent(
897
    array $SETTINGS
898
)
899
{
900
    if (isset($SETTINGS['enable_send_email_on_user_login'])
901
        && (int) $SETTINGS['enable_send_email_on_user_login'] === 1
902
    ) {
903
        $row = DB::queryFirstRow(
904
            'SELECT valeur FROM ' . prefixTable('misc') . ' WHERE type = %s AND intitule = %s',
905
            'cron',
906
            'sending_emails'
907
        );
908
909
        if ((int) (time() - $row['valeur']) >= 300 || (int) $row['valeur'] === 0) {
910
            $rows = DB::query(
911
                'SELECT *
912
                FROM ' . prefixTable('emails') .
913
                ' WHERE status != %s',
914
                'sent'
915
            );
916
            foreach ($rows as $record) {
917
                echo $record['increment_id'] . " >> ";
918
                // Send email
919
                $ret = json_decode(
920
                    sendEmail(
921
                        $record['subject'],
922
                        $record['body'],
923
                        $record['receivers'],
924
                        $SETTINGS
925
                    ),
926
                    true
927
                );
928
929
                // update item_id in files table
930
                DB::update(
931
                    prefixTable('emails'),
932
                    array(
933
                        'status' => $ret['error'] === 'error_mail_not_send' ? 'not_sent' : 'sent',
934
                    ),
935
                    'timestamp = %s',
936
                    $record['timestamp']
937
                );
938
            }
939
        }
940
        // update cron time
941
        DB::update(
942
            prefixTable('misc'),
943
            array(
944
                'valeur' => time(),
945
            ),
946
            'intitule = %s AND type = %s',
947
            'sending_emails',
948
            'cron'
949
        );
950
    }
951
}
952
953
function generateGenericPassword(
954
    int $size,
955
    bool $secure,
956
    bool $lowercase,
957
    bool $capitalize,
958
    bool $numerals,
959
    bool $symbols,
960
    array $SETTINGS
961
): string
962
{
963
    if ((int) $size > (int) $SETTINGS['pwd_maximum_length']) {
964
        return prepareExchangedData(
965
            array(
966
                'error_msg' => 'Password length is too long! ',
967
                'error' => 'true',
968
            ),
969
            'encode'
970
        );
971
    }
972
973
    $generator = new SplClassLoader('PasswordGenerator\Generator', '../includes/libraries');
974
    $generator->register();
975
    $generator = new PasswordGenerator\Generator\ComputerPasswordGenerator();
976
977
    // Is PHP7 being used?
978
    if (version_compare(PHP_VERSION, '7.0.0', '>=')) {
979
        $php7generator = new SplClassLoader('PasswordGenerator\RandomGenerator', '../includes/libraries');
980
        $php7generator->register();
981
        $generator->setRandomGenerator(new PasswordGenerator\RandomGenerator\Php7RandomGenerator());
982
    }
983
984
    // Manage size
985
    $generator->setLength(($size <= 0) ? 10 : $size);
986
987
    if ($secure === true) {
988
        $generator->setSymbols(true);
989
        $generator->setLowercase(true);
990
        $generator->setUppercase(true);
991
        $generator->setNumbers(true);
992
    } else {
993
        $generator->setLowercase($lowercase);
994
        $generator->setUppercase($capitalize);
995
        $generator->setNumbers($numerals);
996
        $generator->setSymbols($symbols);
997
    }
998
999
    return prepareExchangedData(
1000
        array(
1001
            'key' => $generator->generatePasswords(),
1002
            'error' => '',
1003
        ),
1004
        'encode'
1005
    );
1006
}
1007
1008
function refreshUserItemsSeenList(
1009
    array $SETTINGS
1010
): string
1011
{
1012
    // get list of last items seen
1013
    $arr_html = array();
1014
    $rows = DB::query(
1015
        'SELECT i.id AS id, i.label AS label, i.id_tree AS id_tree, l.date, i.perso AS perso, i.restricted_to AS restricted
1016
        FROM ' . prefixTable('log_items') . ' AS l
1017
        RIGHT JOIN ' . prefixTable('items') . ' AS i ON (l.id_item = i.id)
1018
        WHERE l.action = %s AND l.id_user = %i
1019
        ORDER BY l.date DESC
1020
        LIMIT 0, 100',
1021
        'at_shown',
1022
        $_SESSION['user_id']
1023
    );
1024
    if (DB::count() > 0) {
1025
        foreach ($rows as $record) {
1026
            if (in_array($record['id']->id, array_column($arr_html, 'id')) === false) {
1027
                array_push(
1028
                    $arr_html,
1029
                    array(
1030
                        'id' => $record['id'],
1031
                        'label' => htmlspecialchars(stripslashes(htmlspecialchars_decode($record['label'], ENT_QUOTES)), ENT_QUOTES),
1032
                        'tree_id' => $record['id_tree'],
1033
                        'perso' => $record['perso'],
1034
                        'restricted' => $record['restricted'],
1035
                    )
1036
                );
1037
                if (count($arr_html) >= (int) $SETTINGS['max_latest_items']) {
1038
                    break;
1039
                }
1040
            }
1041
        }
1042
    }
1043
1044
    // get wainting suggestions
1045
    $nb_suggestions_waiting = 0;
1046
    if (
1047
        isset($SETTINGS['enable_suggestion']) === true && (int) $SETTINGS['enable_suggestion'] === 1
1048
        && ((int) $_SESSION['user_admin'] === 1 || (int) $_SESSION['user_manager'] === 1)
1049
    ) {
1050
        DB::query('SELECT * FROM ' . prefixTable('suggestion'));
1051
        $nb_suggestions_waiting = DB::count();
1052
    }
1053
1054
    return json_encode(
1055
        array(
1056
            'error' => '',
1057
            'existing_suggestions' => $nb_suggestions_waiting,
1058
            'html_json' => $arr_html,
1059
        ),
1060
        JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
1061
    );
1062
}
1063
1064
function sendingStatistics(
1065
    array $SETTINGS
1066
): void
1067
{
1068
    if (
1069
        isset($SETTINGS['send_statistics_items']) === true
1070
        && isset($SETTINGS['send_stats']) === true
1071
        && isset($SETTINGS['send_stats_time']) === true
1072
        && (int) $SETTINGS['send_stats'] === 1
1073
        && (int) ($SETTINGS['send_stats_time'] + TP_ONE_DAY_SECONDS) > time()
1074
    ) {
1075
        // get statistics data
1076
        $stats_data = getStatisticsData($SETTINGS);
1077
1078
        // get statistics items to share
1079
        $statsToSend = [];
1080
        $statsToSend['ip'] = $_SERVER['SERVER_ADDR'];
1081
        $statsToSend['timestamp'] = time();
1082
        foreach (array_filter(explode(';', $SETTINGS['send_statistics_items'])) as $data) {
1083
            if ($data === 'stat_languages') {
1084
                $tmp = '';
1085
                foreach ($stats_data[$data] as $key => $value) {
1086
                    $tmp .= $tmp === '' ? $key . '-' . $value : ',' . $key . '-' . $value;
1087
                }
1088
                $statsToSend[$data] = $tmp;
1089
            } elseif ($data === 'stat_country') {
1090
                $tmp = '';
1091
                foreach ($stats_data[$data] as $key => $value) {
1092
                    $tmp .= $tmp === '' ? $key . '-' . $value : ',' . $key . '-' . $value;
1093
                }
1094
                $statsToSend[$data] = $tmp;
1095
            } else {
1096
                $statsToSend[$data] = $stats_data[$data];
1097
            }
1098
        }
1099
1100
        // connect to Teampass Statistics database
1101
        $link2 = new MeekroDB(
1102
            'teampass.pw',
1103
            'teampass_user',
1104
            'ZMlEfRzKzFLZNzie',
1105
            'teampass_followup',
1106
            '3306',
1107
            'utf8'
1108
        );
1109
1110
        $link2->insert(
1111
            'statistics',
1112
            $statsToSend
1113
        );
1114
1115
        // update table misc with current timestamp
1116
        DB::update(
1117
            prefixTable('misc'),
1118
            array(
1119
                'valeur' => time(),
1120
            ),
1121
            'type = %s AND intitule = %s',
1122
            'admin',
1123
            'send_stats_time'
1124
        );
1125
1126
        //permits to test only once by session
1127
        $_SESSION['temporary']['send_stats_done'] = true;
1128
        $SETTINGS['send_stats_time'] = time();
1129
1130
        // save change in config file
1131
        handleConfigFile('update', $SETTINGS, 'send_stats_time', $SETTINGS['send_stats_time']);
1132
    }
1133
}
1134
1135
function generateBugReport(
1136
    string $data,
1137
    array $SETTINGS
1138
): string
1139
{
1140
    $config_exclude_vars = array(
1141
        'bck_script_passkey',
1142
        'email_smtp_server',
1143
        'email_auth_username',
1144
        'email_auth_pwd',
1145
        'email_from',
1146
    );
1147
1148
    // Get data
1149
    $post_data = json_decode($data, true);
1150
1151
    // Read config file
1152
    $list_of_options = '';
1153
    $url_found = '';
1154
    $anonym_url = '';
1155
    $tp_config_file = '../includes/config/tp.config.php';
1156
    $data = file($tp_config_file);
1157
    foreach ($data as $line) {
1158
        if (substr($line, 0, 4) === '    ') {
1159
            // Remove extra spaces
1160
            $line = str_replace('    ', '', $line);
1161
1162
            // Identify url to anonymize it
1163
            if (strpos($line, 'cpassman_url') > 0 && empty($url_found) === true) {
1164
                $url_found = substr($line, 19, strlen($line) - 22);
1165
                $tmp = parse_url($url_found);
1166
                $anonym_url = $tmp['scheme'] . '://<anonym_url>' . $tmp['path'];
1167
                $line = "'cpassman_url' => '" . $anonym_url . "\n";
1168
            }
1169
1170
            // Anonymize all urls
1171
            if (empty($anonym_url) === false) {
1172
                $line = str_replace($url_found, $anonym_url, $line);
1173
            }
1174
1175
            // Clear some vars
1176
            foreach ($config_exclude_vars as $var) {
1177
                if (strpos($line, $var) > 0) {
1178
                    $line = "'".$var."' => '<removed>'\n";
1179
                }
1180
            }
1181
1182
            // Complete line to display
1183
            $list_of_options .= $line;
1184
        }
1185
    }
1186
1187
    // Get error
1188
    $err = error_get_last();
1189
1190
    // Get 10 latest errors in Teampass
1191
    $teampass_errors = '';
1192
    $rows = DB::query(
1193
        'SELECT label, date AS error_date
1194
        FROM ' . prefixTable('log_system') . "
1195
        WHERE `type` LIKE 'error'
1196
        ORDER BY `date` DESC
1197
        LIMIT 0, 10"
1198
    );
1199
    if (DB::count() > 0) {
1200
        foreach ($rows as $record) {
1201
            if (empty($teampass_errors) === true) {
1202
                $teampass_errors = ' * ' . date($SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'], (int) $record['error_date']) . ' - ' . $record['label'];
1203
            } else {
1204
                $teampass_errors .= ' * ' . date($SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'], (int) $record['error_date']) . ' - ' . $record['label'];
1205
            }
1206
        }
1207
    }
1208
1209
    $link = mysqli_connect(DB_HOST, DB_USER, DB_PASSWD_CLEAR, DB_NAME, (int) DB_PORT, null);
1210
1211
    // Now prepare text
1212
    $txt = '### Page on which it happened
1213
' . $post_data['current_page'] . '
1214
1215
### Steps to reproduce
1216
1.
1217
2.
1218
3.
1219
1220
### Expected behaviour
1221
Tell us what should happen
1222
1223
1224
### Actual behaviour
1225
Tell us what happens instead
1226
1227
### Server configuration
1228
**Operating system**: ' . php_uname() . '
1229
1230
**Web server:** ' . $_SERVER['SERVER_SOFTWARE'] . '
1231
1232
**Database:** ' . ($link === false ? langHdl('undefined') : mysqli_get_server_info($link)) . '
1233
1234
**PHP version:** ' . PHP_VERSION . '
1235
1236
**Teampass version:** ' . TP_VERSION_FULL . '
1237
1238
**Teampass configuration file:**
1239
```
1240
' . $list_of_options . '
1241
```
1242
1243
**Updated from an older Teampass or fresh install:**
1244
1245
### Client configuration
1246
1247
**Browser:** ' . $post_data['browser_name'] . ' - ' . $post_data['browser_version'] . '
1248
1249
**Operating system:** ' . $post_data['os'] . ' - ' . $post_data['os_archi'] . 'bits
1250
1251
### Logs
1252
1253
#### Web server error log
1254
```
1255
' . $err['message'] . ' - ' . $err['file'] . ' (' . $err['line'] . ')
1256
```
1257
1258
#### Teampass 10 last system errors
1259
```
1260
' . $teampass_errors . '
1261
```
1262
1263
#### Log from the web-browser developer console (CTRL + SHIFT + i)
1264
```
1265
Insert the log here and especially the answer of the query that failed.
1266
```
1267
';
1268
1269
    return prepareExchangedData(
1270
        array(
1271
            'html' => $txt,
1272
            'error' => '',
1273
        ),
1274
        'encode'
1275
    );
1276
}
1277
1278
function isUserPasswordCorrect(
1279
    int $post_user_id,
1280
    string $post_user_password,
1281
    array $SETTINGS
1282
): string
1283
{
1284
    if (is_null($post_user_id) === false && isset($post_user_id) === true && empty($post_user_id) === false) {
1285
        // Check if user exists
1286
        $userInfo = DB::queryFirstRow(
1287
            'SELECT public_key, private_key, pw
1288
            FROM ' . prefixTable('users') . '
1289
            WHERE id = %i',
1290
            $post_user_id
1291
        );
1292
        if (DB::count() > 0) {
1293
            // Get one item
1294
            $record = DB::queryFirstRow(
1295
                'SELECT id, pw
1296
                FROM ' . prefixTable('items') . '
1297
                WHERE perso = 0'
1298
            );
1299
1300
            // Get itemKey from current user
1301
            $currentUserKey = DB::queryFirstRow(
1302
                'SELECT share_key, increment_id
1303
                FROM ' . prefixTable('sharekeys_items') . '
1304
                WHERE object_id = %i AND user_id = %i',
1305
                $record['id'],
1306
                $post_user_id
1307
            );
1308
1309
            if (count($currentUserKey) > 0) {
1310
                // Decrypt itemkey with user key
1311
                // use old password to decrypt private_key
1312
                $_SESSION['user']['private_key'] = decryptPrivateKey($post_user_password, $userInfo['private_key']);
1313
                $_SESSION['user']['public_key'] = decryptPrivateKey($post_user_password, $userInfo['public_key']);
1314
                $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $_SESSION['user']['private_key']);
1315
1316
                if (empty(base64_decode($itemKey)) === false) {
1317
                    // GOOD password
1318
                    return prepareExchangedData(
1319
                        array(
1320
                            'error' => false,
1321
                            'message' => '',
1322
                            'debug' => '',
1323
                        ),
1324
                        'encode'
1325
                    );
1326
                }
1327
            }
1328
            
1329
            // Use the password check
1330
            // load passwordLib library
1331
            $pwdlib = new SplClassLoader('PasswordLib', $SETTINGS['cpassman_dir'] . '/includes/libraries');
1332
            $pwdlib->register();
1333
            $pwdlib = new PasswordLib\PasswordLib();
1334
            
1335
            if ($pwdlib->verifyPasswordHash(htmlspecialchars_decode($post_user_password), $userInfo['pw']) === true) {
1336
                // GOOD password
1337
                return prepareExchangedData(
1338
                    array(
1339
                        'error' => false,
1340
                        'message' => '',
1341
                        'debug' => '',
1342
                    ),
1343
                    'encode'
1344
                );
1345
            }
1346
        }
1347
    }
1348
1349
    return prepareExchangedData(
1350
        array(
1351
            'error' => true,
1352
            'message' => langHdl('password_is_not_correct'),
1353
            'debug' => isset($itemKey) === true ? base64_decode($itemKey) : '',
1354
        ),
1355
        'encode'
1356
    );
1357
}
1358
1359
function changePrivateKeyEncryptionPassword(
1360
    int $post_user_id,
1361
    string $post_current_code,
1362
    string $post_new_code,
1363
    string $post_action_type,
1364
    array $SETTINGS
1365
): string
1366
{
1367
    if (empty($post_new_code) === true) {
1368
        $post_new_code = $_SESSION['user_pwd'];
1369
    }
1370
1371
    if (is_null($post_user_id) === false && isset($post_user_id) === true && empty($post_user_id) === false) {
1372
        // Get user info
1373
        $userData = DB::queryFirstRow(
1374
            'SELECT private_key
1375
            FROM ' . prefixTable('users') . '
1376
            WHERE id = %i',
1377
            $post_user_id
1378
        );
1379
        if (DB::count() > 0) {
1380
            if ($post_action_type === 'encrypt_privkey_with_user_password') {
1381
                // Here the user has his private key encrypted with an OTC.
1382
                // We need to encrypt it with his real password
1383
                $privateKey = decryptPrivateKey($post_new_code, $userData['private_key']);
1384
                $hashedPrivateKey = encryptPrivateKey($post_current_code, $privateKey);
1385
            } else {
1386
                $privateKey = decryptPrivateKey($post_current_code, $userData['private_key']);
1387
                $hashedPrivateKey = encryptPrivateKey($post_new_code, $privateKey);
1388
            }
1389
1390
            // Update user account
1391
            DB::update(
1392
                prefixTable('users'),
1393
                array(
1394
                    'private_key' => $hashedPrivateKey,
1395
                    'special' => 'none',
1396
                ),
1397
                'id = %i',
1398
                $post_user_id
1399
            );
1400
1401
            // Load superGlobals
1402
            include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
1403
            $superGlobal = new protect\SuperGlobal\SuperGlobal();
1404
1405
            $superGlobal->put('private_key', $privateKey, 'SESSION', 'user');
1406
        }
1407
1408
        // Return
1409
        return prepareExchangedData(
1410
            array(
1411
                'error' => false,
1412
                'message' => '',
1413
            ),
1414
            'encode'
1415
        );
1416
    }
1417
    
1418
    return prepareExchangedData(
1419
        array(
1420
            'error' => true,
1421
            'message' => langHdl('error_no_user'),
1422
            'debug' => '',
1423
        ),
1424
        'encode'
1425
    );
1426
}
1427
1428
function initializeUserPassword(
1429
    int $post_user_id,
1430
    string $post_special,
1431
    string $post_user_password,
1432
    string $post_self_change,
1433
    array $SETTINGS
1434
): string
1435
{
1436
    if (is_null($post_user_id) === false && isset($post_user_id) === true && empty($post_user_id) === false) {
1437
        // Get user info
1438
        $userData = DB::queryFirstRow(
1439
            'SELECT email, auth_type, login
1440
            FROM ' . prefixTable('users') . '
1441
            WHERE id = %i',
1442
            $post_user_id
1443
        );
1444
        if (DB::count() > 0 && empty($userData['email']) === false) {
1445
            // If user pwd is empty then generate a new one and send it to user
1446
            if (isset($post_user_password) === false || empty($post_user_password) === true) {
1447
                // Generate new password
1448
                $post_user_password = generateQuickPassword();
1449
            }
1450
1451
            // If LDAP enabled, then
1452
            // check that this password is correct
1453
            $continue = true;
1454
            if ($userData['auth_type'] === 'ldap' && (int) $SETTINGS['ldap_mode'] === 1) {
1455
                $continue = ldapCheckUserPassword(
1456
                    $userData['login'],
1457
                    $post_user_password,
1458
                    $SETTINGS
1459
                );
1460
            }
1461
1462
            if ($continue === true) {
1463
                // Only change if email is successfull
1464
                // GEnerate new keys
1465
                $userKeys = generateUserKeys($post_user_password);
1466
1467
                // load passwordLib library
1468
                $pwdlib = new SplClassLoader('PasswordLib', '../includes/libraries');
1469
                $pwdlib->register();
1470
                $pwdlib = new PasswordLib\PasswordLib();
1471
1472
                // Update user account
1473
                DB::update(
1474
                    prefixTable('users'),
1475
                    array(
1476
                        'special' => $post_special,
1477
                        'pw' => $pwdlib->createPasswordHash($post_user_password),
1478
                        'public_key' => $userKeys['public_key'],
1479
                        'private_key' => $userKeys['private_key'],
1480
                        'last_pw_change' => time(),
1481
                    ),
1482
                    'id = %i',
1483
                    $post_user_id
1484
                );
1485
1486
                // Return
1487
                return prepareExchangedData(
1488
                    array(
1489
                        'error' => false,
1490
                        'message' => '',
1491
                        'user_pwd' => $post_user_password,
1492
                        'user_email' => $userData['email'],
1493
                    ),
1494
                    'encode'
1495
                );
1496
            }
1497
            // Return error
1498
            return prepareExchangedData(
1499
                array(
1500
                    'error' => true,
1501
                    'message' => $continue === false ? langHdl('password_doesnot_correspond_to_ldap_one') : $emailError,
0 ignored issues
show
introduced by
The condition $continue === false is always true.
Loading history...
Comprehensibility Best Practice introduced by
The variable $emailError seems to be never defined.
Loading history...
1502
                    'debug' => '',
1503
                    'self_change' => $post_self_change,
1504
                ),
1505
                'encode'
1506
            );
1507
        }
1508
1509
        // Error
1510
        return prepareExchangedData(
1511
            array(
1512
                'error' => true,
1513
                'message' => langHdl('no_email_set'),
1514
                'debug' => '',
1515
            ),
1516
            'encode'
1517
        );
1518
    }
1519
    
1520
    return prepareExchangedData(
1521
        array(
1522
            'error' => true,
1523
            'message' => langHdl('error_no_user'),
1524
            'debug' => '',
1525
        ),
1526
        'encode'
1527
    );
1528
}
1529
1530
function sendMailToUser(
1531
    string $post_receipt,
1532
    string $post_body,
1533
    string $post_subject,
1534
    string $post_replace,
1535
    array $SETTINGS
1536
): string
1537
{
1538
    if (count($post_replace) > 0 && is_null($post_replace) === false) {
0 ignored issues
show
Bug introduced by
$post_replace of type string is incompatible with the type Countable|array expected by parameter $value of count(). ( Ignorable by Annotation )

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

1538
    if (count(/** @scrutinizer ignore-type */ $post_replace) > 0 && is_null($post_replace) === false) {
Loading history...
1539
        $post_body = str_replace(
1540
            array_keys($post_replace),
1541
            array_values($post_replace),
1542
            $post_body
1543
        );
1544
    }
1545
    $ret = sendEmail(
1546
        $post_subject,
1547
        $post_body,
1548
        $post_receipt,
1549
        $SETTINGS,
1550
        '',
1551
        false
1552
    );
1553
1554
    $ret = json_decode($ret, true);
1555
1556
    return prepareExchangedData(
1557
        array(
1558
            'error' => empty($ret['error']) === true ? false : true,
1559
            'message' => $ret['message'],
1560
        ),
1561
        'encode'
1562
    );
1563
}
1564
1565
function generateOneTimeCode(
1566
    int $post_user_id,
1567
    array $SETTINGS
1568
): string
1569
{
1570
    if (is_null($post_user_id) === false && isset($post_user_id) === true && empty($post_user_id) === false) {
1571
        // Get user info
1572
        $userData = DB::queryFirstRow(
1573
            'SELECT email, auth_type, login
1574
            FROM ' . prefixTable('users') . '
1575
            WHERE id = %i',
1576
            $post_user_id
1577
        );
1578
        if (DB::count() > 0 && empty($userData['email']) === false) {
1579
            // Generate pwd
1580
            $password = generateQuickPassword();
1581
1582
            // GEnerate new keys
1583
            $userKeys = generateUserKeys($password);
1584
1585
            // Save in DB
1586
            DB::update(
1587
                prefixTable('users'),
1588
                array(
1589
                    'public_key' => $userKeys['public_key'],
1590
                    'private_key' => $userKeys['private_key'],
1591
                    'special' => 'generate-keys',
1592
                ),
1593
                'id=%i',
1594
                $post_user_id
1595
            );
1596
1597
            return prepareExchangedData(
1598
                array(
1599
                    'error' => false,
1600
                    'message' => '',
1601
                    'userTemporaryCode' => $password,
1602
                ),
1603
                'encode'
1604
            );
1605
        }
1606
        
1607
        return prepareExchangedData(
1608
            array(
1609
                'error' => true,
1610
                'message' => langHdl('no_email_set'),
1611
            ),
1612
            'encode'
1613
        );
1614
    }
0 ignored issues
show
Bug Best Practice introduced by
The function implicitly returns null when the if condition on line 1570 is false. This is incompatible with the type-hinted return string. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
1615
}
1616
1617
function startReEncryptingUserSharekeys(
1618
    int $post_user_id,
1619
    string $post_self_change,
1620
    array $SETTINGS
1621
): string
1622
{
1623
    $post_user_id = is_null($post_user_id) === true ? $_SESSION['user_id'] : $post_user_id;
0 ignored issues
show
introduced by
The condition is_null($post_user_id) === true is always false.
Loading history...
1624
1625
    if (is_null($post_user_id) === false && isset($post_user_id) === true && empty($post_user_id) === false) {
1626
        // Check if user exists
1627
        DB::queryFirstRow(
1628
            'SELECT *
1629
            FROM ' . prefixTable('users') . '
1630
            WHERE id = %i',
1631
            $post_user_id
1632
        );
1633
        if (DB::count() > 0) {
1634
            // Include libraries
1635
            include_once $SETTINGS['cpassman_dir'] . '/sources/aes.functions.php';
1636
1637
            // CLear old sharekeys
1638
            if ($post_self_change === false) {
0 ignored issues
show
introduced by
The condition $post_self_change === false is always false.
Loading history...
1639
                deleteUserObjetsKeys($post_user_id, $SETTINGS);
1640
            }
1641
1642
            // Continu with next step
1643
            return prepareExchangedData(
1644
                array(
1645
                    'error' => false,
1646
                    'message' => '',
1647
                    'step' => 'step1',
1648
                    'userId' => $post_user_id,
1649
                    'start' => 0,
1650
                    'self_change' => $post_self_change,
1651
                ),
1652
                'encode'
1653
            );
1654
        }
1655
        // Nothing to do
1656
        return prepareExchangedData(
1657
            array(
1658
                'error' => true,
1659
                'message' => langHdl('error_no_user'),
1660
            ),
1661
            'encode'
1662
        );
1663
    }
1664
1665
    return prepareExchangedData(
1666
        array(
1667
            'error' => true,
1668
            'message' => langHdl('error_no_user'),
1669
        ),
1670
        'encode'
1671
    );
1672
}
1673
1674
function continueReEncryptingUserSharekeys(
1675
    int $post_user_id,
1676
    string $post_self_change,
1677
    string $post_action,
1678
    int $post_start,
1679
    int $post_length,
1680
    array $SETTINGS
1681
): string
1682
{
1683
    if (is_null($post_user_id) === false && isset($post_user_id) === true && empty($post_user_id) === false) {
1684
        // Check if user exists
1685
        $userInfo = DB::queryFirstRow(
1686
            'SELECT public_key
1687
            FROM ' . prefixTable('users') . '
1688
            WHERE id = %i',
1689
            $post_user_id
1690
        );
1691
        if (DB::count() > 0) {
1692
            // Include libraries
1693
            include_once $SETTINGS['cpassman_dir'] . '/sources/aes.functions.php';
1694
1695
            // WHAT STEP TO PERFORM?
1696
            if ($post_action === 'step0') {
1697
                // CLear old sharekeys
1698
                if ($post_self_change === false) {
0 ignored issues
show
introduced by
The condition $post_self_change === false is always false.
Loading history...
1699
                    deleteUserObjetsKeys($post_user_id, $SETTINGS);
1700
                }
1701
1702
                $post_action = 'step1';
1703
            }
1704
1705
            // STEP 1 - ITEMS
1706
            if ($post_action === 'step1') {
1707
                $return = continueReEncryptingUserSharekeysStep1(
1708
                    $post_user_id,
1709
                    $post_self_change,
1710
                    $post_action,
1711
                    $post_start,
1712
                    $post_length,
1713
                    $userInfo['public_key'],
1714
                    $SETTINGS
1715
                );
1716
1717
                $next_start = $return['next_start'];
1718
                $post_action = $return['post_action'];
1719
            }
1720
1721
            // STEP 2 - LOGS
1722
            if ($post_action === 'step2') {
1723
                $return = continueReEncryptingUserSharekeysStep2(
1724
                    $post_user_id,
1725
                    $post_self_change,
1726
                    $post_action,
1727
                    $post_start,
1728
                    $post_length,
1729
                    $userInfo['public_key'],
1730
                    $SETTINGS
1731
                );
1732
1733
                $next_start = $return['next_start'];
1734
                $post_action = $return['post_action'];
1735
            }
1736
1737
            // STEP 3 - FIELDS
1738
            if ($post_action === 'step3') {
1739
                $return = continueReEncryptingUserSharekeysStep3(
0 ignored issues
show
Bug introduced by
The call to continueReEncryptingUserSharekeysStep3() has too few arguments starting with SETTINGS. ( Ignorable by Annotation )

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

1739
                $return = /** @scrutinizer ignore-call */ continueReEncryptingUserSharekeysStep3(

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
1740
                    $post_user_id,
1741
                    $post_self_change,
1742
                    $post_action,
1743
                    $post_start,
1744
                    $post_length,
1745
                    $SETTINGS
0 ignored issues
show
Bug introduced by
$SETTINGS of type array is incompatible with the type string expected by parameter $user_public_key of continueReEncryptingUserSharekeysStep3(). ( Ignorable by Annotation )

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

1745
                    /** @scrutinizer ignore-type */ $SETTINGS
Loading history...
1746
                );
1747
1748
                $next_start = $return['next_start'];
1749
                $post_action = $return['post_action'];
1750
            }
1751
            
1752
            // STEP 4 - SUGGESTIONS
1753
            if ($post_action === 'step4') {
1754
                $return = continueReEncryptingUserSharekeysStep4(
1755
                    $post_user_id,
1756
                    $post_self_change,
1757
                    $post_action,
1758
                    $post_start,
1759
                    $post_length,
1760
                    $userInfo['public_key'],
1761
                    $SETTINGS
1762
                );
1763
1764
                $next_start = $return['next_start'];
1765
                $post_action = $return['post_action'];
1766
            }
1767
            
1768
            // STEP 5 - FILES
1769
            if ($post_action === 'step5') {
1770
                $return = continueReEncryptingUserSharekeysStep5(
1771
                    $post_user_id,
1772
                    $post_self_change,
1773
                    $post_action,
1774
                    $post_start,
1775
                    $post_length,
1776
                    $userInfo['public_key'],
1777
                    $SETTINGS
1778
                );
1779
1780
                $next_start = $return['next_start'];
1781
                $post_action = $return['post_action'];
1782
            }
1783
            
1784
            // STEP 6 - PERSONAL ITEMS
1785
            if ($post_action === 'step6') {
1786
                $return = continueReEncryptingUserSharekeysStep6(
1787
                    $post_user_id,
1788
                    $post_self_change,
1789
                    $post_action,
1790
                    $post_start,
1791
                    $post_length,
1792
                    $userInfo['public_key'],
1793
                    $SETTINGS
1794
                );
1795
1796
                $next_start = $return['next_start'];
1797
                $post_action = $return['post_action'];
1798
            }
1799
1800
            // Continu with next step
1801
            return prepareExchangedData(
1802
                array(
1803
                    'error' => false,
1804
                    'message' => '',
1805
                    'step' => $post_action,
1806
                    'start' => isset($next_start) === true ? $next_start : 0,
1807
                    'userId' => $post_user_id,
1808
                    'self_change' => $post_self_change,
1809
                ),
1810
                'encode'
1811
            );
1812
        }
1813
        
1814
        // Nothing to do
1815
        return prepareExchangedData(
1816
            array(
1817
                'error' => false,
1818
                'message' => '',
1819
                'step' => 'finished',
1820
                'start' => 0,
1821
                'userId' => $post_user_id,
1822
                'self_change' => $post_self_change,
1823
            ),
1824
            'encode'
1825
        );
1826
    }
1827
    
1828
    // Nothing to do
1829
    return prepareExchangedData(
1830
        array(
1831
            'error' => true,
1832
            'message' => langHdl('error_no_user'),
1833
        ),
1834
        'encode'
1835
    );
1836
}
1837
1838
function continueReEncryptingUserSharekeysStep1(
1839
    int $post_user_id,
1840
    string $post_self_change,
1841
    string $post_action,
1842
    int $post_start,
1843
    int $post_length,
1844
    string $user_public_key,
1845
    array $SETTINGS
1846
): string
1847
{
1848
    // Loop on items
1849
    $rows = DB::query(
1850
        'SELECT id, pw
1851
        FROM ' . prefixTable('items') . '
1852
        WHERE perso = 0
1853
        LIMIT ' . $post_start . ', ' . $post_length
1854
    );
1855
    foreach ($rows as $record) {
1856
        // Get itemKey from current user
1857
        $currentUserKey = DB::queryFirstRow(
1858
            'SELECT share_key, increment_id
1859
            FROM ' . prefixTable('sharekeys_items') . '
1860
            WHERE object_id = %i AND user_id = %i',
1861
            $record['id'],
1862
            $_SESSION['user_id']
1863
        );
1864
        if (count($currentUserKey) === 0) continue;
1865
1866
        // Decrypt itemkey with admin key
1867
        $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $_SESSION['user']['private_key']);
1868
        
1869
        // Encrypt Item key
1870
        $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
1871
        
1872
        // Save the key in DB
1873
        if ($post_self_change === false) {
1874
            DB::insert(
1875
                prefixTable('sharekeys_items'),
1876
                array(
1877
                    'object_id' => (int) $record['id'],
1878
                    'user_id' => (int) $post_user_id,
1879
                    'share_key' => $share_key_for_item,
1880
                )
1881
            );
1882
        } else {
1883
            // Get itemIncrement from selected user
1884
            if ((int) $post_user_id !== (int) $_SESSION['user_id']) {
1885
                $currentUserKey = DB::queryFirstRow(
1886
                    'SELECT increment_id
1887
                    FROM ' . prefixTable('sharekeys_items') . '
1888
                    WHERE object_id = %i AND user_id = %i',
1889
                    $record['id'],
1890
                    $post_user_id
1891
                );
1892
1893
                if (DB::count() > 0) {
1894
                    // NOw update
1895
                    DB::update(
1896
                        prefixTable('sharekeys_items'),
1897
                        array(
1898
                            'share_key' => $share_key_for_item,
1899
                        ),
1900
                        'increment_id = %i',
1901
                        $currentUserKey['increment_id']
1902
                    );
1903
                } else {
1904
                    DB::insert(
1905
                        prefixTable('sharekeys_items'),
1906
                        array(
1907
                            'object_id' => (int) $record['id'],
1908
                            'user_id' => (int) $post_user_id,
1909
                            'share_key' => $share_key_for_item,
1910
                        )
1911
                    );
1912
                }
1913
            }
1914
        }
1915
    }
1916
1917
    // SHould we change step?
1918
    DB::query(
1919
        'SELECT *
1920
        FROM ' . prefixTable('items') . '
1921
        WHERE perso = 0'
1922
    );
1923
1924
    $next_start = (int) $post_start + (int) $post_length;
1925
    return [
0 ignored issues
show
Bug Best Practice introduced by
The expression return array('next_start...() ? 'step2' : 'step1') returns the type array<string,integer|string> which is incompatible with the type-hinted return string.
Loading history...
1926
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
1927
        'post_action' => $next_start > DB::count() ? 'step2' : 'step1',
1928
    ];
1929
}
1930
1931
function continueReEncryptingUserSharekeysStep2(
1932
    int $post_user_id,
1933
    string $post_self_change,
1934
    string $post_action,
1935
    int $post_start,
1936
    int $post_length,
1937
    string $user_public_key,
1938
    array $SETTINGS
1939
): string
1940
{
1941
    // Loop on logs
1942
    $rows = DB::query(
1943
        'SELECT increment_id
1944
        FROM ' . prefixTable('log_items') . '
1945
        WHERE raison LIKE "at_pw :%" AND encryption_type = "teampass_aes"
1946
        LIMIT ' . $post_start . ', ' . $post_length
1947
    );
1948
    foreach ($rows as $record) {
1949
        // Get itemKey from current user
1950
        $currentUserKey = DB::queryFirstRow(
1951
            'SELECT share_key
1952
            FROM ' . prefixTable('sharekeys_logs') . '
1953
            WHERE object_id = %i AND user_id = %i',
1954
            $record['increment_id'],
1955
            $_SESSION['user_id']
1956
        );
1957
1958
        // Decrypt itemkey with admin key
1959
        $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $_SESSION['user']['private_key']);
1960
1961
        // Encrypt Item key
1962
        $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
1963
1964
        // Save the key in DB
1965
        if ($post_self_change === false) {
1966
            DB::insert(
1967
                prefixTable('sharekeys_logs'),
1968
                array(
1969
                    'object_id' => (int) $record['increment_id'],
1970
                    'user_id' => (int) $post_user_id,
1971
                    'share_key' => $share_key_for_item,
1972
                )
1973
            );
1974
        } else {
1975
            // Get itemIncrement from selected user
1976
            if ((int) $post_user_id !== (int) $_SESSION['user_id']) {
1977
                $currentUserKey = DB::queryFirstRow(
1978
                    'SELECT increment_id
1979
                    FROM ' . prefixTable('sharekeys_items') . '
1980
                    WHERE object_id = %i AND user_id = %i',
1981
                    $record['id'],
1982
                    $post_user_id
1983
                );
1984
            }
1985
1986
            // NOw update
1987
            DB::update(
1988
                prefixTable('sharekeys_logs'),
1989
                array(
1990
                    'share_key' => $share_key_for_item,
1991
                ),
1992
                'increment_id = %i',
1993
                $currentUserKey['increment_id']
1994
            );
1995
        }
1996
    }
1997
1998
    // SHould we change step?
1999
    DB::query(
2000
        'SELECT increment_id
2001
        FROM ' . prefixTable('log_items') . '
2002
        WHERE raison LIKE "at_pw :%" AND encryption_type = "teampass_aes"'
2003
    );
2004
2005
    $next_start = (int) $post_start + (int) $post_length;
2006
    return [
0 ignored issues
show
Bug Best Practice introduced by
The expression return array('next_start...() ? 'step3' : 'step2') returns the type array<string,integer|string> which is incompatible with the type-hinted return string.
Loading history...
2007
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2008
        'post_action' => $next_start > DB::count() ? 'step3' : 'step2',
2009
    ];
2010
}
2011
2012
function continueReEncryptingUserSharekeysStep3(
2013
    int $post_user_id,
2014
    string $post_self_change,
2015
    string $post_action,
2016
    int $post_start,
2017
    int $post_length,
2018
    string $user_public_key,
2019
    array $SETTINGS
2020
): string
2021
{
2022
    // Loop on fields
2023
    $rows = DB::query(
2024
        'SELECT id
2025
        FROM ' . prefixTable('categories_items') . '
2026
        WHERE encryption_type = "teampass_aes"
2027
        LIMIT ' . $post_start . ', ' . $post_length
2028
    );
2029
    foreach ($rows as $record) {
2030
        // Get itemKey from current user
2031
        $currentUserKey = DB::queryFirstRow(
2032
            'SELECT share_key
2033
            FROM ' . prefixTable('sharekeys_fields') . '
2034
            WHERE object_id = %i AND user_id = %i',
2035
            $record['id'],
2036
            $_SESSION['user_id']
2037
        );
2038
2039
        // Decrypt itemkey with admin key
2040
        $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $_SESSION['user']['private_key']);
2041
2042
        // Encrypt Item key
2043
        $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
2044
2045
        // Save the key in DB
2046
        if ($post_self_change === false) {
2047
            DB::insert(
2048
                prefixTable('sharekeys_fields'),
2049
                array(
2050
                    'object_id' => (int) $record['id'],
2051
                    'user_id' => (int) $post_user_id,
2052
                    'share_key' => $share_key_for_item,
2053
                )
2054
            );
2055
        } else {
2056
            // Get itemIncrement from selected user
2057
            if ((int) $post_user_id !== (int) $_SESSION['user_id']) {
2058
                $currentUserKey = DB::queryFirstRow(
2059
                    'SELECT increment_id
2060
                    FROM ' . prefixTable('sharekeys_items') . '
2061
                    WHERE object_id = %i AND user_id = %i',
2062
                    $record['id'],
2063
                    $post_user_id
2064
                );
2065
            }
2066
2067
            // NOw update
2068
            DB::update(
2069
                prefixTable('sharekeys_fields'),
2070
                array(
2071
                    'share_key' => $share_key_for_item,
2072
                ),
2073
                'increment_id = %i',
2074
                $currentUserKey['increment_id']
2075
            );
2076
        }
2077
    }
2078
2079
    // SHould we change step?
2080
    DB::query(
2081
        'SELECT *
2082
        FROM ' . prefixTable('categories_items') . '
2083
        WHERE encryption_type = "teampass_aes"'
2084
    );
2085
2086
    $next_start = (int) $post_start + (int) $post_length;
2087
    return [
0 ignored issues
show
Bug Best Practice introduced by
The expression return array('next_start...() ? 'step4' : 'step3') returns the type array<string,integer|string> which is incompatible with the type-hinted return string.
Loading history...
2088
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2089
        'post_action' => $next_start > DB::count() ? 'step4' : 'step3',
2090
    ];
2091
}
2092
2093
function continueReEncryptingUserSharekeysStep4(
2094
    int $post_user_id,
2095
    string $post_self_change,
2096
    string $post_action,
2097
    int $post_start,
2098
    int $post_length,
2099
    string $user_public_key,
2100
    array $SETTINGS
2101
): string
2102
{
2103
    // Loop on suggestions
2104
    $rows = DB::query(
2105
        'SELECT id
2106
        FROM ' . prefixTable('suggestion') . '
2107
        LIMIT ' . $post_start . ', ' . $post_length
2108
    );
2109
    foreach ($rows as $record) {
2110
        // Get itemKey from current user
2111
        $currentUserKey = DB::queryFirstRow(
2112
            'SELECT share_key
2113
            FROM ' . prefixTable('sharekeys_suggestions') . '
2114
            WHERE object_id = %i AND user_id = %i',
2115
            $record['id'],
2116
            $_SESSION['user_id']
2117
        );
2118
2119
        // Decrypt itemkey with admin key
2120
        $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $_SESSION['user']['private_key']);
2121
2122
        // Encrypt Item key
2123
        $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
2124
2125
        // Save the key in DB
2126
        if ($post_self_change === false) {
2127
            DB::insert(
2128
                prefixTable('sharekeys_suggestions'),
2129
                array(
2130
                    'object_id' => (int) $record['id'],
2131
                    'user_id' => (int) $post_user_id,
2132
                    'share_key' => $share_key_for_item,
2133
                )
2134
            );
2135
        } else {
2136
            // Get itemIncrement from selected user
2137
            if ((int) $post_user_id !== (int) $_SESSION['user_id']) {
2138
                $currentUserKey = DB::queryFirstRow(
2139
                    'SELECT increment_id
2140
                    FROM ' . prefixTable('sharekeys_items') . '
2141
                    WHERE object_id = %i AND user_id = %i',
2142
                    $record['id'],
2143
                    $post_user_id
2144
                );
2145
            }
2146
2147
            // NOw update
2148
            DB::update(
2149
                prefixTable('sharekeys_suggestions'),
2150
                array(
2151
                    'share_key' => $share_key_for_item,
2152
                ),
2153
                'increment_id = %i',
2154
                $currentUserKey['increment_id']
2155
            );
2156
        }
2157
    }
2158
2159
    // SHould we change step?
2160
    DB::query(
2161
        'SELECT *
2162
        FROM ' . prefixTable('suggestion')
2163
    );
2164
2165
    $next_start = (int) $post_start + (int) $post_length;
2166
    return [
0 ignored issues
show
Bug Best Practice introduced by
The expression return array('next_start...() ? 'step5' : 'step4') returns the type array<string,integer|string> which is incompatible with the type-hinted return string.
Loading history...
2167
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2168
        'post_action' => $next_start > DB::count() ? 'step5' : 'step4',
2169
    ];
2170
}
2171
2172
function continueReEncryptingUserSharekeysStep5(
2173
    int $post_user_id,
2174
    string $post_self_change,
2175
    string $post_action,
2176
    int $post_start,
2177
    int $post_length,
2178
    string $user_public_key,
2179
    array $SETTINGS
2180
): string
2181
{
2182
    // Loop on files
2183
    $rows = DB::query(
2184
        'SELECT id
2185
        FROM ' . prefixTable('files') . '
2186
        WHERE status = "' . TP_ENCRYPTION_NAME . '"
2187
        LIMIT ' . $post_start . ', ' . $post_length
2188
    ); //aes_encryption
2189
    foreach ($rows as $record) {
2190
        // Get itemKey from current user
2191
        $currentUserKey = DB::queryFirstRow(
2192
            'SELECT share_key
2193
            FROM ' . prefixTable('sharekeys_files') . '
2194
            WHERE object_id = %i AND user_id = %i',
2195
            $record['id'],
2196
            $_SESSION['user_id']
2197
        );
2198
2199
        // Decrypt itemkey with admin key
2200
        $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $_SESSION['user']['private_key']);
2201
2202
        // Encrypt Item key
2203
        $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
2204
2205
        // Save the key in DB
2206
        if ($post_self_change === false) {
2207
            DB::insert(
2208
                prefixTable('sharekeys_files'),
2209
                array(
2210
                    'object_id' => (int) $record['id'],
2211
                    'user_id' => (int) $post_user_id,
2212
                    'share_key' => $share_key_for_item,
2213
                )
2214
            );
2215
        } else {
2216
            // Get itemIncrement from selected user
2217
            if ((int) $post_user_id !== (int) $_SESSION['user_id']) {
2218
                $currentUserKey = DB::queryFirstRow(
2219
                    'SELECT increment_id
2220
                    FROM ' . prefixTable('sharekeys_items') . '
2221
                    WHERE object_id = %i AND user_id = %i',
2222
                    $record['id'],
2223
                    $post_user_id
2224
                );
2225
            }
2226
2227
            // NOw update
2228
            DB::update(
2229
                prefixTable('sharekeys_files'),
2230
                array(
2231
                    'share_key' => $share_key_for_item,
2232
                ),
2233
                'increment_id = %i',
2234
                $currentUserKey['increment_id']
2235
            );
2236
        }
2237
    }
2238
2239
    // SHould we change step?
2240
    DB::query(
2241
        'SELECT *
2242
        FROM ' . prefixTable('files') . '
2243
        WHERE status = "' . TP_ENCRYPTION_NAME . '"'
2244
    );
2245
2246
    $next_start = (int) $post_start + (int) $post_length;
2247
    return [
0 ignored issues
show
Bug Best Practice introduced by
The expression return array('next_start...() ? 'step6' : 'step5') returns the type array<string,integer|string> which is incompatible with the type-hinted return string.
Loading history...
2248
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2249
        'post_action' => $next_start > DB::count() ? 'step6' : 'step5',
2250
    ];
2251
}
2252
2253
function continueReEncryptingUserSharekeysStep6(
2254
    int $post_user_id,
2255
    string $post_self_change,
2256
    string $post_action,
2257
    int $post_start,
2258
    int $post_length,
2259
    string $user_public_key,
2260
    array $SETTINGS
2261
): string
2262
{
2263
    // IF USER IS NOT THE SAME
2264
    if ((int) $post_user_id === (int) $_SESSION['user_id']) {
2265
        return [
0 ignored issues
show
Bug Best Practice introduced by
The expression return array('next_start..._action' => 'finished') returns the type array<string,integer|string> which is incompatible with the type-hinted return string.
Loading history...
2266
            'next_start' => 0,
2267
            'post_action' => 'finished',
2268
        ];
2269
    }
2270
    
2271
    // Loop on persoanl items
2272
    if (count($_SESSION['personal_folders']) > 0) {
2273
        $rows = DB::query(
2274
            'SELECT id, pw
2275
            FROM ' . prefixTable('items') . '
2276
            WHERE perso = 1 AND id_tree IN %ls
2277
            LIMIT ' . $post_start . ', ' . $post_length,
2278
            $_SESSION['personal_folders']
2279
        );
2280
        foreach ($rows as $record) {
2281
            // Get itemKey from current user
2282
            $currentUserKey = DB::queryFirstRow(
2283
                'SELECT share_key, increment_id
2284
                FROM ' . prefixTable('sharekeys_items') . '
2285
                WHERE object_id = %i AND user_id = %i',
2286
                $record['id'],
2287
                $_SESSION['user_id']
2288
            );
2289
2290
            // Decrypt itemkey with admin key
2291
            $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $_SESSION['user']['private_key']);
2292
2293
            // Encrypt Item key
2294
            $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
2295
2296
            // Save the key in DB
2297
            if ($post_self_change === false) {
2298
                DB::insert(
2299
                    prefixTable('sharekeys_items'),
2300
                    array(
2301
                        'object_id' => (int) $record['id'],
2302
                        'user_id' => (int) $post_user_id,
2303
                        'share_key' => $share_key_for_item,
2304
                    )
2305
                );
2306
            } else {
2307
                // Get itemIncrement from selected user
2308
                if ((int) $post_user_id !== (int) $_SESSION['user_id']) {
2309
                    $currentUserKey = DB::queryFirstRow(
2310
                        'SELECT increment_id
2311
                        FROM ' . prefixTable('sharekeys_items') . '
2312
                        WHERE object_id = %i AND user_id = %i',
2313
                        $record['id'],
2314
                        $post_user_id
2315
                    );
2316
                }
2317
2318
                // NOw update
2319
                DB::update(
2320
                    prefixTable('sharekeys_items'),
2321
                    array(
2322
                        'share_key' => $share_key_for_item,
2323
                    ),
2324
                    'increment_id = %i',
2325
                    $currentUserKey['increment_id']
2326
                );
2327
            }
2328
        }
2329
    }
2330
2331
    // SHould we change step?
2332
    DB::query(
2333
        'SELECT *
2334
        FROM ' . prefixTable('items') . '
2335
        WHERE perso = 0'
2336
    );
2337
2338
    $next_start = (int) $post_start + (int) $post_length;
2339
    return [
0 ignored issues
show
Bug Best Practice introduced by
The expression return array('next_start...? 'finished' : 'step6') returns the type array<string,integer|string> which is incompatible with the type-hinted return string.
Loading history...
2340
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2341
        'post_action' => $next_start > DB::count() ? 'finished' : 'step6',
2342
    ];
2343
}
2344
2345
function migrateTo3_DoUserPersonalItemsEncryption(
2346
    int $post_user_id,
2347
    int $post_start,
2348
    int $post_length,
2349
    string $post_user_psk,
2350
    array $SETTINGS
2351
) {
2352
    $next_step = '';
2353
2354
    if (is_null($post_user_id) === false && isset($post_user_id) === true && empty($post_user_id) === false) {
2355
        // Check if user exists
2356
        $userInfo = DB::queryFirstRow(
2357
            'SELECT public_key, encrypted_psk
2358
            FROM ' . prefixTable('users') . '
2359
            WHERE id = %i',
2360
            $post_user_id
2361
        );
2362
        if (DB::count() > 0) {
2363
            // check if psk is correct.
2364
            if (empty($userInfo['encrypted_psk']) === false) {
2365
                $user_key_encoded = defuse_validate_personal_key(
2366
                    $post_user_psk,
2367
                    $userInfo['encrypted_psk']
2368
                );
2369
2370
                if (strpos($user_key_encoded, "Error ") !== false) {
2371
                    return prepareExchangedData(
2372
                        array(
2373
                            'error' => true,
2374
                            'message' => langHdl('bad_psk'),
2375
                        ),
2376
                        'encode'
2377
                    );
2378
                }
2379
2380
                // Loop on persoanl items
2381
                $rows = DB::query(
2382
                    'SELECT id, pw
2383
                    FROM ' . prefixTable('items') . '
2384
                    WHERE perso = 1 AND id_tree IN %ls
2385
                    LIMIT ' . $post_start . ', ' . $post_length,
2386
                    $_SESSION['personal_folders']
2387
                );
2388
                $countUserPersonalItems = DB::count();
2389
                foreach ($rows as $record) {
2390
                    if ($record['encryption_type'] !== 'teampass_aes') {
2391
                        // Decrypt with Defuse
2392
                        $passwd = cryption(
2393
                            $record['pw'],
2394
                            $user_key_encoded,
2395
                            'decrypt',
2396
                            $SETTINGS
2397
                        );
2398
2399
                        // Encrypt with Object Key
2400
                        $cryptedStuff = doDataEncryption($passwd['string']);
2401
2402
                        // Store new password in DB
2403
                        DB::update(
2404
                            prefixTable('items'),
2405
                            array(
2406
                                'pw' => $cryptedStuff['encrypted'],
2407
                                'encryption_type' => 'teampass_aes',
2408
                            ),
2409
                            'id = %i',
2410
                            $record['id']
2411
                        );
2412
2413
                        // Insert in DB the new object key for this item by user
2414
                        DB::insert(
2415
                            prefixTable('sharekeys_items'),
2416
                            array(
2417
                                'object_id' => (int) $record['id'],
2418
                                'user_id' => (int) $post_user_id,
2419
                                'share_key' => encryptUserObjectKey($cryptedStuff['objectKey'], $userInfo['public_key']),
2420
                            )
2421
                        );
2422
2423
2424
                        // Does this item has Files?
2425
                        // Loop on files
2426
                        $rows = DB::query(
2427
                            'SELECT id, file
2428
                            FROM ' . prefixTable('files') . '
2429
                            WHERE status != %s
2430
                            AND id_item = %i',
2431
                            TP_ENCRYPTION_NAME,
2432
                            $record['id']
2433
                        );
2434
                        //aes_encryption
2435
                        foreach ($rows as $record2) {
2436
                            // Now decrypt the file
2437
                            prepareFileWithDefuse(
2438
                                'decrypt',
2439
                                $SETTINGS['path_to_upload_folder'] . '/' . $record2['file'],
2440
                                $SETTINGS['path_to_upload_folder'] . '/' . $record2['file'] . '.delete',
2441
                                $post_user_psk
0 ignored issues
show
Bug introduced by
$post_user_psk of type string is incompatible with the type array expected by parameter $SETTINGS of prepareFileWithDefuse(). ( Ignorable by Annotation )

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

2441
                                /** @scrutinizer ignore-type */ $post_user_psk
Loading history...
2442
                            );
2443
2444
                            // Encrypt the file
2445
                            $encryptedFile = encryptFile($record2['file'] . '.delete', $SETTINGS['path_to_upload_folder']);
2446
2447
                            DB::update(
2448
                                prefixTable('files'),
2449
                                array(
2450
                                    'file' => $encryptedFile['fileHash'],
2451
                                    'status' => TP_ENCRYPTION_NAME,
2452
                                ),
2453
                                'id = %i',
2454
                                $record2['id']
2455
                            );
2456
2457
                            // Save key
2458
                            DB::insert(
2459
                                prefixTable('sharekeys_files'),
2460
                                array(
2461
                                    'object_id' => (int) $record2['id'],
2462
                                    'user_id' => (int) $_SESSION['user_id'],
2463
                                    'share_key' => encryptUserObjectKey($encryptedFile['objectKey'], $_SESSION['user']['public_key']),
2464
                                )
2465
                            );
2466
2467
                            // Unlink original file
2468
                            unlink($SETTINGS['path_to_upload_folder'] . '/' . $record2['file']);
2469
                        }
2470
                    }
2471
                }
2472
2473
                // SHould we change step?
2474
                $next_start = (int) $post_start + (int) $post_length;
2475
                if ($next_start > $countUserPersonalItems) {
2476
                    // Now update user
2477
                    DB::update(
2478
                        prefixTable('users'),
2479
                        array(
2480
                            'special' => 'none',
2481
                            'upgrade_needed' => 0,
2482
                            'encrypted_psk' => '',
2483
                        ),
2484
                        'id = %i',
2485
                        $post_user_id
2486
                    );
2487
2488
                    $next_step = 'finished';
2489
                    $next_start = 0;
2490
                }
2491
2492
                // Continu with next step
2493
                return prepareExchangedData(
2494
                    array(
2495
                        'error' => false,
2496
                        'message' => '',
2497
                        'step' => $next_step,
2498
                        'start' => $next_start,
2499
                        'userId' => $post_user_id
2500
                    ),
2501
                    'encode'
2502
                );
2503
            }
2504
        }
2505
        
2506
        // Nothing to do
2507
        return prepareExchangedData(
2508
            array(
2509
                'error' => true,
2510
                'message' => langHdl('error_no_user'),
2511
            ),
2512
            'encode'
2513
        );
2514
    }
2515
    
2516
    // Nothing to do
2517
    return prepareExchangedData(
2518
        array(
2519
            'error' => true,
2520
            'message' => langHdl('error_no_user'),
2521
        ),
2522
        'encode'
2523
    );
2524
}
2525
2526
2527
function getUserInfo(
2528
    int $post_user_id,
2529
    string $post_fields,
2530
    array $SETTINGS
2531
)
2532
{
2533
    if (is_null($post_user_id) === false && isset($post_user_id) === true && empty($post_user_id) === false) {
2534
        // Get user info
2535
        $userData = DB::queryFirstRow(
2536
            'SELECT '.$post_fields.'
2537
            FROM ' . prefixTable('users') . '
2538
            WHERE id = %i',
2539
            $post_user_id
2540
        );
2541
        if (DB::count() > 0) {
2542
            return prepareExchangedData(
2543
                array(
2544
                    'error' => false,
2545
                    'message' => '',
2546
                    'queryResults' => $userData,
2547
                ),
2548
                'encode'
2549
            );
2550
        }
2551
    }
2552
    return prepareExchangedData(
2553
        array(
2554
            'error' => true,
2555
            'message' => langHdl('error_no_user'),
2556
        ),
2557
        'encode'
2558
    );
2559
}
2560
2561
function changeUserAuthenticationPassword(
2562
    int $post_user_id,
2563
    string $post_current_pwd,
2564
    string $post_new_pwd,
2565
    array $SETTINGS
2566
)
2567
{
2568
    if (is_null($post_user_id) === false && isset($post_user_id) === true && empty($post_user_id) === false) {
2569
        // Get user info
2570
        $userData = DB::queryFirstRow(
2571
            'SELECT auth_type, login, private_key
2572
            FROM ' . prefixTable('users') . '
2573
            WHERE id = %i',
2574
            $post_user_id
2575
        );
2576
        if (DB::count() > 0) {
2577
            // Now check if current password is correct
2578
            // For this, just check if it is possible to decrypt the privatekey
2579
            // And compare it to the one in session
2580
            $privateKey = decryptPrivateKey($post_current_pwd, $userData['private_key']);
2581
2582
            // Load superGlobals
2583
            include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
2584
            $superGlobal = new protect\SuperGlobal\SuperGlobal();
2585
2586
            if ($superGlobal->get('private_key', 'SESSION', 'user') === $privateKey) {
2587
                // Encrypt it with new password
2588
                $hashedPrivateKey = encryptPrivateKey($post_new_pwd, $privateKey);
2589
2590
                // Generate new hash for auth password
2591
                // load passwordLib library
2592
                $pwdlib = new SplClassLoader('PasswordLib', '../includes/libraries');
2593
                $pwdlib->register();
2594
                $pwdlib = new PasswordLib\PasswordLib();
2595
2596
                // Prepare variables
2597
                $newPw = $pwdlib->createPasswordHash($post_new_pwd);
2598
2599
                // Update user account
2600
                DB::update(
2601
                    prefixTable('users'),
2602
                    array(
2603
                        'private_key' => $hashedPrivateKey,
2604
                        'pw' => $newPw,
2605
                        'special' => 'none',
2606
                    ),
2607
                    'id = %i',
2608
                    $post_user_id
2609
                );
2610
2611
                $superGlobal->put('private_key', $privateKey, 'SESSION', 'user');
2612
2613
                return prepareExchangedData(
2614
                    array(
2615
                        'error' => false,
2616
                        'message' => langHdl('done'),'',
2617
                    ),
2618
                    'encode'
2619
                );
2620
            }
2621
            
2622
            // ERROR
2623
            return prepareExchangedData(
2624
                array(
2625
                    'error' => true,
2626
                    'message' => langHdl('bad_password'),
2627
                ),
2628
                'encode'
2629
            );
2630
        }
2631
    }
2632
        
2633
    return prepareExchangedData(
2634
        array(
2635
            'error' => true,
2636
            'message' => langHdl('error_no_user'),
2637
        ),
2638
        'encode'
2639
    );
2640
}
2641
2642
            
2643
function changeUserLDAPAuthenticationPassword(
2644
    int $post_user_id,
2645
    string $post_previous_pwd,
2646
    string $post_current_pwd
2647
)
2648
{
2649
    if (is_null($post_user_id) === false && isset($post_user_id) === true && empty($post_user_id) === false) {
2650
        // Get user info
2651
        $userData = DB::queryFirstRow(
2652
            'SELECT auth_type, login, private_key, special
2653
            FROM ' . prefixTable('users') . '
2654
            WHERE id = %i',
2655
            $post_user_id
2656
        );
2657
        
2658
        if (DB::count() > 0) {
2659
            // Now check if current password is correct (only if not ldap)
2660
            if ($userData['auth_type'] === 'ldap' && $userData['special'] === 'auth-pwd-change') {
2661
                // As it is a change for an LDAP user
2662
                
2663
                // Now check if current password is correct
2664
                // For this, just check if it is possible to decrypt the privatekey
2665
                // And compare it to the one in session
2666
                $privateKey = decryptPrivateKey($post_previous_pwd, $userData['private_key']);
2667
2668
                // Encrypt it with new password
2669
                $hashedPrivateKey = encryptPrivateKey($post_current_pwd, $privateKey);
2670
2671
                // Update user account
2672
                DB::update(
2673
                    prefixTable('users'),
2674
                    array(
2675
                        'private_key' => $hashedPrivateKey,
2676
                        'special' => 'none',
2677
                    ),
2678
                    'id = %i',
2679
                    $post_user_id
2680
                );
2681
2682
                // Load superGlobals
2683
                include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $SETTINGS seems to be never defined.
Loading history...
2684
                $superGlobal = new protect\SuperGlobal\SuperGlobal();
2685
                $superGlobal->put('private_key', $privateKey, 'SESSION', 'user');
2686
2687
                return prepareExchangedData(
2688
                    array(
2689
                        'error' => false,
2690
                        'message' => langHdl('done'),'',
2691
                    ),
2692
                    'encode'
2693
                );
2694
            }
2695
2696
            // For this, just check if it is possible to decrypt the privatekey
2697
            // And try to decrypt one existing key
2698
            $privateKey = decryptPrivateKey($post_previous_pwd, $userData['private_key']);
2699
2700
            if (empty($privateKey) === true) {
2701
                return prepareExchangedData(
2702
                    array(
2703
                        'error' => true,
2704
                        'message' => langHdl('password_is_not_correct'),
2705
                    ),
2706
                    'encode'
2707
                );
2708
            }
2709
2710
            // Test if possible to decvrypt one key
2711
            // Get one item
2712
            $record = DB::queryFirstRow(
2713
                'SELECT id, pw
2714
                FROM ' . prefixTable('items') . '
2715
                WHERE perso = 0'
2716
            );
2717
2718
            // Get itemKey from current user
2719
            $currentUserKey = DB::queryFirstRow(
2720
                'SELECT share_key, increment_id
2721
                FROM ' . prefixTable('sharekeys_items') . '
2722
                WHERE object_id = %i AND user_id = %i',
2723
                $record['id'],
2724
                $post_user_id
2725
            );
2726
2727
            if (count($currentUserKey) > 0) {
2728
                // Decrypt itemkey with user key
2729
                // use old password to decrypt private_key
2730
                $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $privateKey);
2731
                
2732
                if (empty(base64_decode($itemKey)) === false) {
2733
                    // GOOD password
2734
                    // Encrypt it with current password
2735
                    $hashedPrivateKey = encryptPrivateKey($post_current_pwd, $privateKey);
2736
                    
2737
                    // Update user account
2738
                    DB::update(
2739
                        prefixTable('users'),
2740
                        array(
2741
                            'private_key' => $hashedPrivateKey,
2742
                            'special' => 'none',
2743
                        ),
2744
                        'id = %i',
2745
                        $post_user_id
2746
                    );
2747
                    
2748
                    // Load superGlobals
2749
                    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
2750
                    $superGlobal = new protect\SuperGlobal\SuperGlobal();
2751
                    $superGlobal->put('private_key', $privateKey, 'SESSION', 'user');
2752
2753
                    return prepareExchangedData(
2754
                        array(
2755
                            'error' => false,
2756
                            'message' => langHdl('done'),
2757
                        ),
2758
                        'encode'
2759
                    );
2760
                }
2761
            }
2762
            
2763
            // ERROR
2764
            return prepareExchangedData(
2765
                array(
2766
                    'error' => true,
2767
                    'message' => langHdl('bad_password'),
2768
                ),
2769
                'encode'
2770
            );
2771
        }
2772
    }
2773
2774
    // ERROR
2775
    return prepareExchangedData(
2776
        array(
2777
            'error' => true,
2778
            'message' => langHdl('error_no_user'),
2779
        ),
2780
        'encode'
2781
    );
2782
}