Passed
Push — teampass_3.0 ( 156bc8...fb33ce )
by Nils
05:48
created

getNumberOfItemsToTreat()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 20
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 1
eloc 10
c 1
b 1
f 0
nc 1
nop 2
dl 0
loc 20
rs 9.9332
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Teampass - a collaborative passwords manager.
7
 * ---
8
 * This library is distributed in the hope that it will be useful,
9
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11
 * ---
12
 * @project   Teampass
13
 * @file      main.queries.php
14
 * ---
15
 * @author    Nils Laumaillé ([email protected])
16
 * @copyright 2009-2021 Teampass.net
17
 * @license   https://spdx.org/licenses/GPL-3.0-only.html#licenseText GPL-3.0
18
 * ---
19
 * @see       https://www.teampass.net
20
 */
21
22
if (isset($_SESSION) === false) {
23
    include_once 'SecureHandler.php';
24
    session_name('teampass_session');
25
    session_start();
26
    $_SESSION['CPM'] = 1;
27
}
28
29
if (isset($_SESSION['CPM']) === false || $_SESSION['CPM'] !== 1) {
30
    $_SESSION['error']['code'] = '1004';
31
    include '../error.php';
32
    exit();
33
}
34
35
// Load config
36
if (file_exists('../includes/config/tp.config.php')) {
37
    include '../includes/config/tp.config.php';
38
} elseif (file_exists('./includes/config/tp.config.php')) {
39
    include './includes/config/tp.config.php';
40
} else {
41
    throw new Exception("Error file '/includes/config/tp.config.php' not exists", 1);
42
}
43
44
// 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_NUMBER_INT),
389
                $dataReceived['body'],
390
                (string) filter_var($dataReceived['subject'], FILTER_SANITIZE_STRING),
391
                (array) filter_var($dataReceived['pre_replace'], FILTER_SANITIZE_STRING),
392
                $SETTINGS
393
            );
394
395
            echo $return;
396
397
            break;
398
399
        /*
400
        * Generate a temporary encryption key for user
401
        */
402
        case 'generate_temporary_encryption_key':
403
            $return = generateOneTimeCode(
404
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
405
                $SETTINGS
406
            );
407
            
408
            echo $return;
409
410
            break;
411
412
        /*
413
        * user_sharekeys_reencryption_start
414
        */
415
        case 'user_sharekeys_reencryption_start':
416
            $return = startReEncryptingUserSharekeys(
417
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
418
                (bool) filter_var($dataReceived['self_change'], FILTER_SANITIZE_STRING),
419
                $SETTINGS
420
            );
421
            
422
            echo $return;
423
424
            break;
425
426
        /*
427
        * user_sharekeys_reencryption_next
428
        */
429
        case 'user_sharekeys_reencryption_next':
430
            $return = continueReEncryptingUserSharekeys(
431
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
432
                (bool) filter_var($dataReceived['self_change'], FILTER_SANITIZE_STRING),
433
                (string) filter_var($dataReceived['action'], FILTER_SANITIZE_STRING),
434
                (int) filter_var($dataReceived['start'], FILTER_SANITIZE_NUMBER_INT),
435
                (int) filter_var($dataReceived['length'], FILTER_SANITIZE_NUMBER_INT),
436
                $SETTINGS
437
            );
438
            
439
            echo $return;
440
441
            break;
442
443
        /*
444
        * user_psk_reencryption
445
        */
446
        case 'user_psk_reencryption':
447
            $return = migrateTo3_DoUserPersonalItemsEncryption(
448
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
449
                (int) filter_var($dataReceived['start'], FILTER_SANITIZE_NUMBER_INT),
450
                (int) filter_var($dataReceived['length'], FILTER_SANITIZE_NUMBER_INT),
451
                (string) filter_var($dataReceived['userPsk'], FILTER_SANITIZE_STRING),
452
                $SETTINGS
453
            );
454
            
455
            echo $return;
456
        
457
            break;
458
459
        /*
460
        * Get info 
461
        */
462
        case 'get_user_info':
463
            $return = getUserInfo(
464
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
465
                (string) filter_var($dataReceived['fields'], FILTER_SANITIZE_STRING),
466
                $SETTINGS
467
            );
468
            
469
            echo $return;
470
471
            break;
472
473
        /*
474
        * Change user's authenticataion password
475
        */
476
        case 'change_user_auth_password':
477
            $return = changeUserAuthenticationPassword(
478
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
479
                (string) filter_var($dataReceived['old_password'], FILTER_SANITIZE_STRING),
480
                (string) filter_var($dataReceived['new_password'], FILTER_SANITIZE_STRING),
481
                $SETTINGS
482
            );
483
            
484
            echo $return;
485
486
            break;
487
488
489
        /*
490
        * User's authenticataion password in LDAP has changed
491
        */
492
        case 'change_user_ldap_auth_password':
493
            $return = /** @scrutinizer ignore-call */ changeUserLDAPAuthenticationPassword(
494
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
495
                filter_var($dataReceived['previous_password'], FILTER_SANITIZE_STRING),
496
                filter_var($dataReceived['current_password'], FILTER_SANITIZE_STRING),
497
                $SETTINGS
498
            );
499
            
500
            echo $return;
501
502
            break;
503
504
505
        /*
506
        * How many items for this user
507
        */
508
        case 'get_number_of_items_to_treat':
509
            echo getNumberOfItemsToTreat(
510
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
511
                $SETTINGS
512
            );
513
514
            break;
515
    }
516
}
517
518
519
/**
520
 * Provides the number of items
521
 *
522
 * @param int   $userId     User ID
523
 * @param array $SETTINGS   TeampassSettings
524
 *
525
 * @return int
526
 */
527
function getNumberOfItemsToTreat(
528
    int $userId,
529
    array $SETTINGS
530
): string
531
{
532
    // get number of items
533
    DB::queryFirstRow(
534
        'SELECT increment_id
535
        FROM ' . prefixTable('sharekeys_items') .
536
        ' WHERE user_id = %i',
537
        $userId
538
    );
539
540
    // Send back
541
    return prepareExchangedData(
0 ignored issues
show
Bug Best Practice introduced by
The expression return prepareExchangedD...DB::count()), 'encode') returns the type string which is incompatible with the documented return type integer.
Loading history...
542
        array(
543
            'error' => false,
544
            'nbItems' => DB::count(),
545
        ),
546
        'encode'
547
    );
548
}
549
550
551
/**
552
 * 
553
 */
