Passed
Push — teampass_3.0 ( e9696d...a4fa10 )
by Nils
05:39
created

continueReEncryptingUserSharekeys()   C

Complexity

Conditions 14
Paths 11

Size

Total Lines 163
Code Lines 101

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 2 Features 0
Metric Value
cc 14
eloc 101
c 3
b 2
f 0
nc 11
nop 6
dl 0
loc 163
rs 5.0133

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-2022 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
// Define Timezone
45
date_default_timezone_set(isset($SETTINGS['timezone']) === true ? $SETTINGS['timezone'] : 'UTC');
46
47
// DO CHECKS
48
require_once $SETTINGS['cpassman_dir'] . '/includes/config/include.php';
49
require_once $SETTINGS['cpassman_dir'] . '/sources/checks.php';
50
$post_type = filter_input(INPUT_POST, 'type', FILTER_SANITIZE_STRING);
51
if (
52
    isset($post_type) === true
53
    && ($post_type === 'ga_generate_qr'
54
        //|| $post_type === 'recovery_send_pw_by_email'
55
        //|| $post_type === 'recovery_generate_new_password'
56
        || $post_type === 'get_teampass_settings')
57
) {
58
    // continue
59
    mainQuery($SETTINGS);
60
} elseif (
61
    isset($_SESSION['user_id']) === true
62
    && checkUser($_SESSION['user_id'], $_SESSION['key'], 'home', $SETTINGS) === false
63
) {
64
    $_SESSION['error']['code'] = ERR_NOT_ALLOWED; //not allowed page
65
    include $SETTINGS['cpassman_dir'] . '/error.php';
66
    exit();
67
} elseif ((isset($_SESSION['user_id']) === true
68
        && isset($_SESSION['key'])) === true
69
    || (isset($post_type) === true
70
        //&& $post_type === 'change_user_language'
71
        && null !== filter_input(INPUT_POST, 'data', FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES))
72
) {
73
    // continue
74
    mainQuery($SETTINGS);
75
} else {
76
    $_SESSION['error']['code'] = ERR_NOT_ALLOWED; //not allowed page
77
    include $SETTINGS['cpassman_dir'] . '/error.php';
78
    exit();
79
}
80
81
/**
82
 * Undocumented function.
83
 */
84
function mainQuery(array $SETTINGS)
85
{
86
    header('Content-type: text/html; charset=utf-8');
87
    header('Cache-Control: no-cache, must-revalidate');
88
    error_reporting(E_ERROR);
89
90
91
    // Includes
92
    include_once $SETTINGS['cpassman_dir'] . '/includes/language/' . $_SESSION['user_language'] . '.php';
93
    include_once $SETTINGS['cpassman_dir'] . '/includes/config/settings.php';
94
    include_once $SETTINGS['cpassman_dir'] . '/sources/main.functions.php';
95
    include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php';
96
97
    // Connect to mysql server
98
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
99
100
    DB::$host = DB_HOST;
101
    DB::$user = DB_USER;
102
    DB::$password = defined('DB_PASSWD_CLEAR') === false ? defuseReturnDecrypted(DB_PASSWD, $SETTINGS) : DB_PASSWD_CLEAR;
103
    DB::$dbName = DB_NAME;
104
    DB::$port = DB_PORT;
105
    DB::$encoding = DB_ENCODING;
106
107
    // User's language loading
108
    include_once $SETTINGS['cpassman_dir'] . '/includes/language/' . $_SESSION['user_language'] . '.php';
109
110
    // Prepare post variables
111
    $post_key = filter_input(INPUT_POST, 'key', FILTER_SANITIZE_STRING);
112
    $post_type = filter_input(INPUT_POST, 'type', FILTER_SANITIZE_STRING);
113
    $post_data = filter_input(INPUT_POST, 'data', FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES);
114
115
    // Check KEY
116
    if (isset($post_key) === false || empty($post_key) === true) {
117
        echo prepareExchangedData(
118
            array(
119
                'error' => true,
120
                'message' => langHdl('key_is_not_correct'),
121
            ),
122
            'encode'
123
        );
124
        return false;
125
    }
126
127
    // decrypt and retreive data in JSON format
128
    $dataReceived = prepareExchangedData(
129
        $post_data,
130
        'decode'
131
    );
132
    
133
    // Manage type of action asked
134
    switch ($post_type) {
135
        case 'change_pw':
136
            $return = changePassword(
137
                (string) filter_var($dataReceived['new_pw'], FILTER_SANITIZE_STRING),
138
                isset($dataReceived['current_pw']) === true ? (string) filter_var($dataReceived['current_pw'], FILTER_SANITIZE_STRING) : '',
139
                (int) filter_var($dataReceived['complexity'], FILTER_SANITIZE_NUMBER_INT),
140
                (string) filter_var($dataReceived['change_request'], FILTER_SANITIZE_STRING),
141
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
142
                $SETTINGS
143
            );
144
145
            echo $return;
146
            
147
            break;
148
149
        /*
150
         * This will generate the QR Google Authenticator
151
         */
152
        case 'ga_generate_qr':
153
            $return = generateQRCode(
154
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
155
                (string) filter_var($dataReceived['demand_origin'], FILTER_SANITIZE_STRING),
156
                (string) filter_var($dataReceived['send_email'], FILTER_SANITIZE_STRING),
157
                (string) filter_var($dataReceived['login'], FILTER_SANITIZE_STRING),
158
                (string) filter_var($dataReceived['pwd'], FILTER_SANITIZE_STRING),
159
                $SETTINGS
160
            );
161
162
            echo $return;
163
164
            break;
165
        
166
            /*
167
         * Increase the session time of User
168
         */
169
        case 'increase_session_time':
170
            $return = increaseSessionDuration(
171
                (int) filter_input(INPUT_POST, 'duration', FILTER_SANITIZE_NUMBER_INT)
172
            );
173
174
            echo $return;
175
176
            break;
177
        
178
            /*
179
         * Hide maintenance message
180
         */
181
        case 'hide_maintenance':
182
            $_SESSION['hide_maintenance'] = 1;
183
184
            break;
185
        
186
        /*
187
         * Send emails not sent
188
         */
189
        case 'send_waiting_emails':
190
            sendEmailsNotSent(
191
                $SETTINGS
192
            );
193
            
194
            break;
195
196
        /*
197
         * Generate a password generic
198
         */
199
        case 'generate_password':
200
            $return = generateGenericPassword(
201
                (int) filter_input(INPUT_POST, 'size', FILTER_SANITIZE_NUMBER_INT),
202
                (bool) filter_input(INPUT_POST, 'secure_pwd', FILTER_SANITIZE_STRING),
203
                (bool) filter_input(INPUT_POST, 'lowercase', FILTER_SANITIZE_STRING),
204
                (bool) filter_input(INPUT_POST, 'capitalize', FILTER_SANITIZE_STRING),
205
                (bool) filter_input(INPUT_POST, 'numerals', FILTER_SANITIZE_STRING),
206
                (bool) filter_input(INPUT_POST, 'symbols', FILTER_SANITIZE_NUMBER_INT),
207
                $SETTINGS
208
            );
209
210
            echo $return;
211
            
212
            break;
213
214
        /*
215
         * Refresh list of last items seen
216
         */
217
        case 'refresh_list_items_seen':
218
            $return = refreshUserItemsSeenList(
219
                $SETTINGS
220
            );
221
222
            echo $return;
223
224
            break;
225
226
        /*
227
         * Generates a KEY with CRYPT
228
         */
229
        case 'generate_new_key':
230
            // load passwordLib library
231
            $pwdlib = new SplClassLoader('PasswordLib', '../includes/libraries');
232
            $pwdlib->register();
233
            $pwdlib = new PasswordLib\PasswordLib();
234
            // generate key
235
            $key = $pwdlib->getRandomToken(filter_input(INPUT_POST, 'size', FILTER_SANITIZE_NUMBER_INT));
236
            echo '[{"key" : "' . htmlentities($key, ENT_QUOTES) . '"}]';
237
            break;
238
239
        /*
240
         * Generates a TOKEN with CRYPT
241
         */
242
        case 'save_token':
243
            $token = GenerateCryptKey(
244
                null !== filter_input(INPUT_POST, 'size', FILTER_SANITIZE_NUMBER_INT) ? (int) filter_input(INPUT_POST, 'size', FILTER_SANITIZE_NUMBER_INT) : 20,
245
                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,
246
                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,
247
                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,
248
                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,
249
                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,
250
                $SETTINGS
251
            );
252
            
253
            // store in DB
254
            DB::insert(
255
                prefixTable('tokens'),
256
                array(
257
                    'user_id' => (int) $_SESSION['user_id'],
258
                    'token' => $token,
259
                    'reason' => filter_input(INPUT_POST, 'reason', FILTER_SANITIZE_STRING),
260
                    'creation_timestamp' => time(),
261
                    'end_timestamp' => time() + filter_input(INPUT_POST, 'duration', FILTER_SANITIZE_NUMBER_INT), // in secs
262
                )
263
            );
264
265
            echo '[{"token" : "' . $token . '"}]';
266
            break;
267
268
269
        /*
270
         * TODO Check if suggestions are existing
271
         */
272
        /*
273
        case 'is_existings_suggestions':
274
            if ($_SESSION['user_manager'] === '1' || $_SESSION['is_admin'] === '1') {
275
                $count = 0;
276
                DB::query('SELECT * FROM ' . prefixTable('items_change'));
277
                $count += DB::count();
278
                DB::query('SELECT * FROM ' . prefixTable('suggestion'));
279
                $count += DB::count();
280
281
                echo '[ { "error" : "" , "count" : "' . $count . '" , "show_sug_in_menu" : "0"} ]';
282
                break;
283
            }
284
            
285
            if (isset($_SESSION['nb_item_change_proposals']) && $_SESSION['nb_item_change_proposals'] > 0) {
286
                echo '[ { "error" : "" , "count" : "' . $_SESSION['nb_item_change_proposals'] . '" , "show_sug_in_menu" : "1"} ]';
287
                break;
288
            }
289
            
290
            echo '[ { "error" : "" , "count" : "" , "show_sug_in_menu" : "0"} ]';
291
292
            break;
293
        */
294
295
        /*
296
         * Sending statistics
297
         */
298
        case 'sending_statistics':
299
            sendingStatistics(
300
                $SETTINGS
301
            );
302
303
            break;
304
305
        /*
306
         * delete a file
307
         */
308
        case 'file_deletion':
309
            fileDelete(filter_input(INPUT_POST, 'filename', FILTER_SANITIZE_STRING), $SETTINGS);
310
311
            break;
312
313
        /*
314
         * Generate BUG report
315
         */
316
        case 'generate_bug_report':
317
            $return = generateBugReport(
318
                (string) $post_data,
319
                $SETTINGS
320
            );
321
322
            echo $return;
323
        
324
            break;
325
326
        /*
327
        * get_teampass_settings
328
        */
329
        case 'get_teampass_settings':
330
            // Encrypt data to return
331
            echo prepareExchangedData($SETTINGS, 'encode');
332
333
            break;
334
335
336
        /*
337
        * test_current_user_password_is_correct
338
        */
339
        case 'test_current_user_password_is_correct':
340
            $return = isUserPasswordCorrect(
341
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
342
                (string) filter_var($dataReceived['password'], FILTER_SANITIZE_STRING),
343
                $SETTINGS
344
            );
345
346
            echo $return;
347
348
            break;
349
350
        /*
351
         * User's public/private keys change
352
         */
353
        case 'change_private_key_encryption_password':
354
            $return = changePrivateKeyEncryptionPassword(
355
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
356
                (string) filter_var($dataReceived['current_code'], FILTER_SANITIZE_STRING),
357
                (string) filter_var($dataReceived['new_code'], FILTER_SANITIZE_STRING),
358
                (string) filter_var($dataReceived['action_type'], FILTER_SANITIZE_STRING),
359
                $SETTINGS
360
            );
361
362
            echo $return;
363
        
364
            break;
365
366
        /*
367
         * User's password has to be initialized
368
         */
369
        case 'initialize_user_password':
370
            $return = initializeUserPassword(
371
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
372
                (string) filter_var($dataReceived['special'], FILTER_SANITIZE_STRING),
373
                (string) filter_var($dataReceived['password'], FILTER_SANITIZE_STRING),
374
                (bool) filter_var($dataReceived['self_change'], FILTER_SANITIZE_STRING),
375
                $SETTINGS
376
            );
377
378
            echo $return;
379
380
            break;
381
382
        /*
383
        * CASE
384
        * Send email
385
        */
386
        case 'mail_me':
387
            $return = sendMailToUser(
388
                filter_var($dataReceived['receipt'], FILTER_SANITIZE_STRING),
389
                $dataReceived['body'],
390
                (string) filter_var($dataReceived['subject'], FILTER_SANITIZE_STRING),
391
                (array) filter_var_array(
392
                    $dataReceived['pre_replace'],
393
                    FILTER_SANITIZE_STRING
394
                ),
395
                $SETTINGS
396
            );
397
398
            echo $return;
399
400
            break;
401
402
        /*
403
        * Generate a temporary encryption key for user
404
        */
405
        case 'generate_temporary_encryption_key':
406
            $return = generateOneTimeCode(
407
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
408
                $SETTINGS
409
            );
410
            
411
            echo $return;
412
413
            break;
414
415
        /*
416
        * user_sharekeys_reencryption_start
417
        */
418
        case 'user_sharekeys_reencryption_start':
419
            $return = startReEncryptingUserSharekeys(
420
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
421
                (bool) filter_var($dataReceived['self_change'], FILTER_SANITIZE_STRING),
422
                $SETTINGS
423
            );
424
            
425
            echo $return;
426
427
            break;
428
429
        /*
430
        * user_sharekeys_reencryption_next
431
        */
432
        case 'user_sharekeys_reencryption_next':
433
            $return = continueReEncryptingUserSharekeys(
434
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
435
                (bool) filter_var($dataReceived['self_change'], FILTER_SANITIZE_STRING),
436
                (string) filter_var($dataReceived['action'], FILTER_SANITIZE_STRING),
437
                (int) filter_var($dataReceived['start'], FILTER_SANITIZE_NUMBER_INT),
438
                (int) filter_var($dataReceived['length'], FILTER_SANITIZE_NUMBER_INT),
439
                $SETTINGS
440
            );
441
            
442
            echo $return;
443
444
            break;
445
446
        /*
447
        * user_psk_reencryption
448
        */
449
        case 'user_psk_reencryption':
450
            $return = migrateTo3_DoUserPersonalItemsEncryption(
451
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
452
                (int) filter_var($dataReceived['start'], FILTER_SANITIZE_NUMBER_INT),
453
                (int) filter_var($dataReceived['length'], FILTER_SANITIZE_NUMBER_INT),
454
                (string) filter_var($dataReceived['userPsk'], FILTER_SANITIZE_STRING),
455
                $SETTINGS
456
            );
457
            
458
            echo $return;
459
        
460
            break;
461
462
        /*
463
        * Get info 
464
        */
465
        case 'get_user_info':
466
            $return = getUserInfo(
467
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
468
                (string) filter_var($dataReceived['fields'], FILTER_SANITIZE_STRING),
469
                $SETTINGS
470
            );
471
            
472
            echo $return;
473
474
            break;
475
476
        /*
477
        * Change user's authenticataion password
478
        */
479
        case 'change_user_auth_password':
480
            $return = changeUserAuthenticationPassword(
481
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
482
                (string) filter_var($dataReceived['old_password'], FILTER_SANITIZE_STRING),
483
                (string) filter_var($dataReceived['new_password'], FILTER_SANITIZE_STRING),
484
                $SETTINGS
485
            );
486
            
487
            echo $return;
488
489
            break;
490
491
492
        /*
493
        * User's authenticataion password in LDAP has changed
494
        */
495
        case 'change_user_ldap_auth_password':
496
            $return = /** @scrutinizer ignore-call */ changeUserLDAPAuthenticationPassword(
497
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
498
                filter_var($dataReceived['previous_password'], FILTER_SANITIZE_STRING),
499
                filter_var($dataReceived['current_password'], FILTER_SANITIZE_STRING),
500
                $SETTINGS
501
            );
502
            
503
            echo $return;
504
505
            break;
506
507
508
        /*
509
        * How many items for this user
510
        */
511
        case 'get_number_of_items_to_treat':
512
            echo getNumberOfItemsToTreat(
513
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
514
                $SETTINGS
515
            );
516
517
            break;
518
    }
519
}
520
521
522
/**
523
 * Provides the number of items
524
 *
525
 * @param int   $userId     User ID
526
 * @param array $SETTINGS   TeampassSettings
527
 *
528
 * @return string
529
 */