554
function changePassword(
555
    string $post_new_password,
556
    string $post_current_password,
557
    int $post_password_complexity,
558
    string $post_change_request,
559
    int $post_user_id,
560
    array $SETTINGS
561
): string
562
{
563
    // load passwordLib library
564
    $pwdlib = new SplClassLoader('PasswordLib', '../includes/libraries');
565
    $pwdlib->register();
566
    $pwdlib = new PasswordLib\PasswordLib();
567
568
    // Prepare variables
569
    $post_new_password_hashed = $pwdlib->createPasswordHash($post_new_password);
570
571
    // User has decided to change is PW
572
    if ($post_change_request === 'reset_user_password_expected'
573
        || $post_change_request === 'user_decides_to_change_password'
574
    ) {
575
        // Check that current user is correct
576
        if ((int) $post_user_id !== (int) $_SESSION['user_id']) {
577
            return prepareExchangedData(
578
                array(
579
                    'error' => true,
580
                    'message' => langHdl('error_not_allowed_to'),
581
                ),
582
                'encode'
583
            );
584
        }
585
586
        // check if expected security level is reached
587
        $dataUser = DB::queryfirstrow(
588
            'SELECT *
589
            FROM ' . prefixTable('users') . ' WHERE id = %i',
590
            $post_user_id
591
        );
592
593
        // check if badly written
594
        $dataUser['fonction_id'] = array_filter(
595
            explode(',', str_replace(';', ',', $dataUser['fonction_id']))
596
        );
597
        $dataUser['fonction_id'] = implode(',', $dataUser['fonction_id']);
598
        DB::update(
599
            prefixTable('users'),
600
            array(
601
                'fonction_id' => $dataUser['fonction_id'],
602
            ),
603
            'id = %i',
604
            $post_user_id
605
        );
606
607
        if (empty($dataUser['fonction_id']) === false) {
608
            $data = DB::queryFirstRow(
609
                'SELECT complexity
610
                FROM ' . prefixTable('roles_title') . '
611
                WHERE id IN (' . $dataUser['fonction_id'] . ')
612
                ORDER BY complexity DESC'
613
            );
614
        } else {
615
            // In case user has no roles yet
616
            $data = array();
617
            $data['complexity'] = 0;
618
        }
619
620
        if ((int) $post_password_complexity < (int) $data['complexity']) {
621
            return prepareExchangedData(
622
                array(
623
                    'error' => true,
624
                    'message' => '<div style="margin:10px 0 10px 15px;">' . langHdl('complexity_level_not_reached') . '.<br>' .
625
                        langHdl('expected_complexity_level') . ': <b>' . TP_PW_COMPLEXITY[$data['complexity']][1] . '</b></div>',
626
                ),
627
                'encode'
628
            );
629
        }
630
631
        // Check that the 2 passwords are differents
632
        if ($post_current_password === $post_new_password) {
633
            return prepareExchangedData(
634
                array(
635
                    'error' => true,
636
                    'message' => langHdl('password_already_used'),
637
                ),
638
                'encode'
639
            );
640
        }
641
642
        // update sessions
643
        $_SESSION['last_pw_change'] = mktime(0, 0, 0, (int) date('m'), (int) date('d'), (int) date('y'));
644
        $_SESSION['validite_pw'] = true;
645
646
        // BEfore updating, check that the pwd is correct
647
        if ($pwdlib->verifyPasswordHash($post_new_password, $post_new_password_hashed) === true) {
648
            $special_action = 'none';
649
            if ($post_change_request === 'reset_user_password_expected') {
650
                $_SESSION['user']['private_key'] = decryptPrivateKey($post_current_password, $dataUser['private_key']);
651
            }
652
653
            // update DB
654
            DB::update(
655
                prefixTable('users'),
656
                array(
657
                    'pw' => $post_new_password_hashed,
658
                    'last_pw_change' => mktime(0, 0, 0, (int) date('m'), (int) date('d'), (int) date('y')),
659
                    'last_pw' => $post_current_password,
660
                    'special' => $special_action,
661
                    'private_key' => encryptPrivateKey($post_new_password, $_SESSION['user']['private_key']),
662
                ),
663
                'id = %i',
664
                $post_user_id
665
            );
666
            // update LOG
667
            logEvents($SETTINGS, 'user_mngt', 'at_user_pwd_changed', (string) $_SESSION['user_id'], $_SESSION['login'], $post_user_id);
668
669
            // Send back
670
            return prepareExchangedData(
671
                array(
672
                    'error' => false,
673
                    'message' => '',
674
                ),
675
                'encode'
676
            );
677
        }
678
        // Send back
679
        return prepareExchangedData(
680
            array(
681
                'error' => true,
682
                'message' => langHdl('pwd_hash_not_correct'),
683
            ),
684
            'encode'
685
        );
686
    }
687
    return prepareExchangedData(
688
        array(
689
            'error' => true,
690
            'message' => langHdl('error_not_allowed_to'),
691
        ),
692
        'encode'
693
    );
694
}
695
696
697
698
function generateQRCode(
699
    $post_id,
700
    $post_demand_origin,
701
    $post_send_mail,
702
    $post_login,
703
    $post_pwd,
704
    array $SETTINGS
705
): string
706
{
707
    // is this allowed by setting
708
    if ((isset($SETTINGS['ga_reset_by_user']) === false || (int) $SETTINGS['ga_reset_by_user'] !== 1)
709
        && (null === $post_demand_origin || $post_demand_origin !== 'users_management_list')
710
    ) {
711
        // User cannot ask for a new code
712
        return prepareExchangedData(
713
            array(
714
                'error' => true,
715
                'message' => langHdl('error_not_allowed_to'),
716
            ),
717
            'encode'
718
        );
719
    }
720
721
    // Check if user exists
722
    if (null === $post_id || empty($post_id) === true) {
723
        // Get data about user
724
        $data = DB::queryfirstrow(
725
            'SELECT id, email, pw
726
            FROM ' . prefixTable('users') . '
727
            WHERE login = %s',
728
            $post_login
729
        );
730
    } else {
731
        $data = DB::queryfirstrow(
732
            'SELECT id, login, email, pw
733
            FROM ' . prefixTable('users') . '
734
            WHERE id = %i',
735
            $post_id
736
        );
737
        $post_login = $data['login'];
738
    }
739
    // Get number of returned users
740
    $counter = DB::count();
741
742
    // load passwordLib library
743
    $pwdlib = new SplClassLoader('PasswordLib', $SETTINGS['cpassman_dir'] . '/includes/libraries');
744
    $pwdlib->register();
745
    $pwdlib = new PasswordLib\PasswordLib();
746
747
    // Do treatment
748
    if ($counter === 0) {
749
        // Not a registered user !
750
        logEvents($SETTINGS, 'failed_auth', 'user_not_exists', '', stripslashes($post_login), stripslashes($post_login));
751
        return prepareExchangedData(
752
            array(
753
                'error' => true,
754
                'message' => langHdl('no_user'),
755
                'tst' => 1,
756
            ),
757
            'encode'
758
        );
759
    }
760
761
    if (
762
        isset($post_pwd) === true
763
        && isset($data['pw']) === true
764
        && $pwdlib->verifyPasswordHash($post_pwd, $data['pw']) === false
765
        && $post_demand_origin !== 'users_management_list'
766
    ) {
767
        // checked the given password
768
        logEvents($SETTINGS, 'failed_auth', 'user_password_not_correct', '', stripslashes($post_login), stripslashes($post_login));
769
        return prepareExchangedData(
770
            array(
771
                'error' => true,
772
                'message' => langHdl('no_user'),
773
                'tst' => $post_demand_origin,
774
            ),
775
            'encode'
776
        );
777
    }
778
    
779
    if (empty($data['email']) === true) {
780
        return prepareExchangedData(
781
            array(
782
                'error' => true,
783
                'message' => langHdl('no_email'),
784
            ),
785
            'encode'
786
        );
787
    }
788
    
789
    // generate new GA user code
790
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Authentication/TwoFactorAuth/TwoFactorAuth.php';
791
    $tfa = new Authentication\TwoFactorAuth\TwoFactorAuth($SETTINGS['ga_website_name']);
792
    $gaSecretKey = $tfa->createSecret();
793
    $gaTemporaryCode = GenerateCryptKey(12, false, true, true, false, true, $SETTINGS);
794
795
    DB::update(
796
        prefixTable('users'),
797
        [
798
            'ga' => $gaSecretKey,
799
            'ga_temporary_code' => $gaTemporaryCode,
800
        ],
801
        'id = %i',
802
        $data['id']
803
    );
804
805
    // Log event
806
    logEvents($SETTINGS, 'user_connection', 'at_2fa_google_code_send_by_email', (string) $data['id'], stripslashes($post_login), stripslashes($post_login));
807
808
    // send mail?
809
    if ((int) $post_send_mail === 1) {
810
        json_decode(
811
            sendEmail(
812
                langHdl('email_ga_subject'),
813
                str_replace(
814
                    '#2FACode#',
815
                    $gaTemporaryCode,
816
                    langHdl('email_ga_text')
817
                ),
818
                $data['email'],
819
                $SETTINGS
820
            ),
821
            true
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) {
1253
            // Get one item
1254
            $record = DB::queryFirstRow(
1255
                'SELECT id, pw
1256
                FROM ' . prefixTable('items') . '
1257
                WHERE perso = 0'
1258
            );
1259
1260
            // Get itemKey from current user
1261
            $currentUserKey = DB::queryFirstRow(
1262
                'SELECT share_key, increment_id
1263
                FROM ' . prefixTable('sharekeys_items') . '
1264
                WHERE object_id = %i AND user_id = %i',
1265
                $record['id'],
1266
                $post_user_id
1267
            );
1268
1269
            if (count($currentUserKey) > 0) {
1270
                // Decrypt itemkey with user key
1271
                // use old password to decrypt private_key
1272
                $_SESSION['user']['private_key'] = decryptPrivateKey($post_user_password, $userInfo['private_key']);
1273
                $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $_SESSION['user']['private_key']);
1274
1275
                if (empty(base64_decode($itemKey)) === false) {
1276
                    // GOOD password
1277
                    return prepareExchangedData(
1278
                        array(
1279
                            'error' => false,
1280
                            'message' => '',
1281
                            'debug' => '',
1282
                        ),
1283
                        'encode'
1284
                    );
1285
                }
1286
            }
1287
            
1288
            // Use the password check
1289
            // load passwordLib library
1290
            $pwdlib = new SplClassLoader('PasswordLib', $SETTINGS['cpassman_dir'] . '/includes/libraries');
1291
            $pwdlib->register();
1292
            $pwdlib = new PasswordLib\PasswordLib();
1293
            
1294
            if ($pwdlib->verifyPasswordHash(htmlspecialchars_decode($post_user_password), $userInfo['pw']) === true) {
1295
                // GOOD password
1296
                return prepareExchangedData(
1297
                    array(
1298
                        'error' => false,
1299
                        'message' => '',
1300
                        'debug' => '',
1301
                    ),
1302
                    'encode'
1303
                );
1304
            }
1305
        }
1306
    }
1307
1308
    return prepareExchangedData(
1309
        array(
1310
            'error' => true,
1311
            'message' => langHdl('password_is_not_correct'),
1312
            'debug' => isset($itemKey) === true ? base64_decode($itemKey) : '',
1313
        ),
1314
        'encode'
1315
    );
1316
}
1317
1318
function changePrivateKeyEncryptionPassword(
1319
    int $post_user_id,
1320
    string $post_current_code,
1321
    string $post_new_code,
1322
    string $post_action_type,
1323
    array $SETTINGS
1324
): string
1325
{
1326
    if (empty($post_new_code) === true) {
1327
        $post_new_code = $_SESSION['user_pwd'];
1328
    }
1329
1330
    if (is_null($post_user_id) === false && isset($post_user_id) === true && empty($post_user_id) === false) {
1331
        // Get user info
1332
        $userData = DB::queryFirstRow(
1333
            'SELECT private_key
1334
            FROM ' . prefixTable('users') . '
1335
            WHERE id = %i',
1336
            $post_user_id
1337
        );
1338
        if (DB::count() > 0) {
1339
            if ($post_action_type === 'encrypt_privkey_with_user_password') {
1340
                // Here the user has his private key encrypted with an OTC.
1341
                // We need to encrypt it with his real password
1342
                $privateKey = decryptPrivateKey($post_new_code, $userData['private_key']);
1343
                $hashedPrivateKey = encryptPrivateKey($post_current_code, $privateKey);
1344
            } else {
1345
                $privateKey = decryptPrivateKey($post_current_code, $userData['private_key']);
1346
                $hashedPrivateKey = encryptPrivateKey($post_new_code, $privateKey);
1347
            }
1348
1349
            // Update user account
1350
            DB::update(
1351
                prefixTable('users'),
1352
                array(
1353
                    'private_key' => $hashedPrivateKey,
1354
                    'special' => 'none',
1355
                ),
1356
                'id = %i',
1357
                $post_user_id
1358
            );
1359
1360
            // Load superGlobals
1361
            include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
1362
            $superGlobal = new protect\SuperGlobal\SuperGlobal();
1363
1364
            $superGlobal->put('private_key', $privateKey, 'SESSION', 'user');
1365
        }
1366
1367
        // Return
1368
        return prepareExchangedData(
1369
            array(
1370
                'error' => false,
1371
                'message' => '',
1372
            ),
1373
            'encode'
1374
        );
1375
    }
1376
    
1377
    return prepareExchangedData(
1378
        array(
1379
            'error' => true,
1380
            'message' => langHdl('error_no_user'),
1381
            'debug' => '',
1382
        ),
1383
        'encode'
1384
    );
1385
}
1386
1387
function initializeUserPassword(
1388
    int $post_user_id,
1389
    string $post_special,
1390
    string $post_user_password,
1391
    bool $post_self_change,
1392
    array $SETTINGS
1393
): string
1394
{
1395
    if (is_null($post_user_id) === false && isset($post_user_id) === true && empty($post_user_id) === false) {
1396
        // Get user info
1397
        $userData = DB::queryFirstRow(
1398
            'SELECT email, auth_type, login
1399
            FROM ' . prefixTable('users') . '
1400
            WHERE id = %i',
1401
            $post_user_id
1402
        );
1403
        if (DB::count() > 0 && empty($userData['email']) === false) {
1404
            // If user pwd is empty then generate a new one and send it to user
1405
            if (isset($post_user_password) === false || empty($post_user_password) === true) {
1406
                // Generate new password
1407
                $post_user_password = generateQuickPassword();
1408
            }
1409
1410
            // If LDAP enabled, then
1411
            // check that this password is correct
1412
            $continue = true;
1413
            if ($userData['auth_type'] === 'ldap' && (int) $SETTINGS['ldap_mode'] === 1) {
1414
                $continue = ldapCheckUserPassword(
1415
                    $userData['login'],
1416
                    $post_user_password,
1417
                    $SETTINGS
1418
                );
1419
            }
1420
1421
            if ($continue === true) {
1422
                // Only change if email is successfull
1423
                // GEnerate new keys
1424
                $userKeys = generateUserKeys($post_user_password);
1425
1426
                // load passwordLib library
1427
                $pwdlib = new SplClassLoader('PasswordLib', '../includes/libraries');
1428
                $pwdlib->register();
1429
                $pwdlib = new PasswordLib\PasswordLib();
1430
1431
                // Update user account
1432
                DB::update(
1433
                    prefixTable('users'),
1434
                    array(
1435
                        'special' => $post_special,
1436
                        'pw' => $pwdlib->createPasswordHash($post_user_password),
1437
                        'public_key' => $userKeys['public_key'],
1438
                        'private_key' => $userKeys['private_key'],
1439
                        'last_pw_change' => time(),
1440
                    ),
1441
                    'id = %i',
1442
                    $post_user_id
1443
                );
1444
1445
                // Return
1446
                return prepareExchangedData(
1447
                    array(
1448
                        'error' => false,
1449
                        'message' => '',
1450
                        'user_pwd' => $post_user_password,
1451
                        'user_email' => $userData['email'],
1452
                    ),
1453
                    'encode'
1454
                );
1455
            }
1456
            // Return error
1457
            return prepareExchangedData(
1458
                array(
1459
                    'error' => true,
1460
                    'message' => langHdl('no_email_set'),
1461
                    'debug' => '',
1462
                    'self_change' => $post_self_change,
1463
                ),
1464
                'encode'
1465
            );
1466
        }
1467
1468
        // Error
1469
        return prepareExchangedData(
1470
            array(
1471
                'error' => true,
1472
                'message' => langHdl('no_email_set'),
1473
                'debug' => '',
1474
            ),
1475
            'encode'
1476
        );
1477
    }
1478
    
1479
    return prepareExchangedData(
1480
        array(
1481
            'error' => true,
1482
            'message' => langHdl('error_no_user'),
1483
            'debug' => '',
1484
        ),
1485
        'encode'
1486
    );
1487
}
1488
1489
function sendMailToUser(
1490
    string $post_receipt,
1491
    string $post_body,
1492
    string $post_subject,
1493
    array $post_replace,
1494
    array $SETTINGS
1495
): string
1496
{
1497
    if (count($post_replace) > 0 && is_null($post_replace) === false) {
1498
        $post_body = str_replace(
1499
            array_keys($post_replace),
1500
            array_values($post_replace),
1501
            $post_body
1502
        );
1503
    }
1504
    $ret = sendEmail(
1505
        $post_subject,
1506
        $post_body,
1507
        $post_receipt,
1508
        $SETTINGS,
1509
        '',
1510
        false
1511
    );
1512
1513
    $ret = json_decode($ret, true);
1514
1515
    return prepareExchangedData(
1516
        array(
1517
            'error' => empty($ret['error']) === true ? false : true,
1518
            'message' => $ret['message'],
1519
        ),
1520
        'encode'
1521
    );
1522
}
1523
1524
function generateOneTimeCode(
1525
    int $post_user_id,
1526
    array $SETTINGS
1527
): string
1528
{
1529
    if (is_null($post_user_id) === false && isset($post_user_id) === true && empty($post_user_id) === false) {
1530
        // Get user info
1531
        $userData = DB::queryFirstRow(
1532
            'SELECT email, auth_type, login
1533
            FROM ' . prefixTable('users') . '
1534
            WHERE id = %i',
1535
            $post_user_id
1536
        );
1537
        if (DB::count() > 0 && empty($userData['email']) === false) {
1538
            // Generate pwd
1539
            $password = generateQuickPassword();
1540
1541
            // GEnerate new keys
1542
            $userKeys = generateUserKeys($password);
1543
1544
            // Save in DB
1545
            DB::update(
1546
                prefixTable('users'),
1547
                array(
1548
                    'public_key' => $userKeys['public_key'],
1549
                    'private_key' => $userKeys['private_key'],
1550
                    'special' => 'generate-keys',
1551
                ),
1552
                'id=%i',
1553
                $post_user_id
1554
            );
1555
1556
            return prepareExchangedData(
1557
                array(
1558
                    'error' => false,
1559
                    'message' => '',
1560
                    'userTemporaryCode' => $password,
1561
                ),
1562
                'encode'
1563
            );
1564
        }
1565
        
1566
        return prepareExchangedData(
1567
            array(
1568
                'error' => true,
1569
                'message' => langHdl('no_email_set'),
1570
            ),
1571
            'encode'
1572
        );
1573
    }
1574
        
1575
    return prepareExchangedData(
1576
        array(
1577
            'error' => true,
1578
            'message' => langHdl('error_no_user'),
1579
        ),
1580
        'encode'
1581
    );
1582
}
1583
1584
function startReEncryptingUserSharekeys(
1585
    int $post_user_id,
1586
    bool $post_self_change,
1587
    array $SETTINGS
1588
): string
1589
{
1590
    //$post_user_id = is_null($post_user_id) === true ? $_SESSION['user_id'] : $post_user_id;
1591
1592
    if (is_null($post_user_id) === false && isset($post_user_id) === true && empty($post_user_id) === false) {
1593
        // Check if user exists
1594
        DB::queryFirstRow(
1595
            'SELECT *
1596
            FROM ' . prefixTable('users') . '
1597
            WHERE id = %i',
1598
            $post_user_id
1599
        );
1600
        if (DB::count() > 0) {
1601
            // Include libraries
1602
            include_once $SETTINGS['cpassman_dir'] . '/sources/aes.functions.php';
1603
1604
            // CLear old sharekeys
1605
            if ($post_self_change === false) {
1606
                deleteUserObjetsKeys($post_user_id, $SETTINGS);
1607
            }
1608
1609
            // Continu with next step
1610
            return prepareExchangedData(
1611
                array(
1612
                    'error' => false,
1613
                    'message' => '',
1614
                    'step' => 'step1',
1615
                    'userId' => $post_user_id,
1616
                    'start' => 0,
1617
                    'self_change' => $post_self_change,
1618
                ),
1619
                'encode'
1620
            );
1621
        }
1622
        // Nothing to do
1623
        return prepareExchangedData(
1624
            array(
1625
                'error' => true,
1626
                'message' => langHdl('error_no_user'),
1627
            ),
1628
            'encode'
1629
        );
1630
    }
1631
1632
    return prepareExchangedData(
1633
        array(
1634
            'error' => true,
1635
            'message' => langHdl('error_no_user'),
1636
        ),
1637
        'encode'
1638
    );
1639
}
1640
1641
function continueReEncryptingUserSharekeys(
1642
    int $post_user_id,
1643
    bool $post_self_change,
1644
    string $post_action,
1645
    int $post_start,
1646
    int $post_length,
1647
    array $SETTINGS
1648
): string
1649
{
1650
    if (is_null($post_user_id) === false && isset($post_user_id) === true && empty($post_user_id) === false) {
1651
        // Check if user exists
1652
        $userInfo = DB::queryFirstRow(
1653
            'SELECT public_key
1654
            FROM ' . prefixTable('users') . '
1655
            WHERE id = %i',
1656
            $post_user_id
1657
        );
1658
        if (DB::count() > 0) {
1659
            // Include libraries
1660
            include_once $SETTINGS['cpassman_dir'] . '/sources/aes.functions.php';
1661
1662
            // WHAT STEP TO PERFORM?
1663
            if ($post_action === 'step0') {
1664
                // CLear old sharekeys
1665
                if ($post_self_change === false) {
1666
                    deleteUserObjetsKeys($post_user_id, $SETTINGS);
1667
                }
1668
1669
                $post_action = 'step1';
1670
            }
1671
1672
            // STEP 1 - ITEMS
1673
            if ($post_action === 'step1') {
1674
                $return = continueReEncryptingUserSharekeysStep1(
1675
                    $post_user_id,
1676
                    $post_self_change,
1677
                    $post_action,
1678
                    $post_start,
1679
                    $post_length,
1680
                    $userInfo['public_key'],
1681
                    $SETTINGS
1682
                );
1683
1684
                $next_start = $return['next_start'];
1685
                $post_action = $return['post_action'];
1686
            }
1687
1688
            // STEP 2 - LOGS
1689
            if ($post_action === 'step2') {
1690
                $return = continueReEncryptingUserSharekeysStep2(
1691
                    $post_user_id,
1692
                    $post_self_change,
1693
                    $post_action,
1694
                    $post_start,
1695
                    $post_length,
1696
                    $userInfo['public_key'],
1697
                    $SETTINGS
1698
                );
1699
1700
                $next_start = $return['next_start'];
1701
                $post_action = $return['post_action'];
1702
            }
1703
1704
            // STEP 3 - FIELDS
1705
            if ($post_action === 'step3') {
1706
                $return = continueReEncryptingUserSharekeysStep3(
1707
                    $post_user_id,
1708
                    $post_self_change,
1709
                    $post_action,
1710
                    $post_start,
1711
                    $post_length,
1712
                    $userInfo['public_key'],
1713
                    $SETTINGS
1714
                );
1715
1716
                $next_start = $return['next_start'];
1717
                $post_action = $return['post_action'];
1718
            }
1719
            
1720
            // STEP 4 - SUGGESTIONS
1721
            if ($post_action === 'step4') {
1722
                $return = continueReEncryptingUserSharekeysStep4(
1723
                    $post_user_id,
1724
                    $post_self_change,
1725
                    $post_action,
1726
                    $post_start,
1727
                    $post_length,
1728
                    $userInfo['public_key'],
1729
                    $SETTINGS
1730
                );
1731
1732
                $next_start = $return['next_start'];
1733
                $post_action = $return['post_action'];
1734
            }
1735
            
1736
            // STEP 5 - FILES
1737
            if ($post_action === 'step5') {
1738
                $return = continueReEncryptingUserSharekeysStep5(
1739
                    $post_user_id,
1740
                    $post_self_change,
1741
                    $post_action,
1742
                    $post_start,
1743
                    $post_length,
1744
                    $userInfo['public_key'],
1745
                    $SETTINGS
1746
                );
1747
1748
                $next_start = $return['next_start'];
1749
                $post_action = $return['post_action'];
1750
            }
1751
            
1752
            // STEP 6 - PERSONAL ITEMS
1753
            if ($post_action === 'step6') {
1754
                $return = continueReEncryptingUserSharekeysStep6(
1755
                    $post_user_id,
1756
                    $post_self_change,
1757
                    $post_action,
1758
                    $post_start,
1759
                    $post_length,
1760
                    $userInfo['public_key'],
1761
                    $SETTINGS
1762
                );
1763
1764
                $next_start = $return['next_start'];
1765
                $post_action = $return['post_action'];
1766
            }
1767
1768
            // Continu with next step
1769
            return prepareExchangedData(
1770
                array(
1771
                    'error' => false,
1772
                    'message' => '',
1773
                    'step' => $post_action,
1774
                    'start' => isset($next_start) === true ? $next_start : 0,
1775
                    'userId' => $post_user_id,
1776
                    'self_change' => $post_self_change,
1777
                ),
1778
                'encode'
1779
            );
1780
        }
1781
        
1782
        // Nothing to do
1783
        return prepareExchangedData(
1784
            array(
1785
                'error' => false,
1786
                'message' => '',
1787
                'step' => 'finished',
1788
                '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' => true,
1800
            'message' => langHdl('error_no_user'),
1801
            'extra' => 'On est ici '.$post_user_id,
1802
        ),
1803
        'encode'
1804
    );
1805
}
1806
1807
function continueReEncryptingUserSharekeysStep1(
1808
    int $post_user_id,
1809
    bool $post_self_change,
1810
    string $post_action,
1811
    int $post_start,
1812
    int $post_length,
1813
    string $user_public_key,
1814
    array $SETTINGS
1815
): array 
1816
{
1817
    // Loop on items
1818
    $rows = DB::query(
1819
        'SELECT id, pw
1820
        FROM ' . prefixTable('items') . '
1821
        WHERE perso = 0
1822
        LIMIT ' . $post_start . ', ' . $post_length
1823
    );
1824
    foreach ($rows as $record) {
1825
        // Get itemKey from current user
1826
        $currentUserKey = DB::queryFirstRow(
1827
            'SELECT share_key, increment_id
1828
            FROM ' . prefixTable('sharekeys_items') . '
1829
            WHERE object_id = %i AND user_id = %i',
1830
            $record['id'],
1831
            $_SESSION['user_id']
1832
        );
1833
        if (count($currentUserKey) === 0) continue;
1834
1835
        // Decrypt itemkey with admin key
1836
        $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $_SESSION['user']['private_key']);
1837
        
1838
        // Encrypt Item key
1839
        $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
1840
        
1841
        // Save the key in DB
1842
        if ($post_self_change === false) {
1843
            DB::insert(
1844
                prefixTable('sharekeys_items'),
1845
                array(
1846
                    'object_id' => (int) $record['id'],
1847
                    'user_id' => (int) $post_user_id,
1848
                    'share_key' => $share_key_for_item,
1849
                )
1850
            );
1851
        } else {
1852
            // Get itemIncrement from selected user
1853
            if ((int) $post_user_id !== (int) $_SESSION['user_id']) {
1854
                $currentUserKey = DB::queryFirstRow(
1855
                    'SELECT increment_id
1856
                    FROM ' . prefixTable('sharekeys_items') . '
1857
                    WHERE object_id = %i AND user_id = %i',
1858
                    $record['id'],
1859
                    $post_user_id
1860
                );
1861
1862
                if (DB::count() > 0) {
1863
                    // NOw update
1864
                    DB::update(
1865
                        prefixTable('sharekeys_items'),
1866
                        array(
1867
                            'share_key' => $share_key_for_item,
1868
                        ),
1869
                        'increment_id = %i',
1870
                        $currentUserKey['increment_id']
1871
                    );
1872
                } else {
1873
                    DB::insert(
1874
                        prefixTable('sharekeys_items'),
1875
                        array(
1876
                            'object_id' => (int) $record['id'],
1877
                            'user_id' => (int) $post_user_id,
1878
                            'share_key' => $share_key_for_item,
1879
                        )
1880
                    );
1881
                }
1882
            }
1883
        }
1884
    }
1885
1886
    // SHould we change step?
1887
    DB::query(
1888
        'SELECT *
1889
        FROM ' . prefixTable('items') . '
1890
        WHERE perso = 0'
1891
    );
1892
1893
    $next_start = (int) $post_start + (int) $post_length;
1894
    return [
1895
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
1896
        'post_action' => $next_start > DB::count() ? 'step2' : 'step1',
1897
    ];
1898
}
1899
1900
function continueReEncryptingUserSharekeysStep2(
1901
    int $post_user_id,
1902
    bool $post_self_change,
1903
    string $post_action,
1904
    int $post_start,
1905
    int $post_length,
1906
    string $user_public_key,
1907
    array $SETTINGS
1908
): array
1909
{
1910
    // Loop on logs
1911
    $rows = DB::query(
1912
        'SELECT increment_id
1913
        FROM ' . prefixTable('log_items') . '
1914
        WHERE raison LIKE "at_pw :%" AND encryption_type = "teampass_aes"
1915
        LIMIT ' . $post_start . ', ' . $post_length
1916
    );
1917
    foreach ($rows as $record) {
1918
        // Get itemKey from current user
1919
        $currentUserKey = DB::queryFirstRow(
1920
            'SELECT share_key
1921
            FROM ' . prefixTable('sharekeys_logs') . '
1922
            WHERE object_id = %i AND user_id = %i',
1923
            $record['increment_id'],
1924
            $_SESSION['user_id']
1925
        );
1926
1927
        // Decrypt itemkey with admin key
1928
        $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $_SESSION['user']['private_key']);
1929
1930
        // Encrypt Item key
1931
        $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
1932
1933
        // Save the key in DB
1934
        if ($post_self_change === false) {
1935
            DB::insert(
1936
                prefixTable('sharekeys_logs'),
1937
                array(
1938
                    'object_id' => (int) $record['increment_id'],
1939
                    'user_id' => (int) $post_user_id,
1940
                    'share_key' => $share_key_for_item,
1941
                )
1942
            );
1943
        } else {
1944
            // Get itemIncrement from selected user
1945
            if ((int) $post_user_id !== (int) $_SESSION['user_id']) {
1946
                $currentUserKey = DB::queryFirstRow(
1947
                    'SELECT increment_id
1948
                    FROM ' . prefixTable('sharekeys_items') . '
1949
                    WHERE object_id = %i AND user_id = %i',
1950
                    $record['id'],
1951
                    $post_user_id
1952
                );
1953
            }
1954
1955
            // NOw update
1956
            DB::update(
1957
                prefixTable('sharekeys_logs'),
1958
                array(
1959
                    'share_key' => $share_key_for_item,
1960
                ),
1961
                'increment_id = %i',
1962
                $currentUserKey['increment_id']
1963
            );
1964
        }
1965
    }