530
function getNumberOfItemsToTreat(
531
    int $userId,
532
    array $SETTINGS
533
): string
534
{
535
    // get number of items
536
    DB::queryFirstRow(
537
        'SELECT increment_id
538
        FROM ' . prefixTable('sharekeys_items') .
539
        ' WHERE user_id = %i',
540
        $userId
541
    );
542
543
    // Send back
544
    return prepareExchangedData(
545
        array(
546
            'error' => false,
547
            'nbItems' => DB::count(),
548
        ),
549
        'encode'
550
    );
551
}
552
553
554
/**
555
 * 
556
 */
557
function changePassword(
558
    string $post_new_password,
559
    string $post_current_password,
560
    int $post_password_complexity,
561
    string $post_change_request,
562
    int $post_user_id,
563
    array $SETTINGS
564
): string
565
{
566
    // load passwordLib library
567
    $pwdlib = new SplClassLoader('PasswordLib', '../includes/libraries');
568
    $pwdlib->register();
569
    $pwdlib = new PasswordLib\PasswordLib();
570
571
    // Prepare variables
572
    $post_new_password_hashed = $pwdlib->createPasswordHash($post_new_password);
573
574
    // User has decided to change is PW
575
    if ($post_change_request === 'reset_user_password_expected'
576
        || $post_change_request === 'user_decides_to_change_password'
577
    ) {
578
        // Check that current user is correct
579
        if ((int) $post_user_id !== (int) $_SESSION['user_id']) {
580
            return prepareExchangedData(
581
                array(
582
                    'error' => true,
583
                    'message' => langHdl('error_not_allowed_to'),
584
                ),
585
                'encode'
586
            );
587
        }
588
589
        // check if expected security level is reached
590
        $dataUser = DB::queryfirstrow(
591
            'SELECT *
592
            FROM ' . prefixTable('users') . ' WHERE id = %i',
593
            $post_user_id
594
        );
595
596
        // check if badly written
597
        $dataUser['fonction_id'] = array_filter(
598
            explode(',', str_replace(';', ',', $dataUser['fonction_id']))
599
        );
600
        $dataUser['fonction_id'] = implode(',', $dataUser['fonction_id']);
601
        DB::update(
602
            prefixTable('users'),
603
            array(
604
                'fonction_id' => $dataUser['fonction_id'],
605
            ),
606
            'id = %i',
607
            $post_user_id
608
        );
609
610
        if (empty($dataUser['fonction_id']) === false) {
611
            $data = DB::queryFirstRow(
612
                'SELECT complexity
613
                FROM ' . prefixTable('roles_title') . '
614
                WHERE id IN (' . $dataUser['fonction_id'] . ')
615
                ORDER BY complexity DESC'
616
            );
617
        } else {
618
            // In case user has no roles yet
619
            $data = array();
620
            $data['complexity'] = 0;
621
        }
622
623
        if ((int) $post_password_complexity < (int) $data['complexity']) {
624
            return prepareExchangedData(
625
                array(
626
                    'error' => true,
627
                    'message' => '<div style="margin:10px 0 10px 15px;">' . langHdl('complexity_level_not_reached') . '.<br>' .
628
                        langHdl('expected_complexity_level') . ': <b>' . TP_PW_COMPLEXITY[$data['complexity']][1] . '</b></div>',
629
                ),
630
                'encode'
631
            );
632
        }
633
634
        // Check that the 2 passwords are differents
635
        if ($post_current_password === $post_new_password) {
636
            return prepareExchangedData(
637
                array(
638
                    'error' => true,
639
                    'message' => langHdl('password_already_used'),
640
                ),
641
                'encode'
642
            );
643
        }
644
645
        // update sessions
646
        $_SESSION['last_pw_change'] = mktime(0, 0, 0, (int) date('m'), (int) date('d'), (int) date('y'));
647
        $_SESSION['validite_pw'] = true;
648
649
        // BEfore updating, check that the pwd is correct
650
        if ($pwdlib->verifyPasswordHash($post_new_password, $post_new_password_hashed) === true && empty($dataUser['private_key']) === false) {
651
            $special_action = 'none';
652
            if ($post_change_request === 'reset_user_password_expected') {
653
                $_SESSION['user']['private_key'] = decryptPrivateKey($post_current_password, $dataUser['private_key']);
654
            }
655
656
            // update DB
657
            DB::update(
658
                prefixTable('users'),
659
                array(
660
                    'pw' => $post_new_password_hashed,
661
                    'last_pw_change' => mktime(0, 0, 0, (int) date('m'), (int) date('d'), (int) date('y')),
662
                    'last_pw' => $post_current_password,
663
                    'special' => $special_action,
664
                    'private_key' => encryptPrivateKey($post_new_password, $_SESSION['user']['private_key']),
665
                ),
666
                'id = %i',
667
                $post_user_id
668
            );
669
            // update LOG
670
            logEvents($SETTINGS, 'user_mngt', 'at_user_pwd_changed', (string) $_SESSION['user_id'], $_SESSION['login'], $post_user_id);
671
672
            // Send back
673
            return prepareExchangedData(
674
                array(
675
                    'error' => false,
676
                    'message' => '',
677
                ),
678
                'encode'
679
            );
680
        }
681
        // Send back
682
        return prepareExchangedData(
683
            array(
684
                'error' => true,
685
                'message' => langHdl('pwd_hash_not_correct'),
686
            ),
687
            'encode'
688
        );
689
    }
690
    return prepareExchangedData(
691
        array(
692
            'error' => true,
693
            'message' => langHdl('error_not_allowed_to'),
694
        ),
695
        'encode'
696
    );
697
}
698
699
700
701
function generateQRCode(
702
    $post_id,
703
    $post_demand_origin,
704
    $post_send_mail,
705
    $post_login,
706
    $post_pwd,
707
    array $SETTINGS
708
): string
709
{
710
    // is this allowed by setting
711
    if ((isset($SETTINGS['ga_reset_by_user']) === false || (int) $SETTINGS['ga_reset_by_user'] !== 1)
712
        && (null === $post_demand_origin || $post_demand_origin !== 'users_management_list')
713
    ) {
714
        // User cannot ask for a new code
715
        return prepareExchangedData(
716
            array(
717
                'error' => true,
718
                'message' => langHdl('error_not_allowed_to'),
719
            ),
720
            'encode'
721
        );
722
    }
723
724
    // Check if user exists
725
    if (null === $post_id || empty($post_id) === true) {
726
        // Get data about user
727
        $data = DB::queryfirstrow(
728
            'SELECT id, email, pw
729
            FROM ' . prefixTable('users') . '
730
            WHERE login = %s',
731
            $post_login
732
        );
733
    } else {
734
        $data = DB::queryfirstrow(
735
            'SELECT id, login, email, pw
736
            FROM ' . prefixTable('users') . '
737
            WHERE id = %i',
738
            $post_id
739
        );
740
        $post_login = $data['login'];
741
    }
742
    // Get number of returned users
743
    $counter = DB::count();
744
745
    // load passwordLib library
746
    $pwdlib = new SplClassLoader('PasswordLib', $SETTINGS['cpassman_dir'] . '/includes/libraries');
747
    $pwdlib->register();
748
    $pwdlib = new PasswordLib\PasswordLib();
749
750
    // Do treatment
751
    if ($counter === 0) {
752
        // Not a registered user !
753
        logEvents($SETTINGS, 'failed_auth', 'user_not_exists', '', stripslashes($post_login), stripslashes($post_login));
754
        return prepareExchangedData(
755
            array(
756
                'error' => true,
757
                'message' => langHdl('no_user'),
758
                'tst' => 1,
759
            ),
760
            'encode'
761
        );
762
    }
763
764
    if (
765
        isset($post_pwd) === true
766
        && isset($data['pw']) === true
767
        && $pwdlib->verifyPasswordHash($post_pwd, $data['pw']) === false
768
        && $post_demand_origin !== 'users_management_list'
769
    ) {
770
        // checked the given password
771
        logEvents($SETTINGS, 'failed_auth', 'password_is_not_correct', '', stripslashes($post_login), stripslashes($post_login));
772
        return prepareExchangedData(
773
            array(
774
                'error' => true,
775
                'message' => langHdl('no_user'),
776
                'tst' => $post_demand_origin,
777
            ),
778
            'encode'
779
        );
780
    }
781
    
782
    if (empty($data['email']) === true) {
783
        return prepareExchangedData(
784
            array(
785
                'error' => true,
786
                'message' => langHdl('no_email'),
787
            ),
788
            'encode'
789
        );
790
    }
791
    
792
    // generate new GA user code
793
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Authentication/TwoFactorAuth/TwoFactorAuth.php';
794
    $tfa = new Authentication\TwoFactorAuth\TwoFactorAuth($SETTINGS['ga_website_name']);
795
    $gaSecretKey = $tfa->createSecret();
796
    $gaTemporaryCode = GenerateCryptKey(12, false, true, true, false, true, $SETTINGS);
797
798
    DB::update(
799
        prefixTable('users'),
800
        [
801
            'ga' => $gaSecretKey,
802
            'ga_temporary_code' => $gaTemporaryCode,
803
        ],
804
        'id = %i',
805
        $data['id']
806
    );
807
808
    // Log event
809
    logEvents($SETTINGS, 'user_connection', 'at_2fa_google_code_send_by_email', (string) $data['id'], stripslashes($post_login), stripslashes($post_login));
810
811
    // send mail?
812
    if ((int) $post_send_mail === 1) {
813
        sendEmail(
814
            langHdl('email_ga_subject'),
815
            str_replace(
816
                '#2FACode#',
817
                $gaTemporaryCode,
818
                langHdl('email_ga_text')
819
            ),
820
            $data['email'],
821
            $SETTINGS
822
        );
823
824
        // send back
825
        return prepareExchangedData(
826
            array(
827
                'error' => false,
828
                'message' => $post_send_mail,
829
                'email' => $data['email'],
830
                'email_result' => str_replace(
831
                    '#email#',
832
                    '<b>' . obfuscateEmail($data['email']) . '</b>',
833
                    addslashes(langHdl('admin_email_result_ok'))
834
                ),
835
            ),
836
            'encode'
837
        );
838
    }
839
    
840
    // send back
841
    return prepareExchangedData(
842
        array(
843
            'error' => false,
844
            'message' => '',
845
            'email' => $data['email'],
846
            'email_result' => str_replace(
847
                '#email#',
848
                '<b>' . obfuscateEmail($data['email']) . '</b>',
849
                addslashes(langHdl('admin_email_result_ok'))
850
            ),
851
        ),
852
        'encode'
853
    );
854
}
855
856
function sendEmailsNotSent(
857
    array $SETTINGS
858
)
859
{
860
    if (isset($SETTINGS['enable_send_email_on_user_login'])
861
        && (int) $SETTINGS['enable_send_email_on_user_login'] === 1
862
    ) {
863
        $row = DB::queryFirstRow(
864
            'SELECT valeur FROM ' . prefixTable('misc') . ' WHERE type = %s AND intitule = %s',
865
            'cron',
866
            'sending_emails'
867
        );
868
869
        if ((int) (time() - $row['valeur']) >= 300 || (int) $row['valeur'] === 0) {
870
            $rows = DB::query(
871
                'SELECT *
872
                FROM ' . prefixTable('emails') .
873
                ' WHERE status != %s',
874
                'sent'
875
            );
876
            foreach ($rows as $record) {
877
                //echo $record['increment_id'] . " >> ";
878
                // Send email
879
                $ret = json_decode(
880
                    sendEmail(
881
                        $record['subject'],
882
                        $record['body'],
883
                        $record['receivers'],
884
                        $SETTINGS
885
                    ),
886
                    true
887
                );
888
889
                // update item_id in files table
890
                DB::update(
891
                    prefixTable('emails'),
892
                    array(
893
                        'status' => $ret['error'] === 'error_mail_not_send' ? 'not_sent' : 'sent',
894
                    ),
895
                    'timestamp = %s',
896
                    $record['timestamp']
897
                );
898
            }
899
        }
900
        // update cron time
901
        DB::update(
902
            prefixTable('misc'),
903
            array(
904
                'valeur' => time(),
905
            ),
906
            'intitule = %s AND type = %s',
907
            'sending_emails',
908
            'cron'
909
        );
910
    }
911
}
912
913
function generateGenericPassword(
914
    int $size,
915
    bool $secure,
916
    bool $lowercase,
917
    bool $capitalize,
918
    bool $numerals,
919
    bool $symbols,
920
    array $SETTINGS
921
): string
922
{
923
    if ((int) $size > (int) $SETTINGS['pwd_maximum_length']) {
924
        return prepareExchangedData(
925
            array(
926
                'error_msg' => 'Password length is too long! ',
927
                'error' => 'true',
928
            ),
929
            'encode'
930
        );
931
    }
932
933
    $generator = new SplClassLoader('PasswordGenerator\Generator', '../includes/libraries');
934
    $generator->register();
935
    $generator = new PasswordGenerator\Generator\ComputerPasswordGenerator();
936
937
    // Is PHP7 being used?
938
    if (version_compare(PHP_VERSION, '7.0.0', '>=')) {
939
        $php7generator = new SplClassLoader('PasswordGenerator\RandomGenerator', '../includes/libraries');
940
        $php7generator->register();
941
        $generator->setRandomGenerator(new PasswordGenerator\RandomGenerator\Php7RandomGenerator());
942
    }
943
944
    // Manage size
945
    $generator->setLength(($size <= 0) ? 10 : $size);
946
947
    if ($secure === true) {
948
        $generator->setSymbols(true);
949
        $generator->setLowercase(true);
950
        $generator->setUppercase(true);
951
        $generator->setNumbers(true);
952
    } else {
953
        $generator->setLowercase($lowercase);
954
        $generator->setUppercase($capitalize);
955
        $generator->setNumbers($numerals);
956
        $generator->setSymbols($symbols);
957
    }
958
959
    return prepareExchangedData(
960
        array(
961
            'key' => $generator->generatePasswords(),
962
            'error' => '',
963
        ),
964
        'encode'
965
    );
966
}
967
968
function refreshUserItemsSeenList(
969
    array $SETTINGS
970
): string
971
{
972
    // get list of last items seen
973
    $arr_html = array();
974
    $rows = DB::query(
975
        '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
976
        FROM ' . prefixTable('log_items') . ' AS l
977
        RIGHT JOIN ' . prefixTable('items') . ' AS i ON (l.id_item = i.id)
978
        WHERE l.action = %s AND l.id_user = %i
979
        ORDER BY l.date DESC
980
        LIMIT 0, 100',
981
        'at_shown',
982
        $_SESSION['user_id']
983
    );
984
    if (DB::count() > 0) {
985
        foreach ($rows as $record) {
986
            if (in_array($record['id']->id, array_column($arr_html, 'id')) === false) {
987
                array_push(
988
                    $arr_html,
989
                    array(
990
                        'id' => $record['id'],
991
                        'label' => htmlspecialchars(stripslashes(htmlspecialchars_decode($record['label'], ENT_QUOTES)), ENT_QUOTES),
992
                        'tree_id' => $record['id_tree'],
993
                        'perso' => $record['perso'],
994
                        'restricted' => $record['restricted'],
995
                    )
996
                );
997
                if (count($arr_html) >= (int) $SETTINGS['max_latest_items']) {
998
                    break;
999
                }
1000
            }
1001
        }
1002
    }
1003
1004
    // get wainting suggestions
1005
    $nb_suggestions_waiting = 0;
1006
    if (
1007
        isset($SETTINGS['enable_suggestion']) === true && (int) $SETTINGS['enable_suggestion'] === 1
1008
        && ((int) $_SESSION['user_admin'] === 1 || (int) $_SESSION['user_manager'] === 1)
1009
    ) {
1010
        DB::query('SELECT * FROM ' . prefixTable('suggestion'));
1011
        $nb_suggestions_waiting = DB::count();
1012
    }
1013
1014
    return json_encode(
1015
        array(
1016
            'error' => '',
1017
            'existing_suggestions' => $nb_suggestions_waiting,
1018
            'html_json' => $arr_html,
1019
        ),
1020
        JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
1021
    );
1022
}
1023
1024
function sendingStatistics(
1025
    array $SETTINGS
1026
): void
1027
{
1028
    if (
1029
        isset($SETTINGS['send_statistics_items']) === true
1030
        && isset($SETTINGS['send_stats']) === true
1031
        && isset($SETTINGS['send_stats_time']) === true
1032
        && (int) $SETTINGS['send_stats'] === 1
1033
        && (int) ($SETTINGS['send_stats_time'] + TP_ONE_DAY_SECONDS) > time()
1034
    ) {
1035
        // get statistics data
1036
        $stats_data = getStatisticsData($SETTINGS);
1037
1038
        // get statistics items to share
1039
        $statsToSend = [];
1040
        $statsToSend['ip'] = $_SERVER['SERVER_ADDR'];
1041
        $statsToSend['timestamp'] = time();
1042
        foreach (array_filter(explode(';', $SETTINGS['send_statistics_items'])) as $data) {
1043
            if ($data === 'stat_languages') {
1044
                $tmp = '';
1045
                foreach ($stats_data[$data] as $key => $value) {
1046
                    $tmp .= $tmp === '' ? $key . '-' . $value : ',' . $key . '-' . $value;
1047
                }
1048
                $statsToSend[$data] = $tmp;
1049
            } elseif ($data === 'stat_country') {
1050
                $tmp = '';
1051
                foreach ($stats_data[$data] as $key => $value) {
1052
                    $tmp .= $tmp === '' ? $key . '-' . $value : ',' . $key . '-' . $value;
1053
                }
1054
                $statsToSend[$data] = $tmp;
1055
            } else {
1056
                $statsToSend[$data] = $stats_data[$data];
1057
            }
1058
        }
1059
1060
        // connect to Teampass Statistics database
1061
        $link2 = new MeekroDB(
1062
            'teampass.pw',
1063
            'teampass_user',
1064
            'ZMlEfRzKzFLZNzie',
1065
            'teampass_followup',
1066
            '3306',
1067
            'utf8'
1068
        );
1069
1070
        $link2->insert(
1071
            'statistics',
1072
            $statsToSend
1073
        );
1074
1075
        // update table misc with current timestamp
1076
        DB::update(
1077
            prefixTable('misc'),
1078
            array(
1079
                'valeur' => time(),
1080
            ),
1081
            'type = %s AND intitule = %s',
1082
            'admin',
1083
            'send_stats_time'
1084
        );
1085
1086
        //permits to test only once by session
1087
        $_SESSION['temporary']['send_stats_done'] = true;
1088
        $SETTINGS['send_stats_time'] = time();
1089
1090
        // save change in config file
1091
        handleConfigFile('update', $SETTINGS, 'send_stats_time', $SETTINGS['send_stats_time']);
1092
    }
1093
}
1094
1095
function generateBugReport(
1096
    string $data,
1097
    array $SETTINGS
1098
): string
1099
{
1100
    $config_exclude_vars = array(
1101
        'bck_script_passkey',
1102
        'email_smtp_server',
1103
        'email_auth_username',
1104
        'email_auth_pwd',
1105
        'email_from',
1106
    );
1107
1108
    // Get data
1109
    $post_data = json_decode($data, true);
1110
1111
    // Read config file
1112
    $list_of_options = '';
1113
    $url_found = '';
1114
    $anonym_url = '';
1115
    $tp_config_file = '../includes/config/tp.config.php';
1116
    $data = file($tp_config_file);
1117
    foreach ($data as $line) {
1118
        if (substr($line, 0, 4) === '    ') {
1119
            // Remove extra spaces
1120
            $line = str_replace('    ', '', $line);
1121
1122
            // Identify url to anonymize it
1123
            if (strpos($line, 'cpassman_url') > 0 && empty($url_found) === true) {
1124
                $url_found = substr($line, 19, strlen($line) - 22);
1125
                $tmp = parse_url($url_found);
1126
                $anonym_url = $tmp['scheme'] . '://<anonym_url>' . $tmp['path'];
1127
                $line = "'cpassman_url' => '" . $anonym_url . "\n";
1128
            }
1129
1130
            // Anonymize all urls
1131
            if (empty($anonym_url) === false) {
1132
                $line = str_replace($url_found, $anonym_url, $line);
1133
            }
1134
1135
            // Clear some vars
1136
            foreach ($config_exclude_vars as $var) {
1137
                if (strpos($line, $var) > 0) {
1138
                    $line = "'".$var."' => '<removed>'\n";
1139
                }
1140
            }
1141
1142
            // Complete line to display
1143
            $list_of_options .= $line;
1144
        }
1145
    }
1146
1147
    // Get error
1148
    $err = error_get_last();
1149
1150
    // Get 10 latest errors in Teampass
1151
    $teampass_errors = '';
1152
    $rows = DB::query(
1153
        'SELECT label, date AS error_date
1154
        FROM ' . prefixTable('log_system') . "
1155
        WHERE `type` LIKE 'error'
1156
        ORDER BY `date` DESC
1157
        LIMIT 0, 10"
1158
    );
1159
    if (DB::count() > 0) {
1160
        foreach ($rows as $record) {
1161
            if (empty($teampass_errors) === true) {
1162
                $teampass_errors = ' * ' . date($SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'], (int) $record['error_date']) . ' - ' . $record['label'];
1163
            } else {
1164
                $teampass_errors .= ' * ' . date($SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'], (int) $record['error_date']) . ' - ' . $record['label'];
1165
            }
1166
        }
1167
    }
1168
1169
    $link = mysqli_connect(DB_HOST, DB_USER, DB_PASSWD_CLEAR, DB_NAME, (int) DB_PORT, null);
1170
1171
    // Now prepare text
1172
    $txt = '### Page on which it happened
1173
' . $post_data['current_page'] . '
1174
1175
### Steps to reproduce
1176
1.
1177
2.
1178
3.
1179
1180
### Expected behaviour
1181
Tell us what should happen
1182
1183
1184
### Actual behaviour
1185
Tell us what happens instead
1186
1187
### Server configuration
1188
**Operating system**: ' . php_uname() . '
1189
1190
**Web server:** ' . $_SERVER['SERVER_SOFTWARE'] . '
1191
1192
**Database:** ' . ($link === false ? langHdl('undefined') : mysqli_get_server_info($link)) . '
1193
1194
**PHP version:** ' . PHP_VERSION . '
1195
1196
**Teampass version:** ' . TP_VERSION_FULL . '
1197
1198
**Teampass configuration file:**
1199
```
1200
' . $list_of_options . '
1201
```
1202
1203
**Updated from an older Teampass or fresh install:**
1204
1205
### Client configuration
1206
1207
**Browser:** ' . $post_data['browser_name'] . ' - ' . $post_data['browser_version'] . '
1208
1209
**Operating system:** ' . $post_data['os'] . ' - ' . $post_data['os_archi'] . 'bits
1210
1211
### Logs
1212
1213
#### Web server error log
1214
```
1215
' . $err['message'] . ' - ' . $err['file'] . ' (' . $err['line'] . ')
1216
```
1217
1218
#### Teampass 10 last system errors
1219
```
1220
' . $teampass_errors . '
1221
```
1222
1223
#### Log from the web-browser developer console (CTRL + SHIFT + i)
1224
```
1225
Insert the log here and especially the answer of the query that failed.
1226
```
1227
';
1228
1229
    return prepareExchangedData(
1230
        array(
1231
            'html' => $txt,
1232
            'error' => '',
1233
        ),
1234
        'encode'
1235
    );
1236
}
1237
1238
function isUserPasswordCorrect(
1239
    int $post_user_id,
1240
    string $post_user_password,
1241
    array $SETTINGS
1242
): string
1243
{
1244
    if (is_null($post_user_id) === false && isset($post_user_id) === true && empty($post_user_id) === false) {
1245
        // Check if user exists
1246
        $userInfo = DB::queryFirstRow(
1247
            'SELECT public_key, private_key, pw, auth_type
1248
            FROM ' . prefixTable('users') . '
1249
            WHERE id = %i',
1250
            $post_user_id
1251
        );
1252
        if (DB::count() > 0 && empty($userInfo['private_key']) === false) {
1253
            // Get one item
1254
            $record = DB::queryFirstRow(
1255
                'SELECT object_id
1256
                FROM ' . prefixTable('sharekeys_items') . '
1257
                WHERE user_id = %i',
1258
                $post_user_id
1259
            );
1260
1261
            // Get itemKey from current user
1262
            $currentUserKey = DB::queryFirstRow(
1263
                'SELECT share_key, increment_id
1264
                FROM ' . prefixTable('sharekeys_items') . '
1265
                WHERE object_id = %i AND user_id = %i',
1266
                $record['object_id'],
1267
                $post_user_id
1268
            );
1269
1270
            if ($currentUserKey !== null) {
1271
                // Decrypt itemkey with user key
1272
                // use old password to decrypt private_key
1273
                $_SESSION['user']['private_key'] = decryptPrivateKey($post_user_password, $userInfo['private_key']);
1274
                $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $_SESSION['user']['private_key']);
1275
1276
                if (empty(base64_decode($itemKey)) === false) {
1277
                    // GOOD password
1278
                    return prepareExchangedData(
1279
                        array(
1280
                            'error' => false,
1281
                            'message' => '',
1282
                            'debug' => '',
1283
                        ),
1284
                        'encode'
1285
                    );
1286
                }
1287
            }
1288
            
1289
            // Use the password check
1290
            // load passwordLib library
1291
            $pwdlib = new SplClassLoader('PasswordLib', $SETTINGS['cpassman_dir'] . '/includes/libraries');
1292
            $pwdlib->register();
1293
            $pwdlib = new PasswordLib\PasswordLib();
1294
            
1295
            if ($pwdlib->verifyPasswordHash(htmlspecialchars_decode($post_user_password), $userInfo['pw']) === true) {
1296
                // GOOD password
1297
                return prepareExchangedData(
1298
                    array(
1299
                        'error' => false,
1300
                        'message' => '',
1301
                        'debug' => '',
1302
                    ),
1303
                    'encode'
1304
                );
1305
            }
1306
        }
1307
    }
1308
1309
    return prepareExchangedData(
1310
        array(
1311
            'error' => true,
1312
            'message' => langHdl('password_is_not_correct'),
1313
            'debug' => isset($itemKey) === true ? base64_decode($itemKey) : '',
1314
        ),
1315
        'encode'
1316
    );
1317
}
1318
1319
function changePrivateKeyEncryptionPassword(
1320
    int $post_user_id,
1321
    string $post_current_code,
1322
    string $post_new_code,
1323
    string $post_action_type,
1324
    array $SETTINGS
1325
): string
1326
{
1327
    if (empty($post_new_code) === true) {
1328
        if (empty($_SESSION['user_pwd']) === false) {
1329
            $post_new_code = $_SESSION['user_pwd'];
1330
        } else {
1331
            // no user password???
1332
            return prepareExchangedData(
1333
                array(
1334
                    'error' => true,
1335
                    'message' => langHdl('error_no_user_password_exists'),
1336
                    'debug' => '',
1337
                ),
1338
                'encode'
1339
            );
1340
        }
1341
    }
1342
1343
    if (is_null($post_user_id) === false && isset($post_user_id) === true && empty($post_user_id) === false) {
1344
        // Get user info
1345
        $userData = DB::queryFirstRow(
1346
            'SELECT private_key
1347
            FROM ' . prefixTable('users') . '
1348
            WHERE id = %i',
1349
            $post_user_id
1350
        );
1351
        if (DB::count() > 0 && empty($userData['private_key']) === false) {
1352
            if ($post_action_type === 'encrypt_privkey_with_user_password') {
1353
                // Here the user has his private key encrypted with an OTC.
1354
                // We need to encrypt it with his real password
1355
                $privateKey = decryptPrivateKey($post_new_code, $userData['private_key']);
1356
                $hashedPrivateKey = encryptPrivateKey($post_current_code, $privateKey);
1357
            } else {
1358
                $privateKey = decryptPrivateKey($post_current_code, $userData['private_key']);
1359
                $hashedPrivateKey = encryptPrivateKey($post_new_code, $privateKey);
1360
            }
1361
1362
            // Update user account
1363
            DB::update(
1364
                prefixTable('users'),
1365
                array(
1366
                    'private_key' => $hashedPrivateKey,
1367
                    'special' => 'none',
1368
                ),
1369
                'id = %i',
1370
                $post_user_id
1371
            );
1372
1373
            // Load superGlobals
1374
            include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
1375
            $superGlobal = new protect\SuperGlobal\SuperGlobal();
1376
1377
            $superGlobal->put('private_key', $privateKey, 'SESSION', 'user');
1378
        }
1379
1380
        // Return
1381
        return prepareExchangedData(
1382
            array(
1383
                'error' => false,
1384
                'message' => '',
1385
            ),
1386
            'encode'
1387
        );
1388
    }
1389
    
1390
    return prepareExchangedData(
1391
        array(
1392
            'error' => true,
1393
            'message' => langHdl('error_no_user'),
1394
            'debug' => '',
1395
        ),
1396
        'encode'
1397
    );
1398
}
1399
1400
function initializeUserPassword(
1401
    int $post_user_id,
1402
    string $post_special,
1403
    string $post_user_password,
1404
    bool $post_self_change,
1405
    array $SETTINGS
1406
): string
1407
{
1408
    if (is_null($post_user_id) === false && isset($post_user_id) === true && empty($post_user_id) === false) {
1409
        // Get user info
1410
        $userData = DB::queryFirstRow(
1411
            'SELECT email, auth_type, login
1412
            FROM ' . prefixTable('users') . '
1413
            WHERE id = %i',
1414
            $post_user_id
1415
        );
1416
        if (DB::count() > 0 && empty($userData['email']) === false) {
1417
            // If user pwd is empty then generate a new one and send it to user
1418
            if (isset($post_user_password) === false || empty($post_user_password) === true) {
1419
                // Generate new password
1420
                $post_user_password = generateQuickPassword();
1421
            }
1422
1423
            // If LDAP enabled, then
1424
            // check that this password is correct
1425
            $continue = true;
1426
            if ($userData['auth_type'] === 'ldap' && (int) $SETTINGS['ldap_mode'] === 1) {
1427
                $continue = ldapCheckUserPassword(
1428
                    $userData['login'],
1429
                    $post_user_password,
1430
                    $SETTINGS
1431
                );
1432
            }
1433
1434
            if ($continue === true) {
1435
                // Only change if email is successfull
1436
                // GEnerate new keys
1437
                $userKeys = generateUserKeys($post_user_password);
1438
1439
                // load passwordLib library
1440
                $pwdlib = new SplClassLoader('PasswordLib', '../includes/libraries');
1441
                $pwdlib->register();
1442
                $pwdlib = new PasswordLib\PasswordLib();
1443
1444
                // Update user account
1445
                DB::update(
1446
                    prefixTable('users'),
1447
                    array(
1448
                        'special' => $post_special,
1449
                        'pw' => $pwdlib->createPasswordHash($post_user_password),
1450
                        'public_key' => $userKeys['public_key'],
1451
                        'private_key' => $userKeys['private_key'],
1452
                        'last_pw_change' => time(),
1453
                    ),
1454
                    'id = %i',
1455
                    $post_user_id
1456
                );
1457
1458
                // Return
1459
                return prepareExchangedData(
1460
                    array(
1461
                        'error' => false,
1462
                        'message' => '',
1463
                        'user_pwd' => $post_user_password,
1464
                        'user_email' => $userData['email'],
1465
                    ),
1466
                    'encode'
1467
                );
1468
            }
1469
            // Return error
1470
            return prepareExchangedData(
1471
                array(
1472
                    'error' => true,
1473
                    'message' => langHdl('no_email_set'),
1474
                    'debug' => '',
1475
                    'self_change' => $post_self_change,
1476
                ),
1477
                'encode'
1478
            );
1479
        }
1480
1481
        // Error
1482
        return prepareExchangedData(
1483
            array(
1484
                'error' => true,
1485
                'message' => langHdl('no_email_set'),
1486
                'debug' => '',
1487
            ),
1488
            'encode'
1489
        );
1490
    }
1491
    
1492
    return prepareExchangedData(
1493
        array(
1494
            'error' => true,
1495
            'message' => langHdl('error_no_user'),
1496
            'debug' => '',
1497
        ),
1498
        'encode'
1499
    );
1500
}
1501
1502
function sendMailToUser(
1503
    string $post_receipt,
1504
    string $post_body,
1505
    string $post_subject,
1506
    array $post_replace,
1507
    array $SETTINGS
1508
): string
1509
{
1510
    if (count($post_replace) > 0 && is_null($post_replace) === false) {
1511
        $post_body = str_replace(
1512
            array_keys($post_replace),
1513
            array_values($post_replace),
1514
            $post_body
1515
        );
1516
    }
1517
    
1518
    $ret = sendEmail(
1519
        $post_subject,
1520
        $post_body,
1521
        $post_receipt,
1522
        $SETTINGS,
1523
        '',
1524
        false
1525
    );
1526
1527
    $ret = json_decode($ret, true);
1528
1529
    return prepareExchangedData(
1530
        array(
1531
            'error' => empty($ret['error']) === true ? false : true,
1532
            'message' => $ret['message'],
1533
        ),
1534
        'encode'
1535
    );
1536
}
1537
1538
function generateOneTimeCode(
1539
    int $post_user_id,
1540
    array $SETTINGS
1541
): string
1542
{
1543
    if (is_null($post_user_id) === false && isset($post_user_id) === true && empty($post_user_id) === false) {
1544
        // Get user info
1545
        $userData = DB::queryFirstRow(
1546
            'SELECT email, auth_type, login
1547
            FROM ' . prefixTable('users') . '
1548
            WHERE id = %i',
1549
            $post_user_id
1550
        );
1551
        if (DB::count() > 0 && empty($userData['email']) === false) {
1552
            // Generate pwd
1553
            $password = generateQuickPassword();
1554
1555
            // GEnerate new keys
1556
            $userKeys = generateUserKeys($password);
1557
1558
            // Save in DB
1559
            DB::update(
1560
                prefixTable('users'),
1561
                array(
1562
                    'public_key' => $userKeys['public_key'],
1563
                    'private_key' => $userKeys['private_key'],
1564
                    'special' => 'generate-keys',
1565
                ),
1566
                'id=%i',
1567
                $post_user_id
1568
            );
1569
1570
            return prepareExchangedData(
1571
                array(
1572
                    'error' => false,
1573
                    'message' => '',
1574
                    'userTemporaryCode' => $password,
1575
                ),
1576
                'encode'
1577
            );
1578
        }
1579
        
1580
        return prepareExchangedData(
1581
            array(
1582
                'error' => true,
1583
                'message' => langHdl('no_email_set'),
1584
            ),
1585
            'encode'
1586
        );
1587
    }
1588
        
1589
    return prepareExchangedData(
1590
        array(
1591
            'error' => true,
1592
            'message' => langHdl('error_no_user'),
1593
        ),
1594
        'encode'
1595
    );
1596
}
1597
1598
function startReEncryptingUserSharekeys(
1599
    int $post_user_id,
1600
    bool $post_self_change,
1601
    array $SETTINGS
1602
): string
1603
{
1604
    //$post_user_id = is_null($post_user_id) === true ? $_SESSION['user_id'] : $post_user_id;
1605
1606
    if (is_null($post_user_id) === false && isset($post_user_id) === true && empty($post_user_id) === false) {
1607
        // Check if user exists
1608
        DB::queryFirstRow(
1609
            'SELECT *
1610
            FROM ' . prefixTable('users') . '
1611
            WHERE id = %i',
1612
            $post_user_id
1613
        );
1614
        if (DB::count() > 0) {
1615
            // Include libraries
1616
            include_once $SETTINGS['cpassman_dir'] . '/sources/aes.functions.php';
1617
1618
            // CLear old sharekeys
1619
            if ($post_self_change === false) {
1620
                deleteUserObjetsKeys($post_user_id, $SETTINGS);
1621
            }
1622
1623
            // Continu with next step
1624
            return prepareExchangedData(
1625
                array(
1626
                    'error' => false,
1627
                    'message' => '',
1628
                    'step' => 'step1',
1629
                    'userId' => $post_user_id,
1630
                    'start' => 0,
1631
                    'self_change' => $post_self_change,
1632
                ),
1633
                'encode'
1634
            );
1635
        }
1636
        // Nothing to do
1637
        return prepareExchangedData(
1638
            array(
1639
                'error' => true,
1640
                'message' => langHdl('error_no_user'),
1641
            ),
1642
            'encode'
1643
        );
1644
    }
1645
1646
    return prepareExchangedData(
1647
        array(
1648
            'error' => true,
1649
            'message' => langHdl('error_no_user'),
1650
        ),
1651
        'encode'
1652
    );
1653
}
1654
1655
function continueReEncryptingUserSharekeys(
1656
    int $post_user_id,
1657
    bool $post_self_change,
1658
    string $post_action,
1659
    int $post_start,
1660
    int $post_length,
1661
    array $SETTINGS
1662
): string
1663
{
1664
    if (is_null($post_user_id) === false && isset($post_user_id) === true && empty($post_user_id) === false) {
1665
        // Check if user exists
1666
        $userInfo = DB::queryFirstRow(
1667
            'SELECT public_key
1668
            FROM ' . prefixTable('users') . '
1669
            WHERE id = %i',
1670
            $post_user_id
1671
        );
1672
        if (isset($userInfo['public_key']) === true) {
1673
            // Include libraries
1674
            include_once $SETTINGS['cpassman_dir'] . '/sources/aes.functions.php';
1675
1676
            // WHAT STEP TO PERFORM?
1677
            if ($post_action === 'step0') {
1678
                // CLear old sharekeys
1679
                if ($post_self_change === false) {
1680
                    deleteUserObjetsKeys($post_user_id, $SETTINGS);
1681
                }
1682
1683
                $next_action = 'step1';
1684
            }
1685
            
1686
            // STEP 1 - ITEMS
1687
            elseif ($post_action === 'step1') {
1688
                $return = continueReEncryptingUserSharekeysStep1(
1689
                    $post_user_id,
1690
                    $post_self_change,
1691
                    $post_action,
1692
                    $post_start,
1693
                    $post_length,
1694
                    $userInfo['public_key'],
1695
                    $SETTINGS
1696
                );
1697
1698
                $next_start = $return['next_start'];
1699
                $next_action = $return['post_action'];
1700
            }
1701
1702
            // STEP 2 - LOGS
1703
            elseif ($post_action === 'step2') {
1704
                $return = continueReEncryptingUserSharekeysStep2(
1705
                    $post_user_id,
1706
                    $post_self_change,
1707
                    $post_action,
1708
                    $post_start,
1709
                    $post_length,
1710
                    $userInfo['public_key'],
1711
                    $SETTINGS
1712
                );
1713
1714
                $next_start = $return['next_start'];
1715
                $next_action = $return['post_action'];
1716
            }
1717
1718
            // STEP 3 - FIELDS
1719
            elseif ($post_action === 'step3') {
1720
                $return = continueReEncryptingUserSharekeysStep3(
1721
                    $post_user_id,
1722
                    $post_self_change,
1723
                    $post_action,
1724
                    $post_start,
1725
                    $post_length,
1726
                    $userInfo['public_key'],
1727
                    $SETTINGS
1728
                );
1729
1730
                $next_start = $return['next_start'];
1731
                $next_action = $return['post_action'];
1732
            }
1733
            
1734
            // STEP 4 - SUGGESTIONS
1735
            elseif ($post_action === 'step4') {
1736
                $return = continueReEncryptingUserSharekeysStep4(
1737
                    $post_user_id,
1738
                    $post_self_change,
1739
                    $post_action,
1740
                    $post_start,
1741
                    $post_length,
1742
                    $userInfo['public_key'],
1743
                    $SETTINGS
1744
                );
1745
1746
                $next_start = $return['next_start'];
1747
                $next_action = $return['post_action'];
1748
            }
1749
            
1750
            // STEP 5 - FILES
1751
            elseif ($post_action === 'step5') {
1752
                $return = continueReEncryptingUserSharekeysStep5(
1753
                    $post_user_id,
1754
                    $post_self_change,
1755
                    $post_action,
1756
                    $post_start,
1757
                    $post_length,
1758
                    $userInfo['public_key'],
1759
                    $SETTINGS
1760
                );
1761
1762
                $next_start = $return['next_start'];
1763
                $next_action = $return['post_action'];
1764
            }
1765
            
1766
            // STEP 6 - PERSONAL ITEMS
1767
            elseif ($post_action === 'step6') {
1768
                $return = continueReEncryptingUserSharekeysStep6(
1769
                    $post_user_id,
1770
                    $post_self_change,
1771
                    $post_action,
1772
                    $post_start,
1773
                    $post_length,
1774
                    $userInfo['public_key'],
1775
                    $SETTINGS
1776
                );
1777
1778
                $next_start = $return['next_start'];
1779
                $next_action = $return['post_action'];
1780
            }
1781
            
1782
            // Continu with next step
1783
            return prepareExchangedData(
1784
                array(
1785
                    'error' => false,
1786
                    'message' => '',
1787
                    'step' => $next_action,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $next_action does not seem to be defined for all execution paths leading up to this point.
Loading history...
1788
                    'start' => isset($next_start) === true ? $next_start : 0,
1789
                    'userId' => $post_user_id,
1790
                    'self_change' => $post_self_change,
1791
                ),
1792
                'encode'
1793
            );
1794
        }
1795
        
1796
        // Nothing to do
1797
        return prepareExchangedData(
1798
            array(
1799
                'error' => false,
1800
                'message' => '',
1801
                'step' => 'finished',
1802
                'start' => 0,
1803
                'userId' => $post_user_id,
1804
                'self_change' => $post_self_change,
1805
            ),
1806
            'encode'
1807
        );
1808
    }
1809
    
1810
    // Nothing to do
1811
    return prepareExchangedData(
1812
        array(
1813
            'error' => true,
1814
            'message' => langHdl('error_no_user'),
1815
            'extra' => $post_user_id,
1816
        ),
1817
        'encode'
1818
    );
1819
}
1820
1821
function continueReEncryptingUserSharekeysStep1(
1822
    int $post_user_id,
1823
    bool $post_self_change,
1824
    string $post_action,
1825
    int $post_start,
1826
    int $post_length,
1827
    string $user_public_key,
1828
    array $SETTINGS
1829
): array 
1830
{
1831
    // Loop on items
1832
    $rows = DB::query(
1833
        'SELECT id, pw
1834
        FROM ' . prefixTable('items') . '
1835
        WHERE perso = 0
1836
        LIMIT ' . $post_start . ', ' . $post_length
1837
    );
1838
    foreach ($rows as $record) {
1839
        // Get itemKey from current user
1840
        $currentUserKey = DB::queryFirstRow(
1841
            'SELECT share_key, increment_id
1842
            FROM ' . prefixTable('sharekeys_items') . '
1843
            WHERE object_id = %i AND user_id = %i',
1844
            $record['id'],
1845
            $_SESSION['user_id']
1846
        );
1847
        if ($currentUserKey === null || count($currentUserKey) === 0) continue;
1848
1849
        // Decrypt itemkey with admin key
1850
        $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $_SESSION['user']['private_key']);
1851
        
1852
        // Encrypt Item key
1853
        $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
1854
        
1855
        // Save the key in DB
1856
        if ($post_self_change === false) {
1857
            DB::insert(
1858
                prefixTable('sharekeys_items'),
1859
                array(
1860
                    'object_id' => (int) $record['id'],
1861
                    'user_id' => (int) $post_user_id,
1862
                    'share_key' => $share_key_for_item,
1863
                )
1864
            );
1865
        } else {
1866
            // Get itemIncrement from selected user
1867
            if ((int) $post_user_id !== (int) $_SESSION['user_id']) {
1868
                $currentUserKey = DB::queryFirstRow(
1869
                    'SELECT increment_id
1870
                    FROM ' . prefixTable('sharekeys_items') . '
1871
                    WHERE object_id = %i AND user_id = %i',
1872
                    $record['id'],
1873
                    $post_user_id
1874
                );
1875
1876
                if (DB::count() > 0) {
1877
                    // NOw update
1878
                    DB::update(
1879
                        prefixTable('sharekeys_items'),
1880
                        array(
1881
                            'share_key' => $share_key_for_item,
1882
                        ),
1883
                        'increment_id = %i',
1884
                        $currentUserKey['increment_id']
1885
                    );
1886
                } else {
1887
                    DB::insert(
1888
                        prefixTable('sharekeys_items'),
1889
                        array(
1890
                            'object_id' => (int) $record['id'],
1891
                            'user_id' => (int) $post_user_id,
1892
                            'share_key' => $share_key_for_item,
1893
                        )
1894
                    );
1895
                }
1896
            }
1897
        }
1898
    }
1899
1900
    // SHould we change step?
1901
    DB::query(
1902
        'SELECT *
1903
        FROM ' . prefixTable('items') . '
1904
        WHERE perso = 0'
1905
    );
1906
1907
    $next_start = (int) $post_start + (int) $post_length;
1908
    return [
1909
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
1910
        'post_action' => $next_start > DB::count() ? 'step2' : 'step1',
1911
    ];
1912
}
1913
1914
function continueReEncryptingUserSharekeysStep2(
1915
    int $post_user_id,
1916
    bool $post_self_change,
1917
    string $post_action,
1918
    int $post_start,
1919
    int $post_length,
1920
    string $user_public_key,
1921
    array $SETTINGS
1922
): array
1923
{
1924
    // Loop on logs
1925
    $rows = DB::query(
1926
        'SELECT increment_id
1927
        FROM ' . prefixTable('log_items') . '
1928
        WHERE raison LIKE "at_pw :%" AND encryption_type = "teampass_aes"
1929
        LIMIT ' . $post_start . ', ' . $post_length
1930
    );
1931
    foreach ($rows as $record) {
1932
        // Get itemKey from current user
1933
        $currentUserKey = DB::queryFirstRow(
1934
            'SELECT share_key
1935
            FROM ' . prefixTable('sharekeys_logs') . '
1936
            WHERE object_id = %i AND user_id = %i',
1937
            $record['increment_id'],
1938
            $_SESSION['user_id']
1939
        );
1940
1941
        // Decrypt itemkey with admin key
1942
        $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $_SESSION['user']['private_key']);
1943
1944
        // Encrypt Item key
1945
        $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
1946
1947
        // Save the key in DB
1948
        if ($post_self_change === false) {
1949
            DB::insert(
1950
                prefixTable('sharekeys_logs'),
1951
                array(
1952
                    'object_id' => (int) $record['increment_id'],
1953
                    'user_id' => (int) $post_user_id,
1954
                    'share_key' => $share_key_for_item,
1955
                )
1956
            );
1957
        } else {
1958
            // Get itemIncrement from selected user
1959
            if ((int) $post_user_id !== (int) $_SESSION['user_id']) {
1960
                $currentUserKey = DB::queryFirstRow(
1961
                    'SELECT increment_id
1962
                    FROM ' . prefixTable('sharekeys_items') . '
1963
                    WHERE object_id = %i AND user_id = %i',
1964
                    $record['id'],
1965
                    $post_user_id
1966
                );
1967
            }
1968
1969
            // NOw update
1970
            DB::update(
1971
                prefixTable('sharekeys_logs'),
1972
                array(
1973
                    'share_key' => $share_key_for_item,
1974
                ),
1975
                'increment_id = %i',
1976
                $currentUserKey['increment_id']
1977
            );
1978
        }
1979
    }