1966
1967
    // SHould we change step?
1968
    DB::query(
1969
        'SELECT increment_id
1970
        FROM ' . prefixTable('log_items') . '
1971
        WHERE raison LIKE "at_pw :%" AND encryption_type = "teampass_aes"'
1972
    );
1973
1974
    $next_start = (int) $post_start + (int) $post_length;
1975
    return [
1976
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
1977
        'post_action' => $next_start > DB::count() ? 'step3' : 'step2',
1978
    ];
1979
}
1980
1981
function continueReEncryptingUserSharekeysStep3(
1982
    int $post_user_id,
1983
    bool $post_self_change,
1984
    string $post_action,
1985
    int $post_start,
1986
    int $post_length,
1987
    string $user_public_key,
1988
    array $SETTINGS
1989
): array
1990
{
1991
    // Loop on fields
1992
    $rows = DB::query(
1993
        'SELECT id
1994
        FROM ' . prefixTable('categories_items') . '
1995
        WHERE encryption_type = "teampass_aes"
1996
        LIMIT ' . $post_start . ', ' . $post_length
1997
    );
1998
    foreach ($rows as $record) {
1999
        // Get itemKey from current user
2000
        $currentUserKey = DB::queryFirstRow(
2001
            'SELECT share_key
2002
            FROM ' . prefixTable('sharekeys_fields') . '
2003
            WHERE object_id = %i AND user_id = %i',
2004
            $record['id'],
2005
            $_SESSION['user_id']
2006
        );
2007
2008
        // Decrypt itemkey with admin key
2009
        $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $_SESSION['user']['private_key']);
2010
2011
        // Encrypt Item key
2012
        $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
2013
2014
        // Save the key in DB
2015
        if ($post_self_change === false) {
2016
            DB::insert(
2017
                prefixTable('sharekeys_fields'),
2018
                array(
2019
                    'object_id' => (int) $record['id'],
2020
                    'user_id' => (int) $post_user_id,
2021
                    'share_key' => $share_key_for_item,
2022
                )
2023
            );
2024
        } else {
2025
            // Get itemIncrement from selected user
2026
            if ((int) $post_user_id !== (int) $_SESSION['user_id']) {
2027
                $currentUserKey = DB::queryFirstRow(
2028
                    'SELECT increment_id
2029
                    FROM ' . prefixTable('sharekeys_items') . '
2030
                    WHERE object_id = %i AND user_id = %i',
2031
                    $record['id'],
2032
                    $post_user_id
2033
                );
2034
            }
2035
2036
            // NOw update
2037
            DB::update(
2038
                prefixTable('sharekeys_fields'),
2039
                array(
2040
                    'share_key' => $share_key_for_item,
2041
                ),
2042
                'increment_id = %i',
2043
                $currentUserKey['increment_id']
2044
            );
2045
        }
2046
    }