1980
1981
    // SHould we change step?
1982
    DB::query(
1983
        'SELECT increment_id
1984
        FROM ' . prefixTable('log_items') . '
1985
        WHERE raison LIKE "at_pw :%" AND encryption_type = "teampass_aes"'
1986
    );
1987
1988
    $next_start = (int) $post_start + (int) $post_length;
1989
    return [
1990
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
1991
        'post_action' => $next_start > DB::count() ? 'step3' : 'step2',
1992
    ];
1993
}
1994
1995
function continueReEncryptingUserSharekeysStep3(
1996
    int $post_user_id,
1997
    bool $post_self_change,
1998
    string $post_action,
1999
    int $post_start,
2000
    int $post_length,
2001
    string $user_public_key,
2002
    array $SETTINGS
2003
): array
2004
{
2005
    // Loop on fields
2006
    $rows = DB::query(
2007
        'SELECT id
2008
        FROM ' . prefixTable('categories_items') . '
2009
        WHERE encryption_type = "teampass_aes"
2010
        LIMIT ' . $post_start . ', ' . $post_length
2011
    );
2012
    foreach ($rows as $record) {
2013
        // Get itemKey from current user
2014
        $currentUserKey = DB::queryFirstRow(
2015
            'SELECT share_key
2016
            FROM ' . prefixTable('sharekeys_fields') . '
2017
            WHERE object_id = %i AND user_id = %i',
2018
            $record['id'],
2019
            $_SESSION['user_id']
2020
        );
2021
2022
        // Decrypt itemkey with admin key
2023
        $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $_SESSION['user']['private_key']);
2024
2025
        // Encrypt Item key
2026
        $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
2027
2028
        // Save the key in DB
2029
        if ($post_self_change === false) {
2030
            DB::insert(
2031
                prefixTable('sharekeys_fields'),
2032
                array(
2033
                    'object_id' => (int) $record['id'],
2034
                    'user_id' => (int) $post_user_id,
2035
                    'share_key' => $share_key_for_item,
2036
                )
2037
            );
2038
        } else {
2039
            // Get itemIncrement from selected user
2040
            if ((int) $post_user_id !== (int) $_SESSION['user_id']) {
2041
                $currentUserKey = DB::queryFirstRow(
2042
                    'SELECT increment_id
2043
                    FROM ' . prefixTable('sharekeys_items') . '
2044
                    WHERE object_id = %i AND user_id = %i',
2045
                    $record['id'],
2046
                    $post_user_id
2047
                );
2048
            }
2049
2050
            // NOw update
2051
            DB::update(
2052
                prefixTable('sharekeys_fields'),
2053
                array(
2054
                    'share_key' => $share_key_for_item,
2055
                ),
2056
                'increment_id = %i',
2057
                $currentUserKey['increment_id']
2058
            );
2059
        }
2060
    }
2061
2062
    // SHould we change step?
2063
    DB::query(
2064
        'SELECT *
2065
        FROM ' . prefixTable('categories_items') . '
2066
        WHERE encryption_type = "teampass_aes"'
2067
    );
2068
2069
    $next_start = (int) $post_start + (int) $post_length;
2070
    return [
2071
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2072
        'post_action' => $next_start > DB::count() ? 'step4' : 'step3',
2073
    ];
2074
}
2075
2076
function continueReEncryptingUserSharekeysStep4(
2077
    int $post_user_id,
2078
    bool $post_self_change,
2079
    string $post_action,
2080
    int $post_start,
2081
    int $post_length,
2082
    string $user_public_key,
2083
    array $SETTINGS
2084
): array
2085
{
2086
    // Loop on suggestions
2087
    $rows = DB::query(
2088
        'SELECT id
2089
        FROM ' . prefixTable('suggestion') . '
2090
        LIMIT ' . $post_start . ', ' . $post_length
2091
    );
2092
    foreach ($rows as $record) {
2093
        // Get itemKey from current user
2094
        $currentUserKey = DB::queryFirstRow(
2095
            'SELECT share_key
2096
            FROM ' . prefixTable('sharekeys_suggestions') . '
2097
            WHERE object_id = %i AND user_id = %i',
2098
            $record['id'],
2099
            $_SESSION['user_id']
2100
        );
2101
2102
        // Decrypt itemkey with admin key
2103
        $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $_SESSION['user']['private_key']);
2104
2105
        // Encrypt Item key
2106
        $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
2107
2108
        // Save the key in DB
2109
        if ($post_self_change === false) {
2110
            DB::insert(
2111
                prefixTable('sharekeys_suggestions'),
2112
                array(
2113
                    'object_id' => (int) $record['id'],
2114
                    'user_id' => (int) $post_user_id,
2115
                    'share_key' => $share_key_for_item,
2116
                )
2117
            );
2118
        } else {
2119
            // Get itemIncrement from selected user
2120
            if ((int) $post_user_id !== (int) $_SESSION['user_id']) {
2121
                $currentUserKey = DB::queryFirstRow(
2122
                    'SELECT increment_id
2123
                    FROM ' . prefixTable('sharekeys_items') . '
2124
                    WHERE object_id = %i AND user_id = %i',
2125
                    $record['id'],
2126
                    $post_user_id
2127
                );
2128
            }
2129
2130
            // NOw update
2131
            DB::update(
2132
                prefixTable('sharekeys_suggestions'),
2133
                array(
2134
                    'share_key' => $share_key_for_item,
2135
                ),
2136
                'increment_id = %i',
2137
                $currentUserKey['increment_id']
2138
            );
2139
        }
2140
    }