2047
2048
    // SHould we change step?
2049
    DB::query(
2050
        'SELECT *
2051
        FROM ' . prefixTable('categories_items') . '
2052
        WHERE encryption_type = "teampass_aes"'
2053
    );
2054
2055
    $next_start = (int) $post_start + (int) $post_length;
2056
    return [
2057
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2058
        'post_action' => $next_start > DB::count() ? 'step4' : 'step3',
2059
    ];
2060
}
2061
2062
function continueReEncryptingUserSharekeysStep4(
2063
    int $post_user_id,
2064
    bool $post_self_change,
2065
    string $post_action,
2066
    int $post_start,
2067
    int $post_length,
2068
    string $user_public_key,
2069
    array $SETTINGS
2070
): array
2071
{
2072
    // Loop on suggestions
2073
    $rows = DB::query(
2074
        'SELECT id
2075
        FROM ' . prefixTable('suggestion') . '
2076
        LIMIT ' . $post_start . ', ' . $post_length
2077
    );
2078
    foreach ($rows as $record) {
2079
        // Get itemKey from current user
2080
        $currentUserKey = DB::queryFirstRow(
2081
            'SELECT share_key
2082
            FROM ' . prefixTable('sharekeys_suggestions') . '
2083
            WHERE object_id = %i AND user_id = %i',
2084
            $record['id'],
2085
            $_SESSION['user_id']
2086
        );
2087
2088
        // Decrypt itemkey with admin key
2089
        $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $_SESSION['user']['private_key']);
2090
2091
        // Encrypt Item key
2092
        $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
2093
2094
        // Save the key in DB
2095
        if ($post_self_change === false) {
2096
            DB::insert(
2097
                prefixTable('sharekeys_suggestions'),
2098
                array(
2099
                    'object_id' => (int) $record['id'],
2100
                    'user_id' => (int) $post_user_id,
2101
                    'share_key' => $share_key_for_item,
2102
                )
2103
            );
2104
        } else {
2105
            // Get itemIncrement from selected user
2106
            if ((int) $post_user_id !== (int) $_SESSION['user_id']) {
2107
                $currentUserKey = DB::queryFirstRow(
2108
                    'SELECT increment_id
2109
                    FROM ' . prefixTable('sharekeys_items') . '
2110
                    WHERE object_id = %i AND user_id = %i',
2111
                    $record['id'],
2112
                    $post_user_id
2113
                );
2114
            }
2115
2116
            // NOw update
2117
            DB::update(
2118
                prefixTable('sharekeys_suggestions'),
2119
                array(
2120
                    'share_key' => $share_key_for_item,
2121
                ),
2122
                'increment_id = %i',
2123
                $currentUserKey['increment_id']
2124
            );
2125
        }
2126
    }