2141
2142
    // SHould we change step?
2143
    DB::query(
2144
        'SELECT *
2145
        FROM ' . prefixTable('suggestion')
2146
    );
2147
2148
    $next_start = (int) $post_start + (int) $post_length;
2149
    return [
2150
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2151
        'post_action' => $next_start > DB::count() ? 'step5' : 'step4',
2152
    ];
2153
}
2154
2155
function continueReEncryptingUserSharekeysStep5(
2156
    int $post_user_id,
2157
    bool $post_self_change,
2158
    string $post_action,
2159
    int $post_start,
2160
    int $post_length,
2161
    string $user_public_key,
2162
    array $SETTINGS
2163
): array
2164
{
2165
    // Loop on files
2166
    $rows = DB::query(
2167
        'SELECT id
2168
        FROM ' . prefixTable('files') . '
2169
        WHERE status = "' . TP_ENCRYPTION_NAME . '"
2170
        LIMIT ' . $post_start . ', ' . $post_length
2171
    ); //aes_encryption
2172
    foreach ($rows as $record) {
2173
        // Get itemKey from current user
2174
        $currentUserKey = DB::queryFirstRow(
2175
            'SELECT share_key
2176
            FROM ' . prefixTable('sharekeys_files') . '
2177
            WHERE object_id = %i AND user_id = %i',
2178
            $record['id'],
2179
            $_SESSION['user_id']
2180
        );
2181
2182
        // Decrypt itemkey with admin key
2183
        $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $_SESSION['user']['private_key']);
2184
2185
        // Encrypt Item key
2186
        $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
2187
2188
        // Save the key in DB
2189
        if ($post_self_change === false) {
2190
            DB::insert(
2191
                prefixTable('sharekeys_files'),
2192
                array(
2193
                    'object_id' => (int) $record['id'],
2194
                    'user_id' => (int) $post_user_id,
2195
                    'share_key' => $share_key_for_item,
2196
                )
2197
            );
2198
        } else {
2199
            // Get itemIncrement from selected user
2200
            if ((int) $post_user_id !== (int) $_SESSION['user_id']) {
2201
                $currentUserKey = DB::queryFirstRow(
2202
                    'SELECT increment_id
2203
                    FROM ' . prefixTable('sharekeys_items') . '
2204
                    WHERE object_id = %i AND user_id = %i',
2205
                    $record['id'],
2206
                    $post_user_id
2207
                );
2208
            }
2209
2210
            // NOw update
2211
            DB::update(
2212
                prefixTable('sharekeys_files'),
2213
                array(
2214
                    'share_key' => $share_key_for_item,
2215
                ),
2216
                'increment_id = %i',
2217
                $currentUserKey['increment_id']
2218
            );
2219
        }
2220
    }
2221
2222
    // SHould we change step?
2223
    DB::query(
2224
        'SELECT *
2225
        FROM ' . prefixTable('files') . '
2226
        WHERE status = "' . TP_ENCRYPTION_NAME . '"'
2227
    );
2228
2229
    $next_start = (int) $post_start + (int) $post_length;
2230
    return [
2231
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2232
        'post_action' => $next_start > DB::count() ? 'step6' : 'step5',
2233
    ];
2234
}
2235
2236
function continueReEncryptingUserSharekeysStep6(
2237
    int $post_user_id,
2238
    bool $post_self_change,
2239
    string $post_action,
2240
    int $post_start,
2241
    int $post_length,
2242
    string $user_public_key,
2243
    array $SETTINGS
2244
): array
2245
{
2246
    // IF USER IS NOT THE SAME
2247
    if ((int) $post_user_id === (int) $_SESSION['user_id']) {
2248
        return [
2249
            'next_start' => 0,
2250
            'post_action' => 'finished',
2251
        ];
2252
    }
2253
    
2254
    // Loop on persoanl items
2255
    if (count($_SESSION['personal_folders']) > 0) {
2256
        $rows = DB::query(
2257
            'SELECT id, pw
2258
            FROM ' . prefixTable('items') . '
2259
            WHERE perso = 1 AND id_tree IN %ls
2260
            LIMIT ' . $post_start . ', ' . $post_length,
2261
            $_SESSION['personal_folders']
2262
        );
2263
        foreach ($rows as $record) {
2264
            // Get itemKey from current user
2265
            $currentUserKey = DB::queryFirstRow(
2266
                'SELECT share_key, increment_id
2267
                FROM ' . prefixTable('sharekeys_items') . '
2268
                WHERE object_id = %i AND user_id = %i',
2269
                $record['id'],
2270
                $_SESSION['user_id']
2271
            );
2272
2273
            // Decrypt itemkey with admin key
2274
            $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $_SESSION['user']['private_key']);
2275
2276
            // Encrypt Item key
2277
            $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
2278
2279
            // Save the key in DB
2280
            if ($post_self_change === false) {
2281
                DB::insert(
2282
                    prefixTable('sharekeys_items'),
2283
                    array(
2284
                        'object_id' => (int) $record['id'],
2285
                        'user_id' => (int) $post_user_id,
2286
                        'share_key' => $share_key_for_item,
2287
                    )
2288
                );
2289
            } else {
2290
                // Get itemIncrement from selected user
2291
                if ((int) $post_user_id !== (int) $_SESSION['user_id']) {
2292
                    $currentUserKey = DB::queryFirstRow(
2293
                        'SELECT increment_id
2294
                        FROM ' . prefixTable('sharekeys_items') . '
2295
                        WHERE object_id = %i AND user_id = %i',
2296
                        $record['id'],
2297
                        $post_user_id
2298
                    );
2299
                }
2300
2301
                // NOw update
2302
                DB::update(
2303
                    prefixTable('sharekeys_items'),
2304
                    array(
2305
                        'share_key' => $share_key_for_item,
2306
                    ),
2307
                    'increment_id = %i',
2308
                    $currentUserKey['increment_id']
2309
                );
2310
            }
2311
        }
2312
    }