2127
2128
    // SHould we change step?
2129
    DB::query(
2130
        'SELECT *
2131
        FROM ' . prefixTable('suggestion')
2132
    );
2133
2134
    $next_start = (int) $post_start + (int) $post_length;
2135
    return [
2136
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2137
        'post_action' => $next_start > DB::count() ? 'step5' : 'step4',
2138
    ];
2139
}
2140
2141
function continueReEncryptingUserSharekeysStep5(
2142
    int $post_user_id,
2143
    bool $post_self_change,
2144
    string $post_action,
2145
    int $post_start,
2146
    int $post_length,
2147
    string $user_public_key,
2148
    array $SETTINGS
2149
): array
2150
{
2151
    // Loop on files
2152
    $rows = DB::query(
2153
        'SELECT id
2154
        FROM ' . prefixTable('files') . '
2155
        WHERE status = "' . TP_ENCRYPTION_NAME . '"
2156
        LIMIT ' . $post_start . ', ' . $post_length
2157
    ); //aes_encryption
2158
    foreach ($rows as $record) {
2159
        // Get itemKey from current user
2160
        $currentUserKey = DB::queryFirstRow(
2161
            'SELECT share_key
2162
            FROM ' . prefixTable('sharekeys_files') . '
2163
            WHERE object_id = %i AND user_id = %i',
2164
            $record['id'],
2165
            $_SESSION['user_id']
2166
        );
2167
2168
        // Decrypt itemkey with admin key
2169
        $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $_SESSION['user']['private_key']);
2170
2171
        // Encrypt Item key
2172
        $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
2173
2174
        // Save the key in DB
2175
        if ($post_self_change === false) {
2176
            DB::insert(
2177
                prefixTable('sharekeys_files'),
2178
                array(
2179
                    'object_id' => (int) $record['id'],
2180
                    'user_id' => (int) $post_user_id,
2181
                    'share_key' => $share_key_for_item,
2182
                )
2183
            );
2184
        } else {
2185
            // Get itemIncrement from selected user
2186
            if ((int) $post_user_id !== (int) $_SESSION['user_id']) {
2187
                $currentUserKey = DB::queryFirstRow(
2188
                    'SELECT increment_id
2189
                    FROM ' . prefixTable('sharekeys_items') . '
2190
                    WHERE object_id = %i AND user_id = %i',
2191
                    $record['id'],
2192
                    $post_user_id
2193
                );
2194
            }
2195
2196
            // NOw update
2197
            DB::update(
2198
                prefixTable('sharekeys_files'),
2199
                array(
2200
                    'share_key' => $share_key_for_item,
2201
                ),
2202
                'increment_id = %i',
2203
                $currentUserKey['increment_id']
2204
            );
2205
        }
2206
    }
2207
2208
    // SHould we change step?
2209
    DB::query(
2210
        'SELECT *
2211
        FROM ' . prefixTable('files') . '
2212
        WHERE status = "' . TP_ENCRYPTION_NAME . '"'
2213
    );
2214
2215
    $next_start = (int) $post_start + (int) $post_length;
2216
    return [
2217
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2218
        'post_action' => $next_start > DB::count() ? 'step6' : 'step5',
2219
    ];
2220
}
2221
2222
function continueReEncryptingUserSharekeysStep6(
2223
    int $post_user_id,
2224
    bool $post_self_change,
2225
    string $post_action,
2226
    int $post_start,
2227
    int $post_length,
2228
    string $user_public_key,
2229
    array $SETTINGS
2230
): array
2231
{
2232
    // IF USER IS NOT THE SAME
2233
    if ((int) $post_user_id === (int) $_SESSION['user_id']) {
2234
        return [
2235
            'next_start' => 0,
2236
            'post_action' => 'finished',
2237
        ];
2238
    }
2239
    
2240
    // Loop on persoanl items
2241
    if (count($_SESSION['personal_folders']) > 0) {
2242
        $rows = DB::query(
2243
            'SELECT id, pw
2244
            FROM ' . prefixTable('items') . '
2245
            WHERE perso = 1 AND id_tree IN %ls
2246
            LIMIT ' . $post_start . ', ' . $post_length,
2247
            $_SESSION['personal_folders']
2248
        );
2249
        foreach ($rows as $record) {
2250
            // Get itemKey from current user
2251
            $currentUserKey = DB::queryFirstRow(
2252
                'SELECT share_key, increment_id
2253
                FROM ' . prefixTable('sharekeys_items') . '
2254
                WHERE object_id = %i AND user_id = %i',
2255
                $record['id'],
2256
                $_SESSION['user_id']
2257
            );
2258
2259
            // Decrypt itemkey with admin key
2260
            $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $_SESSION['user']['private_key']);
2261
2262
            // Encrypt Item key
2263
            $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
2264
2265
            // Save the key in DB
2266
            if ($post_self_change === false) {
2267
                DB::insert(
2268
                    prefixTable('sharekeys_items'),
2269
                    array(
2270
                        'object_id' => (int) $record['id'],
2271
                        'user_id' => (int) $post_user_id,
2272
                        'share_key' => $share_key_for_item,
2273
                    )
2274
                );
2275
            } else {
2276
                // Get itemIncrement from selected user
2277
                if ((int) $post_user_id !== (int) $_SESSION['user_id']) {
2278
                    $currentUserKey = DB::queryFirstRow(
2279
                        'SELECT increment_id
2280
                        FROM ' . prefixTable('sharekeys_items') . '
2281
                        WHERE object_id = %i AND user_id = %i',
2282
                        $record['id'],
2283
                        $post_user_id
2284
                    );
2285
                }
2286
2287
                // NOw update
2288
                DB::update(
2289
                    prefixTable('sharekeys_items'),
2290
                    array(
2291
                        'share_key' => $share_key_for_item,
2292
                    ),
2293
                    'increment_id = %i',
2294
                    $currentUserKey['increment_id']
2295
                );
2296
            }
2297
        }
2298
    }