2313
2314
    // SHould we change step?
2315
    DB::query(
2316
        'SELECT *
2317
        FROM ' . prefixTable('items') . '
2318
        WHERE perso = 0'
2319
    );
2320
2321
    $next_start = (int) $post_start + (int) $post_length;
2322
    return [
2323
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2324
        'post_action' => $next_start > DB::count() ? 'finished' : 'step6',
2325
    ];
2326
}
2327
2328
function migrateTo3_DoUserPersonalItemsEncryption(
2329
    int $post_user_id,
2330
    int $post_start,
2331
    int $post_length,
2332
    string $post_user_psk,
2333
    array $SETTINGS
2334
) {
2335
    $next_step = '';
2336
2337
    if (is_null($post_user_id) === false && isset($post_user_id) === true && empty($post_user_id) === false) {
2338
        // Check if user exists
2339
        $userInfo = DB::queryFirstRow(
2340
            'SELECT public_key, encrypted_psk
2341
            FROM ' . prefixTable('users') . '
2342
            WHERE id = %i',
2343
            $post_user_id
2344
        );
2345
        if (DB::count() > 0) {
2346
            // check if psk is correct.
2347
            if (empty($userInfo['encrypted_psk']) === false) {
2348
                $user_key_encoded = defuse_validate_personal_key(
2349
                    $post_user_psk,
2350
                    $userInfo['encrypted_psk']
2351
                );
2352
2353
                if (strpos($user_key_encoded, "Error ") !== false) {
2354
                    return prepareExchangedData(
2355
                        array(
2356
                            'error' => true,
2357
                            'message' => langHdl('bad_psk'),
2358
                        ),
2359
                        'encode'
2360
                    );
2361
                }
2362
2363
                // Loop on persoanl items
2364
                $rows = DB::query(
2365
                    'SELECT id, pw
2366
                    FROM ' . prefixTable('items') . '
2367
                    WHERE perso = 1 AND id_tree IN %ls
2368
                    LIMIT ' . $post_start . ', ' . $post_length,
2369
                    $_SESSION['personal_folders']
2370
                );
2371
                $countUserPersonalItems = DB::count();
2372
                foreach ($rows as $record) {
2373
                    if ($record['encryption_type'] !== 'teampass_aes') {
2374
                        // Decrypt with Defuse
2375
                        $passwd = cryption(
2376
                            $record['pw'],
2377
                            $user_key_encoded,
2378
                            'decrypt',
2379
                            $SETTINGS
2380
                        );
2381
2382
                        // Encrypt with Object Key
2383
                        $cryptedStuff = doDataEncryption($passwd['string']);
2384
2385
                        // Store new password in DB
2386
                        DB::update(
2387
                            prefixTable('items'),
2388
                            array(
2389
                                'pw' => $cryptedStuff['encrypted'],
2390
                                'encryption_type' => 'teampass_aes',
2391
                            ),
2392
                            'id = %i',
2393
                            $record['id']
2394
                        );
2395
2396
                        // Insert in DB the new object key for this item by user
2397
                        DB::insert(
2398
                            prefixTable('sharekeys_items'),
2399
                            array(
2400
                                'object_id' => (int) $record['id'],
2401
                                'user_id' => (int) $post_user_id,
2402
                                'share_key' => encryptUserObjectKey($cryptedStuff['objectKey'], $userInfo['public_key']),
2403
                            )
2404
                        );
2405
2406
2407
                        // Does this item has Files?
2408
                        // Loop on files
2409
                        $rows = DB::query(
2410
                            'SELECT id, file
2411
                            FROM ' . prefixTable('files') . '
2412
                            WHERE status != %s
2413
                            AND id_item = %i',
2414
                            TP_ENCRYPTION_NAME,
2415
                            $record['id']
2416
                        );
2417
                        //aes_encryption
2418
                        foreach ($rows as $record2) {
2419
                            // Now decrypt the file
2420
                            prepareFileWithDefuse(
2421
                                'decrypt',
2422
                                $SETTINGS['path_to_upload_folder'] . '/' . $record2['file'],
2423
                                $SETTINGS['path_to_upload_folder'] . '/' . $record2['file'] . '.delete',
2424
                                $SETTINGS,
2425
                                $post_user_psk
2426
                            );
2427
2428
                            // Encrypt the file
2429
                            $encryptedFile = encryptFile($record2['file'] . '.delete', $SETTINGS['path_to_upload_folder']);
2430
2431
                            DB::update(
2432
                                prefixTable('files'),
2433
                                array(
2434
                                    'file' => $encryptedFile['fileHash'],
2435
                                    'status' => TP_ENCRYPTION_NAME,
2436
                                ),
2437
                                'id = %i',
2438
                                $record2['id']
2439
                            );
2440
2441
                            // Save key
2442
                            DB::insert(
2443
                                prefixTable('sharekeys_files'),
2444
                                array(
2445
                                    'object_id' => (int) $record2['id'],
2446
                                    'user_id' => (int) $_SESSION['user_id'],
2447
                                    'share_key' => encryptUserObjectKey($encryptedFile['objectKey'], $_SESSION['user']['public_key']),
2448
                                )
2449
                            );
2450
2451
                            // Unlink original file
2452
                            unlink($SETTINGS['path_to_upload_folder'] . '/' . $record2['file']);
2453
                        }
2454
                    }
2455
                }
2456
2457
                // SHould we change step?
2458
                $next_start = (int) $post_start + (int) $post_length;
2459
                if ($next_start > $countUserPersonalItems) {
2460
                    // Now update user
2461
                    DB::update(
2462
                        prefixTable('users'),
2463
                        array(
2464
                            'special' => 'none',
2465
                            'upgrade_needed' => 0,
2466
                            'encrypted_psk' => '',
2467
                        ),
2468
                        'id = %i',
2469
                        $post_user_id
2470
                    );
2471
2472
                    $next_step = 'finished';
2473
                    $next_start = 0;
2474
                }
2475
2476
                // Continu with next step
2477
                return prepareExchangedData(
2478
                    array(
2479
                        'error' => false,
2480
                        'message' => '',
2481
                        'step' => $next_step,
2482
                        'start' => $next_start,
2483
                        'userId' => $post_user_id
2484
                    ),
2485
                    'encode'
2486
                );
2487
            }
2488
        }
2489
        
2490
        // Nothing to do
2491
        return prepareExchangedData(
2492
            array(
2493
                'error' => true,
2494
                'message' => langHdl('error_no_user'),
2495
            ),
2496
            'encode'
2497
        );
2498
    }
2499
    
2500
    // Nothing to do
2501
    return prepareExchangedData(
2502
        array(
2503
            'error' => true,
2504
            'message' => langHdl('error_no_user'),
2505
        ),
2506
        'encode'
2507
    );
2508
}
2509
2510
2511
function getUserInfo(
2512
    int $post_user_id,
2513
    string $post_fields,
2514
    array $SETTINGS
2515
)
2516
{
2517
    if (is_null($post_user_id) === false && isset($post_user_id) === true && empty($post_user_id) === false) {
2518
        // Get user info
2519
        $userData = DB::queryFirstRow(
2520
            'SELECT '.$post_fields.'
2521
            FROM ' . prefixTable('users') . '
2522
            WHERE id = %i',
2523
            $post_user_id
2524
        );
2525
        if (DB::count() > 0) {
2526
            return prepareExchangedData(
2527
                array(
2528
                    'error' => false,
2529
                    'message' => '',
2530
                    'queryResults' => $userData,
2531
                ),
2532
                'encode'
2533
            );
2534
        }
2535
    }
2536
    return prepareExchangedData(
2537
        array(
2538
            'error' => true,
2539
            'message' => langHdl('error_no_user'),
2540
        ),
2541
        'encode'
2542
    );
2543
}
2544
2545
function changeUserAuthenticationPassword(
2546
    int $post_user_id,
2547
    string $post_current_pwd,
2548
    string $post_new_pwd,
2549
    array $SETTINGS
2550
)
2551
{
2552
    if (is_null($post_user_id) === false && isset($post_user_id) === true && empty($post_user_id) === false) {
2553
        // Get user info
2554
        $userData = DB::queryFirstRow(
2555
            'SELECT auth_type, login, private_key
2556
            FROM ' . prefixTable('users') . '
2557
            WHERE id = %i',
2558
            $post_user_id
2559
        );
2560
        if (DB::count() > 0 && empty($userData['private_key']) === false) {
2561
            // Now check if current password is correct
2562
            // For this, just check if it is possible to decrypt the privatekey
2563
            // And compare it to the one in session
2564
            $privateKey = decryptPrivateKey($post_current_pwd, $userData['private_key']);
2565
2566
            // Load superGlobals
2567
            include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
2568
            $superGlobal = new protect\SuperGlobal\SuperGlobal();
2569
2570
            if ($superGlobal->get('private_key', 'SESSION', 'user') === $privateKey) {
2571
                // Encrypt it with new password
2572
                $hashedPrivateKey = encryptPrivateKey($post_new_pwd, $privateKey);
2573
2574
                // Generate new hash for auth password
2575
                // load passwordLib library
2576
                $pwdlib = new SplClassLoader('PasswordLib', '../includes/libraries');
2577
                $pwdlib->register();
2578
                $pwdlib = new PasswordLib\PasswordLib();
2579
2580
                // Prepare variables
2581
                $newPw = $pwdlib->createPasswordHash($post_new_pwd);
2582
2583
                // Update user account
2584
                DB::update(
2585
                    prefixTable('users'),
2586
                    array(
2587
                        'private_key' => $hashedPrivateKey,
2588
                        'pw' => $newPw,
2589
                        'special' => 'none',
2590
                    ),
2591
                    'id = %i',
2592
                    $post_user_id
2593
                );
2594
2595
                $superGlobal->put('private_key', $privateKey, 'SESSION', 'user');
2596
2597
                return prepareExchangedData(
2598
                    array(
2599
                        'error' => false,
2600
                        'message' => langHdl('done'),'',
2601
                    ),
2602
                    'encode'
2603
                );
2604
            }
2605
            
2606
            // ERROR
2607
            return prepareExchangedData(
2608
                array(
2609
                    'error' => true,
2610
                    'message' => langHdl('bad_password'),
2611
                ),
2612
                'encode'
2613
            );
2614
        }
2615
    }
2616
        
2617
    return prepareExchangedData(
2618
        array(
2619
            'error' => true,
2620
            'message' => langHdl('error_no_user'),
2621
        ),
2622
        'encode'
2623
    );
2624
}
2625
2626
            