2299
2300
    // SHould we change step?
2301
    DB::query(
2302
        'SELECT *
2303
        FROM ' . prefixTable('items') . '
2304
        WHERE perso = 0'
2305
    );
2306
2307
    $next_start = (int) $post_start + (int) $post_length;
2308
    return [
2309
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2310
        'post_action' => $next_start > DB::count() ? 'finished' : 'step6',
2311
    ];
2312
}
2313
2314
function migrateTo3_DoUserPersonalItemsEncryption(
2315
    int $post_user_id,
2316
    int $post_start,
2317
    int $post_length,
2318
    string $post_user_psk,
2319
    array $SETTINGS
2320
) {
2321
    $next_step = '';
2322
2323
    if (is_null($post_user_id) === false && isset($post_user_id) === true && empty($post_user_id) === false) {
2324
        // Check if user exists
2325
        $userInfo = DB::queryFirstRow(
2326
            'SELECT public_key, encrypted_psk
2327
            FROM ' . prefixTable('users') . '
2328
            WHERE id = %i',
2329
            $post_user_id
2330
        );
2331
        if (DB::count() > 0) {
2332
            // check if psk is correct.
2333
            if (empty($userInfo['encrypted_psk']) === false) {
2334
                $user_key_encoded = defuse_validate_personal_key(
2335
                    $post_user_psk,
2336
                    $userInfo['encrypted_psk']
2337
                );
2338
2339
                if (strpos($user_key_encoded, "Error ") !== false) {
2340
                    return prepareExchangedData(
2341
                        array(
2342
                            'error' => true,
2343
                            'message' => langHdl('bad_psk'),
2344
                        ),
2345
                        'encode'
2346
                    );
2347
                }
2348
2349
                // Loop on persoanl items
2350
                $rows = DB::query(
2351
                    'SELECT id, pw
2352
                    FROM ' . prefixTable('items') . '
2353
                    WHERE perso = 1 AND id_tree IN %ls
2354
                    LIMIT ' . $post_start . ', ' . $post_length,
2355
                    $_SESSION['personal_folders']
2356
                );
2357
                $countUserPersonalItems = DB::count();
2358
                foreach ($rows as $record) {
2359
                    if ($record['encryption_type'] !== 'teampass_aes') {
2360
                        // Decrypt with Defuse
2361
                        $passwd = cryption(
2362
                            $record['pw'],
2363
                            $user_key_encoded,
2364
                            'decrypt',
2365
                            $SETTINGS
2366
                        );
2367
2368
                        // Encrypt with Object Key
2369
                        $cryptedStuff = doDataEncryption($passwd['string']);
2370
2371
                        // Store new password in DB
2372
                        DB::update(
2373
                            prefixTable('items'),
2374
                            array(
2375
                                'pw' => $cryptedStuff['encrypted'],
2376
                                'encryption_type' => 'teampass_aes',
2377
                            ),
2378
                            'id = %i',
2379
                            $record['id']
2380
                        );
2381
2382
                        // Insert in DB the new object key for this item by user
2383
                        DB::insert(
2384
                            prefixTable('sharekeys_items'),
2385
                            array(
2386
                                'object_id' => (int) $record['id'],
2387
                                'user_id' => (int) $post_user_id,
2388
                                'share_key' => encryptUserObjectKey($cryptedStuff['objectKey'], $userInfo['public_key']),
2389
                            )
2390
                        );
2391
2392
2393
                        // Does this item has Files?
2394
                        // Loop on files
2395
                        $rows = DB::query(
2396
                            'SELECT id, file
2397
                            FROM ' . prefixTable('files') . '
2398
                            WHERE status != %s
2399
                            AND id_item = %i',
2400
                            TP_ENCRYPTION_NAME,
2401
                            $record['id']
2402
                        );
2403
                        //aes_encryption
2404
                        foreach ($rows as $record2) {
2405
                            // Now decrypt the file
2406
                            prepareFileWithDefuse(
2407
                                'decrypt',
2408
                                $SETTINGS['path_to_upload_folder'] . '/' . $record2['file'],
2409
                                $SETTINGS['path_to_upload_folder'] . '/' . $record2['file'] . '.delete',
2410
                                $SETTINGS,
2411
                                $post_user_psk
2412
                            );
2413
2414
                            // Encrypt the file
2415
                            $encryptedFile = encryptFile($record2['file'] . '.delete', $SETTINGS['path_to_upload_folder']);
2416
2417
                            DB::update(
2418
                                prefixTable('files'),
2419
                                array(
2420
                                    'file' => $encryptedFile['fileHash'],
2421
                                    'status' => TP_ENCRYPTION_NAME,
2422
                                ),
2423
                                'id = %i',
2424
                                $record2['id']
2425
                            );
2426
2427
                            // Save key
2428
                            DB::insert(
2429
                                prefixTable('sharekeys_files'),
2430
                                array(
2431
                                    'object_id' => (int) $record2['id'],
2432
                                    'user_id' => (int) $_SESSION['user_id'],
2433
                                    'share_key' => encryptUserObjectKey($encryptedFile['objectKey'], $_SESSION['user']['public_key']),
2434
                                )
2435
                            );
2436
2437
                            // Unlink original file
2438
                            unlink($SETTINGS['path_to_upload_folder'] . '/' . $record2['file']);
2439
                        }
2440
                    }
2441
                }
2442
2443
                // SHould we change step?
2444
                $next_start = (int) $post_start + (int) $post_length;
2445
                if ($next_start > $countUserPersonalItems) {
2446
                    // Now update user
2447
                    DB::update(
2448
                        prefixTable('users'),
2449
                        array(
2450
                            'special' => 'none',
2451
                            'upgrade_needed' => 0,
2452
                            'encrypted_psk' => '',
2453
                        ),
2454
                        'id = %i',
2455
                        $post_user_id
2456
                    );
2457
2458
                    $next_step = 'finished';
2459
                    $next_start = 0;
2460
                }
2461
2462
                // Continu with next step
2463
                return prepareExchangedData(
2464
                    array(
2465
                        'error' => false,
2466
                        'message' => '',
2467
                        'step' => $next_step,
2468
                        'start' => $next_start,
2469
                        'userId' => $post_user_id
2470
                    ),
2471
                    'encode'
2472
                );
2473
            }
2474
        }
2475
        
2476
        // Nothing to do
2477
        return prepareExchangedData(
2478
            array(
2479
                'error' => true,
2480
                'message' => langHdl('error_no_user'),
2481
            ),
2482
            'encode'
2483
        );
2484
    }
2485
    
2486
    // Nothing to do
2487
    return prepareExchangedData(
2488
        array(
2489
            'error' => true,
2490
            'message' => langHdl('error_no_user'),
2491
        ),
2492
        'encode'
2493
    );
2494
}
2495
2496
2497
function getUserInfo(
2498
    int $post_user_id,
2499
    string $post_fields,
2500
    array $SETTINGS
2501
)
2502
{
2503
    if (is_null($post_user_id) === false && isset($post_user_id) === true && empty($post_user_id) === false) {
2504
        // Get user info
2505
        $userData = DB::queryFirstRow(
2506
            'SELECT '.$post_fields.'
2507
            FROM ' . prefixTable('users') . '
2508
            WHERE id = %i',
2509
            $post_user_id
2510
        );
2511
        if (DB::count() > 0) {
2512
            return prepareExchangedData(
2513
                array(
2514
                    'error' => false,
2515
                    'message' => '',
2516
                    'queryResults' => $userData,
2517
                ),
2518
                'encode'
2519
            );
2520
        }
2521
    }
2522
    return prepareExchangedData(
2523
        array(
2524
            'error' => true,
2525
            'message' => langHdl('error_no_user'),
2526
        ),
2527
        'encode'
2528
    );
2529
}
2530
2531
function changeUserAuthenticationPassword(
2532
    int $post_user_id,
2533
    string $post_current_pwd,
2534
    string $post_new_pwd,
2535
    array $SETTINGS
2536
)
2537
{
2538
    if (is_null($post_user_id) === false && isset($post_user_id) === true && empty($post_user_id) === false) {
2539
        // Get user info
2540
        $userData = DB::queryFirstRow(
2541
            'SELECT auth_type, login, private_key
2542
            FROM ' . prefixTable('users') . '
2543
            WHERE id = %i',
2544
            $post_user_id
2545
        );
2546
        if (DB::count() > 0) {
2547
            // Now check if current password is correct
2548
            // For this, just check if it is possible to decrypt the privatekey
2549
            // And compare it to the one in session
2550
            $privateKey = decryptPrivateKey($post_current_pwd, $userData['private_key']);
2551
2552
            // Load superGlobals
2553
            include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
2554
            $superGlobal = new protect\SuperGlobal\SuperGlobal();
2555
2556
            if ($superGlobal->get('private_key', 'SESSION', 'user') === $privateKey) {
2557
                // Encrypt it with new password
2558
                $hashedPrivateKey = encryptPrivateKey($post_new_pwd, $privateKey);
2559
2560
                // Generate new hash for auth password
2561
                // load passwordLib library
2562
                $pwdlib = new SplClassLoader('PasswordLib', '../includes/libraries');
2563
                $pwdlib->register();
2564
                $pwdlib = new PasswordLib\PasswordLib();
2565
2566
                // Prepare variables
2567
                $newPw = $pwdlib->createPasswordHash($post_new_pwd);
2568
2569
                // Update user account
2570
                DB::update(
2571
                    prefixTable('users'),
2572
                    array(
2573
                        'private_key' => $hashedPrivateKey,
2574
                        'pw' => $newPw,
2575
                        'special' => 'none',
2576
                    ),
2577
                    'id = %i',
2578
                    $post_user_id
2579
                );
2580
2581
                $superGlobal->put('private_key', $privateKey, 'SESSION', 'user');
2582
2583
                return prepareExchangedData(
2584
                    array(
2585
                        'error' => false,
2586
                        'message' => langHdl('done'),'',
2587
                    ),
2588
                    'encode'
2589
                );
2590
            }
2591
            
2592
            // ERROR
2593
            return prepareExchangedData(
2594
                array(
2595
                    'error' => true,
2596
                    'message' => langHdl('bad_password'),
2597
                ),
2598
                'encode'
2599
            );
2600
        }
2601
    }
2602
        
2603
    return prepareExchangedData(
2604
        array(
2605
            'error' => true,
2606
            'message' => langHdl('error_no_user'),
2607
        ),
2608
        'encode'
2609
    );
2610
}
2611
2612
            
2613
function changeUserLDAPAuthenticationPassword(
2614
    int $post_user_id,
2615
    string $post_previous_pwd,
2616
    string $post_current_pwd,
2617
    array $SETTINGS
2618
)
2619
{
2620
    if (is_null($post_user_id) === false && isset($post_user_id) === true && empty($post_user_id) === false) {
2621
        // Get user info
2622
        $userData = DB::queryFirstRow(
2623
            'SELECT auth_type, login, private_key, special
2624
            FROM ' . prefixTable('users') . '
2625
            WHERE id = %i',
2626
            $post_user_id
2627
        );
2628
        
2629
        if (DB::count() > 0) {
2630
            // Now check if current password is correct (only if not ldap)
2631
            if ($userData['auth_type'] === 'ldap' && $userData['special'] === 'auth-pwd-change') {
2632
                // As it is a change for an LDAP user
2633
                
2634
                // Now check if current password is correct
2635
                // For this, just check if it is possible to decrypt the privatekey
2636
                // And compare it to the one in session
2637
                $privateKey = decryptPrivateKey($post_previous_pwd, $userData['private_key']);
2638
2639
                // Encrypt it with new password
2640
                $hashedPrivateKey = encryptPrivateKey($post_current_pwd, $privateKey);
2641
2642
                // Update user account
2643
                DB::update(
2644
                    prefixTable('users'),
2645
                    array(
2646
                        'private_key' => $hashedPrivateKey,
2647
                        'special' => 'none',
2648
                    ),
2649
                    'id = %i',
2650
                    $post_user_id
2651
                );
2652
2653
                // Load superGlobals
2654
                include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
2655
                $superGlobal = new protect\SuperGlobal\SuperGlobal();
2656
                $superGlobal->put('private_key', $privateKey, 'SESSION', 'user');
2657
2658
                return prepareExchangedData(
2659
                    array(
2660
                        'error' => false,
2661
                        'message' => langHdl('done'),'',
2662
                    ),
2663
                    'encode'
2664
                );
2665
            }
2666
2667
            // For this, just check if it is possible to decrypt the privatekey
2668
            // And try to decrypt one existing key
2669
            $privateKey = decryptPrivateKey($post_previous_pwd, $userData['private_key']);
2670
2671
            if (empty($privateKey) === true) {
2672
                return prepareExchangedData(
2673
                    array(
2674
                        'error' => true,
2675
                        'message' => langHdl('password_is_not_correct'),
2676
                    ),
2677
                    'encode'
2678
                );
2679
            }
2680
2681
            // Test if possible to decvrypt one key
2682
            // Get one item
2683
            $record = DB::queryFirstRow(
2684
                'SELECT id, pw
2685
                FROM ' . prefixTable('items') . '
2686
                WHERE perso = 0'
2687
            );
2688
2689
            // Get itemKey from current user
2690
            $currentUserKey = DB::queryFirstRow(
2691
                'SELECT share_key, increment_id
2692
                FROM ' . prefixTable('sharekeys_items') . '
2693
                WHERE object_id = %i AND user_id = %i',
2694
                $record['id'],
2695
                $post_user_id
2696
            );
2697
2698
            if (count($currentUserKey) > 0) {
2699
                // Decrypt itemkey with user key
2700
                // use old password to decrypt private_key
2701
                $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $privateKey);
2702
                
2703
                if (empty(base64_decode($itemKey)) === false) {
2704
                    // GOOD password
2705
                    // Encrypt it with current password
2706
                    $hashedPrivateKey = encryptPrivateKey($post_current_pwd, $privateKey);
2707
                    
2708
                    // Update user account
2709
                    DB::update(
2710
                        prefixTable('users'),
2711
                        array(
2712
                            'private_key' => $hashedPrivateKey,
2713
                            'special' => 'none',
2714
                        ),
2715
                        'id = %i',
2716
                        $post_user_id
2717
                    );
2718
                    
2719
                    // Load superGlobals
2720
                    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
2721
                    $superGlobal = new protect\SuperGlobal\SuperGlobal();
2722
                    $superGlobal->put('private_key', $privateKey, 'SESSION', 'user');
2723
2724
                    return prepareExchangedData(
2725
                        array(
2726
                            'error' => false,
2727
                            'message' => langHdl('done'),
2728
                        ),
2729
                        'encode'
2730
                    );
2731
                }
2732
            }
2733
            
2734
            // ERROR
2735
            return prepareExchangedData(
2736
                array(
2737
                    'error' => true,
2738
                    'message' => langHdl('bad_password'),
2739
                ),
2740
                'encode'
2741
            );
2742
        }
2743
    }
2744
2745
    // ERROR
2746
    return prepareExchangedData(
2747
        array(
2748
            'error' => true,
2749
            'message' => langHdl('error_no_user'),
2750
        ),
2751
        'encode'
2752
    );
2753
}
2754
2755
2756
function increaseSessionDuration(
2757
    int $duration
2758
): string
2759
{
2760
    // check if session is not already expired.
2761
    if ($_SESSION['sessionDuration'] > time()) {
2762
        // Calculate end of session
2763
        $_SESSION['sessionDuration'] = (int) ($_SESSION['sessionDuration'] + $duration);
2764
        // Update table
2765
        DB::update(
2766
            prefixTable('users'),
2767
            array(
2768
                'session_end' => $_SESSION['sessionDuration'],
2769
            ),
2770
            'id = %i',
2771
            $_SESSION['user_id']
2772
        );
2773
        // Return data
2774
        return '[{"new_value":"' . $_SESSION['sessionDuration'] . '"}]';
2775
    }
2776
    
2777
    return '[{"new_value":"expired"}]';
2778
}