2627
function changeUserLDAPAuthenticationPassword(
2628
    int $post_user_id,
2629
    string $post_previous_pwd,
2630
    string $post_current_pwd,
2631
    array $SETTINGS
2632
)
2633
{
2634
    if (is_null($post_user_id) === false && isset($post_user_id) === true && empty($post_user_id) === false) {
2635
        // Get user info
2636
        $userData = DB::queryFirstRow(
2637
            'SELECT auth_type, login, private_key, special
2638
            FROM ' . prefixTable('users') . '
2639
            WHERE id = %i',
2640
            $post_user_id
2641
        );
2642
        
2643
        if (DB::count() > 0 && empty($userData['private_key']) === false) {
2644
            // Now check if current password is correct (only if not ldap)
2645
            if ($userData['auth_type'] === 'ldap' && $userData['special'] === 'auth-pwd-change') {
2646
                // As it is a change for an LDAP user
2647
                
2648
                // Now check if current password is correct
2649
                // For this, just check if it is possible to decrypt the privatekey
2650
                // And compare it to the one in session
2651
                $privateKey = decryptPrivateKey($post_previous_pwd, $userData['private_key']);
2652
2653
                // Encrypt it with new password
2654
                $hashedPrivateKey = encryptPrivateKey($post_current_pwd, $privateKey);
2655
2656
                // Update user account
2657
                DB::update(
2658
                    prefixTable('users'),
2659
                    array(
2660
                        'private_key' => $hashedPrivateKey,
2661
                        'special' => 'none',
2662
                    ),
2663
                    'id = %i',
2664
                    $post_user_id
2665
                );
2666
2667
                // Load superGlobals
2668
                include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
2669
                $superGlobal = new protect\SuperGlobal\SuperGlobal();
2670
                $superGlobal->put('private_key', $privateKey, 'SESSION', 'user');
2671
2672
                return prepareExchangedData(
2673
                    array(
2674
                        'error' => false,
2675
                        'message' => langHdl('done'),'',
2676
                    ),
2677
                    'encode'
2678
                );
2679
            }
2680
2681
            // For this, just check if it is possible to decrypt the privatekey
2682
            // And try to decrypt one existing key
2683
            $privateKey = decryptPrivateKey($post_previous_pwd, $userData['private_key']);
2684
2685
            if (empty($privateKey) === true) {
2686
                return prepareExchangedData(
2687
                    array(
2688
                        'error' => true,
2689
                        'message' => langHdl('password_is_not_correct'),
2690
                    ),
2691
                    'encode'
2692
                );
2693
            }
2694
2695
            // Test if possible to decvrypt one key
2696
            // Get one item
2697
            $record = DB::queryFirstRow(
2698
                'SELECT id, pw
2699
                FROM ' . prefixTable('items') . '
2700
                WHERE perso = 0'
2701
            );
2702
2703
            // Get itemKey from current user
2704
            $currentUserKey = DB::queryFirstRow(
2705
                'SELECT share_key, increment_id
2706
                FROM ' . prefixTable('sharekeys_items') . '
2707
                WHERE object_id = %i AND user_id = %i',
2708
                $record['id'],
2709
                $post_user_id
2710
            );
2711
2712
            if (count($currentUserKey) > 0) {
2713
                // Decrypt itemkey with user key
2714
                // use old password to decrypt private_key
2715
                $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $privateKey);
2716
                
2717
                if (empty(base64_decode($itemKey)) === false) {
2718
                    // GOOD password
2719
                    // Encrypt it with current password
2720
                    $hashedPrivateKey = encryptPrivateKey($post_current_pwd, $privateKey);
2721
                    
2722
                    // Update user account
2723
                    DB::update(
2724
                        prefixTable('users'),
2725
                        array(
2726
                            'private_key' => $hashedPrivateKey,
2727
                            'special' => 'none',
2728
                        ),
2729
                        'id = %i',
2730
                        $post_user_id
2731
                    );
2732
                    
2733
                    // Load superGlobals
2734
                    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
2735
                    $superGlobal = new protect\SuperGlobal\SuperGlobal();
2736
                    $superGlobal->put('private_key', $privateKey, 'SESSION', 'user');
2737
2738
                    return prepareExchangedData(
2739
                        array(
2740
                            'error' => false,
2741
                            'message' => langHdl('done'),
2742
                        ),
2743
                        'encode'
2744
                    );
2745
                }
2746
            }
2747
            
2748
            // ERROR
2749
            return prepareExchangedData(
2750
                array(
2751
                    'error' => true,
2752
                    'message' => langHdl('bad_password'),
2753
                ),
2754
                'encode'
2755
            );
2756
        }
2757
    }
2758
2759
    // ERROR
2760
    return prepareExchangedData(
2761
        array(
2762
            'error' => true,
2763
            'message' => langHdl('error_no_user'),
2764
        ),
2765
        'encode'
2766
    );
2767
}
2768
2769
2770
function increaseSessionDuration(
2771
    int $duration
2772
): string
2773
{
2774
    // check if session is not already expired.
2775
    if ($_SESSION['sessionDuration'] > time()) {
2776
        // Calculate end of session
2777
        $_SESSION['sessionDuration'] = (int) ($_SESSION['sessionDuration'] + $duration);
2778
        // Update table
2779
        DB::update(
2780
            prefixTable('users'),
2781
            array(
2782
                'session_end' => $_SESSION['sessionDuration'],
2783
            ),
2784
            'id = %i',
2785
            $_SESSION['user_id']
2786
        );
2787
        // Return data
2788
        return '[{"new_value":"' . $_SESSION['sessionDuration'] . '"}]';
2789
    }
2790
    
2791
    return '[{"new_value":"expired"}]';
2792
}