Passed
Pull Request — master (#4412)
by Nils
06:38
created

changePrivateKeyEncryptionPassword()   C

Complexity

Conditions 11
Paths 17

Size

Total Lines 94
Code Lines 53

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
cc 11
eloc 53
c 2
b 0
f 1
nc 17
nop 5
dl 0
loc 94
rs 6.8787

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Teampass - a collaborative passwords manager.
7
 * ---
8
 * This file is part of the TeamPass project.
9
 * 
10
 * TeamPass is free software: you can redistribute it and/or modify it
11
 * under the terms of the GNU General Public License as published by
12
 * the Free Software Foundation, version 3 of the License.
13
 * 
14
 * TeamPass is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
 * GNU General Public License for more details.
18
 * 
19
 * You should have received a copy of the GNU General Public License
20
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21
 * 
22
 * Certain components of this file may be under different licenses. For
23
 * details, see the `licenses` directory or individual file headers.
24
 * ---
25
 * @file      main.queries.php
26
 * @author    Nils Laumaillé ([email protected])
27
 * @copyright 2009-2024 Teampass.net
28
 * @license   GPL-3.0
29
 * @see       https://www.teampass.net
30
 */
31
32
use TeampassClasses\PasswordManager\PasswordManager;
33
use TeampassClasses\SessionManager\SessionManager;
34
use Symfony\Component\HttpFoundation\Request as SymfonyRequest;
35
use TeampassClasses\Language\Language;
36
use Hackzilla\PasswordGenerator\Generator\ComputerPasswordGenerator;
37
use Hackzilla\PasswordGenerator\RandomGenerator\Php7RandomGenerator;
38
use RobThree\Auth\TwoFactorAuth;
39
use EZimuel\PHPSecureSession;
40
use TeampassClasses\PerformChecks\PerformChecks;
41
use TeampassClasses\ConfigManager\ConfigManager;
42
use TeampassClasses\EmailService\EmailService;
43
use TeampassClasses\EmailService\EmailSettings;
44
45
// Load functions
46
require_once 'main.functions.php';
47
48
loadClasses('DB');
49
$session = SessionManager::getSession();
50
$request = SymfonyRequest::createFromGlobals();
51
$lang = new Language($session->get('user-language') ?? 'english');
52
53
// TODO : ajouter un check sue l'envoi de la key
54
55
// Load config
56
$configManager = new ConfigManager();
57
$SETTINGS = $configManager->getAllSettings();
58
59
// Do checks
60
// Instantiate the class with posted data
61
$checkUserAccess = new PerformChecks(
62
    dataSanitizer(
63
        [
64
            'type' => $request->request->get('type', '') !== '' ? htmlspecialchars($request->request->get('type')) : '',
65
        ],
66
        [
67
            'type' => 'trim|escape',
68
        ],
69
    ),
70
    [
71
        'user_id' => returnIfSet($session->get('user-id'), null),
72
        'user_key' => returnIfSet($session->get('key'), null),
73
    ]
74
);
75
// Handle the case
76
echo $checkUserAccess->caseHandler();
77
if (
78
    ($checkUserAccess->userAccessPage('home') === false ||
79
    $checkUserAccess->checkSession() === false)
80
    && in_array(filter_input(INPUT_POST, 'type', FILTER_SANITIZE_FULL_SPECIAL_CHARS), ['get_teampass_settings', 'ga_generate_qr']) === false
81
) {
82
    // Not allowed page
83
    $session->set('system-error_code', ERR_NOT_ALLOWED);
84
    include $SETTINGS['cpassman_dir'] . '/error.php';
85
    exit;
86
}
87
88
// Define Timezone
89
date_default_timezone_set(isset($SETTINGS['timezone']) === true ? $SETTINGS['timezone'] : 'UTC');
90
set_time_limit(600);
91
92
// DO CHECKS
93
$post_type = filter_input(INPUT_POST, 'type', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
94
if (
95
    isset($post_type) === true
96
    && ($post_type === 'ga_generate_qr'
97
        || $post_type === 'get_teampass_settings')
98
) {
99
    // continue
100
    mainQuery($SETTINGS);
101
} elseif (
102
    $session->has('user-id') && null !== $session->get('user-id')
103
    && $checkUserAccess->userAccessPage('home') === false
104
) {
105
    $session->set('system-error_code', ERR_NOT_ALLOWED); //not allowed page
106
    include __DIR__.'/../error.php';
107
    exit();
108
} elseif (($session->has('user-id') && null !== $session->get('user-id')
109
        && $session->get('key') !== null)
110
    || (isset($post_type) === true
111
        && null !== filter_input(INPUT_POST, 'data', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_FLAG_NO_ENCODE_QUOTES))
112
) {
113
    // continue
114
    mainQuery($SETTINGS);
115
} else {
116
    $session->set('system-error_code', ERR_NOT_ALLOWED); //not allowed page
117
    include __DIR__.'/../error.php';
118
    exit();
119
}
120
121
// Includes
122
include_once __DIR__.'/../sources/main.functions.php';
123
124
/**
125
 * Undocumented function.
126
 */
127
function mainQuery(array $SETTINGS)
128
{
129
    header('Content-type: text/html; charset=utf-8');
130
    header('Cache-Control: no-cache');
131
    error_reporting(E_ERROR);
132
133
    // Load libraries
134
    loadClasses('DB');
135
136
    // User's language loading
137
    $session = SessionManager::getSession();
138
    $lang = new Language($session->get('user-language') ?? 'english');
139
    $request = SymfonyRequest::createFromGlobals();
140
141
    // Prepare POST variables
142
    $inputData = dataSanitizer(
143
        [
144
            'type' => $request->request->filter('type', '', FILTER_SANITIZE_SPECIAL_CHARS),
145
            'data' => $request->request->filter('data', '', FILTER_SANITIZE_SPECIAL_CHARS),
146
            'key' => $request->request->filter('key', '', FILTER_SANITIZE_SPECIAL_CHARS),
147
            'type_category' => $request->request->filter('type_category', '', FILTER_SANITIZE_SPECIAL_CHARS),
148
        ],
149
        [
150
            'type' => 'trim|escape',
151
            'data' => 'trim|escape',
152
            'key' => 'trim|escape',
153
            'type_category' => 'trim|escape',
154
        ]
155
    );
156
    
157
    // Check KEY
158
    if (isValueSetNullEmpty($inputData['key']) === true) {
159
        echo prepareExchangedData(
160
            array(
161
                'error' => true,
162
                'message' => $lang->get('key_is_not_correct'),
163
            ),
164
            'encode',
165
            $inputData['key']
166
        );
167
        return false;
168
    }
169
    // decrypt and retreive data in JSON format
170
    $dataReceived = empty($inputData['data']) === false ? prepareExchangedData(
171
        $inputData['data'],
172
        'decode'
173
    ) : '';
174
    
175
    switch ($inputData['type_category']) {
176
        case 'action_password':
177
            echo passwordHandler($inputData['type'], $dataReceived, $SETTINGS);
178
            break;
179
180
        case 'action_user':
181
            echo userHandler($inputData['type'], $dataReceived, $SETTINGS, $inputData['key']);
182
            break;
183
184
        case 'action_mail':
185
            echo mailHandler($inputData['type'], $dataReceived, $SETTINGS);
186
            break;
187
188
        case 'action_key':
189
            // deepcode ignore ServerLeak: All cases handled by keyHandler return an encrypted string that is sent back to the client
190
            echo keyHandler($inputData['type'], $dataReceived, $SETTINGS);
191
            break;
192
193
        case 'action_system':
194
            echo systemHandler($inputData['type'], $dataReceived, $SETTINGS);
195
            break;
196
197
        case 'action_utils':
198
            echo utilsHandler($inputData['type'], $dataReceived, $SETTINGS);
199
            break;
200
    }
201
    
202
}
203
204
/**
205
 * Handler for all password tasks
206
 *
207
 * @param string $post_type
208
 * @param array|null|string $dataReceived
209
 * @param array $SETTINGS
210
 * @return string
211
 */
212
function passwordHandler(string $post_type, /*php8 array|null|string*/ $dataReceived, array $SETTINGS): string
213
{
214
    $session = SessionManager::getSession();
215
216
    switch ($post_type) {
217
        case 'change_pw'://action_password
218
            return changePassword(
219
                (string) filter_var($dataReceived['new_pw'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
220
                isset($dataReceived['current_pw']) === true ? (string) filter_var($dataReceived['current_pw'], FILTER_SANITIZE_FULL_SPECIAL_CHARS) : '',
221
                (int) filter_var($dataReceived['complexity'], FILTER_SANITIZE_NUMBER_INT),
222
                (string) filter_var($dataReceived['change_request'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
223
                (int) $session->get('user-id'),
224
                $SETTINGS
225
            );
226
227
        /*
228
         * Change user's authentication password
229
         */
230
        case 'change_user_auth_password'://action_password
231
            return changeUserAuthenticationPassword(
232
                (int) $session->get('user-id'),
233
                (string) filter_var($dataReceived['old_password'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
234
                (string) filter_var($dataReceived['new_password'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
235
                $SETTINGS
236
            );
237
238
        /*
239
         * User's authentication password in LDAP has changed
240
         */
241
        case 'change_user_ldap_auth_password'://action_password
242
            return /** @scrutinizer ignore-call */ changeUserLDAPAuthenticationPassword(
243
                (int) $session->get('user-id'),
244
                filter_var($dataReceived['previous_password'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
245
                filter_var($dataReceived['current_password'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
246
                $SETTINGS
247
            );
248
249
        /*
250
         * test_current_user_password_is_correct
251
         */
252
        case 'test_current_user_password_is_correct'://action_password
253
            return isUserPasswordCorrect(
254
                (int) $session->get('user-id'),
255
                (string) $dataReceived['password'],
256
                $SETTINGS
257
            );
258
259
        /*
260
         * Default case
261
         */
262
        default :
263
            return prepareExchangedData(
264
                array(
265
                    'error' => true,
266
                ),
267
                'encode'
268
            );
269
    }
270
}
271
272
/**
273
 * Handler for all user tasks
274
 *
275
 * @param string $post_type
276
 * @param array|null|string $dataReceived
277
 * @param array $SETTINGS
278
 * @param string $post_key
279
 * @return string
280
 */
281
function userHandler(string $post_type, array|null|string $dataReceived, array $SETTINGS, string $post_key): string
282
{
283
    $session = SessionManager::getSession();
284
285
    // List of post types allowed to all users
286
    $all_users_can_access = [
287
        'get_user_info',
288
        'increase_session_time',
289
        'generate_password',
290
        'refresh_list_items_seen',
291
        'ga_generate_qr',
292
        'user_get_session_time',
293
        'save_user_location'
294
    ];
295
296
    // Default values
297
    $filtered_user_id = $session->get('user-id');
298
299
    // User can't manage users and requested type is administrative.
300
    if ((int) $session->get('user-admin') !== 1 &&
301
        (int) $session->get('user-manager') !== 1 &&
302
        (int) $session->get('user-can_manage_all_users') !== 1 &&
303
        !in_array($post_type, $all_users_can_access)) {
304
305
        return prepareExchangedData(
306
            array(
307
                'error' => true,
308
            ),
309
            'encode'
310
        );
311
    }
312
313
    if (isset($dataReceived['user_id'])) {
314
        // Get info about user to modify
315
        $targetUserInfos = DB::queryfirstrow(
316
            'SELECT admin, gestionnaire, can_manage_all_users, isAdministratedByRole FROM ' . prefixTable('users') . '
317
            WHERE id = %i',
318
            $dataReceived['user_id']
319
        );
320
321
        if (
322
            // Administrator user
323
            (int) $session->get('user-admin') === 1
324
            // Manager of basic/ro users in this role
325
            || ((int) $session->get('user-manager') === 1
326
                && in_array($targetUserInfos['isAdministratedByRole'], $session->get('user-roles_array'))
327
                && (int) $targetUserInfos['admin'] !== 1
328
                && (int) $targetUserInfos['can_manage_all_users'] !== 1
329
                && (int) $targetUserInfos['gestionnaire'] !== 1)
330
            // Manager of all basic/ro users
331
            || ((int) $session->get('user-can_manage_all_users') === 1
332
                && (int) $targetUserInfos['admin'] !== 1
333
                && (int) $targetUserInfos['can_manage_all_users'] !== 1
334
                && (int) $targetUserInfos['gestionnaire'] !== 1)
335
        ) {
336
            // This user is allowed to modify other users.
337
            $filtered_user_id = $dataReceived['user_id'];
338
        }
339
    }
340
341
    switch ($post_type) {
342
        /*
343
        * Get info 
344
        */
345
        case 'get_user_info'://action_user
346
            return getUserInfo(
347
                (int) $filtered_user_id,
348
                (string) filter_var($dataReceived['fields'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
349
                $SETTINGS
350
            );
351
352
        /*
353
        * Increase the session time of User
354
        */
355
        case 'increase_session_time'://action_user
356
            return increaseSessionDuration(
357
                (int) filter_input(INPUT_POST, 'duration', FILTER_SANITIZE_NUMBER_INT)
358
            );
359
360
        /*
361
        * Generate a password generic
362
        */
363
        case 'generate_password'://action_user
364
            return generateGenericPassword(
365
                (int) filter_input(INPUT_POST, 'size', FILTER_SANITIZE_NUMBER_INT),
366
                (bool) filter_input(INPUT_POST, 'secure_pwd', FILTER_VALIDATE_BOOLEAN),
367
                (bool) filter_input(INPUT_POST, 'lowercase', FILTER_VALIDATE_BOOLEAN),
368
                (bool) filter_input(INPUT_POST, 'capitalize', FILTER_VALIDATE_BOOLEAN),
369
                (bool) filter_input(INPUT_POST, 'numerals', FILTER_VALIDATE_BOOLEAN),
370
                (bool) filter_input(INPUT_POST, 'symbols', FILTER_VALIDATE_BOOLEAN),
371
                $SETTINGS
372
            );
373
374
        /*
375
        * Refresh list of last items seen
376
        */
377
        case 'refresh_list_items_seen'://action_user
378
            if ($session->has('user-id') || (int) $session->get('user-id') && null !== $session->get('user-id') || (int) $session->get('user-id') > 0) {
379
                return refreshUserItemsSeenList(
380
                    $SETTINGS
381
                );
382
383
            } else {
384
                return json_encode(
385
                    array(
386
                        'error' => '',
387
                        'existing_suggestions' => 0,
388
                        'html_json' => '',
389
                    ),
390
                    JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
391
                );
392
            }
393
394
        /*
395
        * This will generate the QR Google Authenticator
396
        */
397
        case 'ga_generate_qr'://action_user
398
            return generateQRCode(
399
                (int) $filtered_user_id,
400
                (string) filter_var($dataReceived['demand_origin'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
401
                (string) filter_var($dataReceived['send_email'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
402
                (string) filter_var($dataReceived['login'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
403
                (string) filter_var($dataReceived['pwd'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
404
                (string) filter_var($dataReceived['token'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
405
                $SETTINGS
406
            );
407
408
        /*
409
        * This will set the user ready
410
        */
411
        case 'user_is_ready'://action_user
412
            return userIsReady(
413
                (int) $filtered_user_id,
414
                (string) $SETTINGS['cpassman_dir']
415
            );
416
417
        /*
418
        * This post type is used to check if the user session is still valid
419
        */
420
        case 'user_get_session_time'://action_user
421
            return userGetSessionTime(
422
                (int) $session->get('user-id'),
423
                (string) $SETTINGS['cpassman_dir'],
424
                (int) $SETTINGS['maximum_session_expiration_time'],
425
            );
426
427
        case 'save_user_location'://action_user
428
            return userSaveIp(
429
                (int) $session->get('user-id'),
430
                (string) filter_var($dataReceived['action'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
431
            );
432
433
        /*
434
        * Default case
435
        */
436
        default :
437
            return prepareExchangedData(
438
                array(
439
                    'error' => true,
440
                ),
441
                'encode'
442
            );
443
    }
444
}
445
446
/**
447
 * Handler for all mail tasks
448
 *
449
 * @param string $post_type
450
 * @param array|null|string $dataReceived
451
 * @param array $SETTINGS
452
 * @return string
453
 */
454
function mailHandler(string $post_type, /*php8 array|null|string */$dataReceived, array $SETTINGS): string
455
{
456
    $session = SessionManager::getSession();
457
458
    switch ($post_type) {
459
        /*
460
         * CASE
461
         * Send email
462
         */
463
        case 'mail_me'://action_mail
464
            // Get info about user to send email
465
            $data_user = DB::queryfirstrow(
466
                'SELECT admin, gestionnaire, can_manage_all_users, isAdministratedByRole FROM ' . prefixTable('users') . '
467
                WHERE email = %s',
468
                filter_var($dataReceived['receipt'], FILTER_SANITIZE_FULL_SPECIAL_CHARS)
469
            );
470
471
            // Unknown email address
472
            if (!$data_user) {
473
                return prepareExchangedData(
474
                    array(
475
                        'error' => true,
476
                    ),
477
                    'encode'
478
                );
479
            }
480
481
            // Only administrators and managers can send mails
482
            if (
483
                // Administrator user
484
                (int) $session->get('user-admin') === 1
485
                // Manager of basic/ro users in this role
486
                || ((int) $session->get('user-manager') === 1
487
                    && in_array($data_user['isAdministratedByRole'], $session->get('user-roles_array'))
488
                    && (int) $data_user['admin'] !== 1
489
                    && (int) $data_user['can_manage_all_users'] !== 1
490
                    && (int) $data_user['gestionnaire'] !== 1)
491
                // Manager of all basic/ro users
492
                || ((int) $session->get('user-can_manage_all_users') === 1
493
                    && (int) $data_user['admin'] !== 1
494
                    && (int) $data_user['can_manage_all_users'] !== 1
495
                    && (int) $data_user['gestionnaire'] !== 1)
496
            ) {
497
                return sendMailToUser(
498
                    filter_var($dataReceived['receipt'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
499
                    $dataReceived['body'],
500
                    (string) filter_var($dataReceived['subject'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
501
                    (array) filter_var_array(
502
                        $dataReceived['pre_replace'],
503
                        FILTER_SANITIZE_FULL_SPECIAL_CHARS
504
                    ),
505
                    true
506
                );
507
            }
508
509
            return prepareExchangedData(
510
                array(
511
                    'error' => true,
512
                ),
513
                'encode'
514
            );
515
        /*
516
        * Send emails not sent
517
        */
518
        case 'send_waiting_emails'://mail
519
            // Administrative task
520
            if ((int) $session->get('user-admin') !== 1) {
521
                return prepareExchangedData(
522
                    array(
523
                        'error' => true,
524
                    ),
525
                    'encode'
526
                );
527
            }
528
529
            sendEmailsNotSent(
530
                $SETTINGS
531
            );
532
            return prepareExchangedData(
533
                array(
534
                    'error' => false,
535
                    'message' => 'mail_sent',
536
                ),
537
                'encode'
538
            );
539
540
        /*
541
        * Default case
542
        */
543
        default :
544
            return prepareExchangedData(
545
                array(
546
                    'error' => true,
547
                ),
548
                'encode'
549
            );
550
    }
551
}
552
553
/**
554
 * Handler for all key related tasks
555
 *
556
 * @param string $post_type
557
 * @param array|null|string $dataReceived
558
 * @param array $SETTINGS
559
 * @return string
560
 */
561
function keyHandler(string $post_type, /*php8 array|null|string */$dataReceived, array $SETTINGS): string
562
{
563
    $session = SessionManager::getSession();
564
565
    // List of post types allowed to all users
566
    $all_users_can_access = [
567
        'change_private_key_encryption_password',
568
        'user_new_keys_generation',
569
        'user_recovery_keys_download',
570
    ];
571
572
    // Default values
573
    $filtered_user_id = $session->get('user-id');
574
575
    if (isset($dataReceived['user_id'])) {
576
        // Get info about user to modify
577
        $targetUserInfos = DB::queryfirstrow(
578
            'SELECT admin, gestionnaire, can_manage_all_users, isAdministratedByRole FROM ' . prefixTable('users') . '
579
            WHERE id = %i',
580
            $dataReceived['user_id']
581
        );
582
    
583
        if (
584
            // Administrator user
585
            (int) $session->get('user-admin') === 1
586
            // Manager of basic/ro users in this role
587
            || ((int) $session->get('user-manager') === 1
588
                && in_array($targetUserInfos['isAdministratedByRole'], $session->get('user-roles_array'))
589
                && (int) $targetUserInfos['admin'] !== 1
590
                && (int) $targetUserInfos['can_manage_all_users'] !== 1
591
                && (int) $targetUserInfos['gestionnaire'] !== 1)
592
            // Manager of all basic/ro users
593
            || ((int) $session->get('user-can_manage_all_users') === 1
594
                && (int) $targetUserInfos['admin'] !== 1
595
                && (int) $targetUserInfos['can_manage_all_users'] !== 1
596
                && (int) $targetUserInfos['gestionnaire'] !== 1)
597
        ) {
598
            // This user is allowed to modify other users.
599
            $filtered_user_id = $dataReceived['user_id'];
600
    
601
        } else if (!in_array($post_type, $all_users_can_access)) {
602
            // User can't manage users and requested type is administrative.
603
            return prepareExchangedData(
604
                array(
605
                    'error' => true,
606
                ),
607
                'encode'
608
            ); 
609
        }
610
    }
611
612
    switch ($post_type) {
613
        /*
614
         * Generate a temporary encryption key for user
615
         */
616
        case 'generate_temporary_encryption_key'://action_key
617
            return generateOneTimeCode(
618
                (int) filter_var($filtered_user_id, FILTER_SANITIZE_NUMBER_INT)
619
            );
620
621
        /*
622
         * user_sharekeys_reencryption_next
623
         */
624
        case 'user_sharekeys_reencryption_next'://action_key
625
            return continueReEncryptingUserSharekeys(
626
                (int) filter_var($filtered_user_id, FILTER_SANITIZE_NUMBER_INT),
627
                (bool) filter_var($dataReceived['self_change'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
628
                (string) filter_var($dataReceived['action'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
629
                (int) filter_var($dataReceived['start'], FILTER_SANITIZE_NUMBER_INT),
630
                (int) filter_var($dataReceived['length'], FILTER_SANITIZE_NUMBER_INT),
631
                $SETTINGS
632
            );
633
634
        /*
635
         * user_psk_reencryption
636
         */
637
        case 'user_psk_reencryption'://action_key
638
            return migrateTo3_DoUserPersonalItemsEncryption(
639
                (int) filter_var($filtered_user_id, FILTER_SANITIZE_NUMBER_INT),
640
                (int) filter_var($dataReceived['start'], FILTER_SANITIZE_NUMBER_INT),
641
                (int) filter_var($dataReceived['length'], FILTER_SANITIZE_NUMBER_INT),
642
                (int) filter_var($dataReceived['counterItemsToTreat'], FILTER_SANITIZE_NUMBER_INT),
643
                (string) filter_var($dataReceived['userPsk'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
644
                $SETTINGS
645
            );
646
647
        /*
648
         * User's public/private keys change
649
         */
650
        case 'change_private_key_encryption_password'://action_key
651
            return changePrivateKeyEncryptionPassword(
652
                (int) filter_var($filtered_user_id, FILTER_SANITIZE_NUMBER_INT),
653
                (string) $dataReceived['current_code'],
654
                (string) filter_var($dataReceived['new_code'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
655
                (string) filter_var($dataReceived['action_type'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
656
                $SETTINGS
657
            );
658
659
        /*
660
         * Launch user keys change on his demand
661
         */
662
        case 'user_new_keys_generation'://action_key
663
664
            // Handle the case where no PWD is provided (user reset his own encryption keys).
665
            if (empty($dataReceived['user_pwd']) && (int) $filtered_user_id === $session->get('user-id')) {
666
                $dataReceived['user_pwd'] = $session->get('user-password');
667
            }
668
669
            return handleUserKeys(
670
                (int) filter_var($filtered_user_id, FILTER_SANITIZE_NUMBER_INT),
671
                (string) filter_var($dataReceived['user_pwd'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
672
                (int) isset($SETTINGS['maximum_number_of_items_to_treat']) === true ? $SETTINGS['maximum_number_of_items_to_treat'] : NUMBER_ITEMS_IN_BATCH,
673
                (string) filter_var($dataReceived['encryption_key'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
674
                (bool) filter_var($dataReceived['delete_existing_keys'], FILTER_VALIDATE_BOOLEAN),
675
                (bool) filter_var($dataReceived['send_email_to_user'], FILTER_VALIDATE_BOOLEAN),
676
                (bool) filter_var($dataReceived['encrypt_with_user_pwd'], FILTER_VALIDATE_BOOLEAN),
677
                (bool) isset($dataReceived['generate_user_new_password']) === true ? filter_var($dataReceived['generate_user_new_password'], FILTER_VALIDATE_BOOLEAN) : false,
678
                (string) filter_var($dataReceived['email_body'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
679
                (bool) filter_var($dataReceived['user_self_change'], FILTER_VALIDATE_BOOLEAN),
680
                (string) filter_var($dataReceived['recovery_public_key'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
681
                (string) filter_var($dataReceived['recovery_private_key'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
682
            );
683
684
        /*
685
         * Launch user recovery download
686
         */
687
        case 'user_recovery_keys_download'://action_key
688
            return handleUserRecoveryKeysDownload(
689
                (int) $filtered_user_id,
690
                (array) $SETTINGS,
691
            );
692
693
        /*
694
         * Default case
695
         */
696
        default :
697
            return prepareExchangedData(
698
                array(
699
                    'error' => true,
700
                ),
701
                'encode'
702
            );
703
    }
704
}
705
706
/**
707
 * Handler for all system tasks
708
 *
709
 * @param string $post_type
710
 * @param array|null|string $dataReceived
711
 * @param array $SETTINGS
712
 * @return string
713
 */
714
function systemHandler(string $post_type, array|null|string $dataReceived, array $SETTINGS): string
715
{
716
    $session = SessionManager::getSession();
717
    switch ($post_type) {
718
        /*
719
        * How many items for this user
720
        */
721
        case 'get_number_of_items_to_treat'://action_system
722
            return getNumberOfItemsToTreat(
723
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
724
                $SETTINGS
725
            );
726
727
        /*
728
        * refresh_folders_categories
729
        */
730
        case 'refresh_folders_categories'://action_system
731
            handleFoldersCategories(
732
                []
733
            );
734
            return prepareExchangedData(
735
                array(
736
                    'error' => false,
737
                ),
738
                'encode'
739
            );
740
741
        /*
742
         * Sending statistics
743
         */
744
        case 'sending_statistics'://action_system
745
            sendingStatistics(
746
                $SETTINGS
747
            );
748
            return prepareExchangedData(
749
                array(
750
                    'error' => false,
751
                ),
752
                'encode'
753
            );
754
755
        /*
756
            * Generate BUG report
757
            */
758
        case 'generate_bug_report'://action_system
759
760
            // Only administrators can see this confidential informations.
761
            if ((int) $session->get('user-admin') !== 1) {
762
                return prepareExchangedData(
763
                    array(
764
                        'error' => false,
765
                    ),
766
                    'encode'
767
                );
768
            }
769
770
            return generateBugReport(
771
                (array) $dataReceived,
772
                $SETTINGS
773
            );
774
775
        /*
776
        * get_teampass_settings
777
        */
778
        case 'get_teampass_settings'://action_system
779
780
            // Encrypt data to return
781
            return prepareExchangedData(
782
                array_intersect_key(
783
                    $SETTINGS, 
784
                    array(
785
                        'ldap_user_attribute' => '',
786
                        'enable_pf_feature' => '',
787
                        'clipboard_life_duration' => '',
788
                        'enable_favourites' => '',
789
                        'copy_to_clipboard_small_icons' => '',
790
                        'enable_attachment_encryption' => '',
791
                        'google_authentication' => '',
792
                        'agses_authentication_enabled' => '',
793
                        'yubico_authentication' => '',
794
                        'duo' => '',
795
                        'personal_saltkey_security_level' => '',
796
                        'enable_tasks_manager' => '',
797
                        'insert_manual_entry_item_history' => '',
798
                        'show_item_data' => '',
799
                    )
800
                ),
801
                'encode'
802
            );
803
804
        /*
805
         * Generates a TOKEN with CRYPT
806
         */
807
        case 'save_token'://action_system
808
            $token = GenerateCryptKey(
809
                null !== filter_input(INPUT_POST, 'size', FILTER_SANITIZE_NUMBER_INT) ? (int) filter_input(INPUT_POST, 'size', FILTER_SANITIZE_NUMBER_INT) : 20,
810
                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,
811
                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,
812
                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,
813
                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,
814
                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
815
            );
816
            
817
            // store in DB
818
            DB::insert(
819
                prefixTable('tokens'),
820
                array(
821
                    'user_id' => (int) $session->get('user-id'),
822
                    'token' => $token,
823
                    'reason' => filter_input(INPUT_POST, 'reason', FILTER_SANITIZE_FULL_SPECIAL_CHARS),
824
                    'creation_timestamp' => time(),
825
                    'end_timestamp' => time() + filter_input(INPUT_POST, 'duration', FILTER_SANITIZE_NUMBER_INT), // in secs
826
                )
827
            );
828
829
            return '[{"token" : "' . $token . '"}]';
830
831
        /*
832
        * Default case
833
        */
834
        default :
835
            return prepareExchangedData(
836
                array(
837
                    'error' => true,
838
                ),
839
                'encode'
840
            );
841
    }
842
}
843
844
845
function utilsHandler(string $post_type, array|null|string $dataReceived, array $SETTINGS): string
846
{
847
    switch ($post_type) {
848
        /*
849
         * generate_an_otp
850
         */
851
        case 'generate_an_otp'://action_utils
852
            return generateAnOTP(
853
                (string) filter_var($dataReceived['label'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
854
                (bool) filter_var($dataReceived['with_qrcode'], FILTER_VALIDATE_BOOLEAN),
855
                (string) filter_var($dataReceived['secret_key'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
856
            );
857
858
859
        /*
860
         * Default case
861
         */
862
        default :
863
            return prepareExchangedData(
864
                array(
865
                    'error' => true,
866
                ),
867
                'encode'
868
            );
869
    }
870
}
871
872
/**
873
 * Permits to set the user ready
874
 *
875
 * @param integer $userid
876
 * @param string $dir
877
 * @return string
878
 */
879
function userIsReady(int $userid, string $dir): string
880
{
881
    DB::update(
882
        prefixTable('users'),
883
        array(
884
            'is_ready_for_usage' => 1,
885
        ),
886
        'id = %i',
887
        $userid
888
    );
889
890
    // Send back
891
    return prepareExchangedData(
892
        array(
893
            'error' => false,
894
        ),
895
        'encode'
896
    ); 
897
}
898
899
900
/**
901
 * Permits to set the user ready
902
 *
903
 * @param integer $userid
904
 * @return string
905
 */
906
function userGetSessionTime(int $userid, string $dir, int $maximum_session_expiration_time): string
907
{
908
    $session = SessionManager::getSession();
909
    // Send back
910
    return prepareExchangedData(
911
        array(
912
            'error' => false,
913
            'timestamp' => $session->get('user-session_duration'),
914
            'max_time_to_add' => intdiv((($maximum_session_expiration_time*60) - ((int) $session->get('user-session_duration') - time())), 60),
915
            'max_session_duration' => $maximum_session_expiration_time,
916
        ),
917
        'encode'
918
    ); 
919
}
920
921
/**
922
 * Save the user's IP
923
 *
924
 * @param integer $userID
925
 * @param string $action
926
 * @return string
927
 */
928
function userSaveIp(int $userID, string $action): string
929
{
930
    if ($action === 'perform') {
931
        DB::update(
932
            prefixTable('users'),
933
            array(
934
                'user_ip' => getClientIpServer(),
935
                'user_ip_lastdate' => time(),
936
            ),
937
            'id = %i',
938
            $userID
939
        );
940
    }
941
942
    return prepareExchangedData(
943
        array(
944
            'error' => false,
945
        ),
946
        'encode'
947
    );
948
}
949
950
/**
951
 * Provides the number of items
952
 *
953
 * @param int   $userId     User ID
954
 * @param array $SETTINGS   TeampassSettings
955
 *
956
 * @return string
957
 */
958
function getNumberOfItemsToTreat(
959
    int $userId,
960
    array $SETTINGS
961
): string
962
{
963
    // get number of items
964
    DB::queryFirstRow(
965
        'SELECT increment_id
966
        FROM ' . prefixTable('sharekeys_items') .
967
        ' WHERE user_id = %i',
968
        $userId
969
    );
970
971
    // Send back
972
    return prepareExchangedData(
973
        array(
974
            'error' => false,
975
            'nbItems' => DB::count(),
976
        ),
977
        'encode'
978
    );
979
}
980
981
982
/**
983
 * 
984
 */
985
function changePassword(
986
    string $post_new_password,
987
    string $post_current_password,
988
    int $post_password_complexity,
989
    string $post_change_request,
990
    int $post_user_id,
991
    array $SETTINGS
992
): string
993
{
994
    $session = SessionManager::getSession();
995
    
996
    // Create password hash
997
    $passwordManager = new PasswordManager();
998
    $post_new_password_hashed = $passwordManager->hashPassword($post_new_password);
999
1000
    // Load user's language
1001
    $lang = new Language($session->get('user-language') ?? 'english');
1002
1003
    // User has decided to change is PW
1004
    if ($post_change_request === 'reset_user_password_expected'
1005
        || $post_change_request === 'user_decides_to_change_password'
1006
    ) {
1007
        // Check that current user is correct
1008
        if ((int) $post_user_id !== (int) $session->get('user-id')) {
1009
            return prepareExchangedData(
1010
                array(
1011
                    'error' => true,
1012
                    'message' => $lang->get('error_not_allowed_to'),
1013
                ),
1014
                'encode'
1015
            );
1016
        }
1017
1018
        // check if expected security level is reached
1019
        $dataUser = DB::queryfirstrow(
1020
            'SELECT *
1021
            FROM ' . prefixTable('users') . ' WHERE id = %i',
1022
            $post_user_id
1023
        );
1024
1025
        // check if badly written
1026
        $dataUser['fonction_id'] = array_filter(
1027
            explode(',', str_replace(';', ',', $dataUser['fonction_id']))
1028
        );
1029
        $dataUser['fonction_id'] = implode(',', $dataUser['fonction_id']);
1030
        DB::update(
1031
            prefixTable('users'),
1032
            array(
1033
                'fonction_id' => $dataUser['fonction_id'],
1034
            ),
1035
            'id = %i',
1036
            $post_user_id
1037
        );
1038
1039
        if (empty($dataUser['fonction_id']) === false) {
1040
            $data = DB::queryFirstRow(
1041
                'SELECT complexity
1042
                FROM ' . prefixTable('roles_title') . '
1043
                WHERE id IN (' . $dataUser['fonction_id'] . ')
1044
                ORDER BY complexity DESC'
1045
            );
1046
        } else {
1047
            // In case user has no roles yet
1048
            $data = array();
1049
            $data['complexity'] = 0;
1050
        }
1051
1052
        if ((int) $post_password_complexity < (int) $data['complexity']) {
1053
            return prepareExchangedData(
1054
                array(
1055
                    'error' => true,
1056
                    'message' => '<div style="margin:10px 0 10px 15px;">' . $lang->get('complexity_level_not_reached') . '.<br>' .
1057
                        $lang->get('expected_complexity_level') . ': <b>' . TP_PW_COMPLEXITY[$data['complexity']][1] . '</b></div>',
1058
                ),
1059
                'encode'
1060
            );
1061
        }
1062
1063
        // Check that the 2 passwords are differents
1064
        if ($post_current_password === $post_new_password) {
1065
            return prepareExchangedData(
1066
                array(
1067
                    'error' => true,
1068
                    'message' => $lang->get('password_already_used'),
1069
                ),
1070
                'encode'
1071
            );
1072
        }
1073
1074
        // update sessions
1075
        $session->set('user-last_pw_change', mktime(0, 0, 0, (int) date('m'), (int) date('d'), (int) date('y')));
1076
        $session->set('user-validite_pw', 1);
1077
1078
        // BEfore updating, check that the pwd is correct
1079
        if ($passwordManager->verifyPassword($post_new_password_hashed, $post_new_password) === true && empty($dataUser['private_key']) === false) {
1080
            $special_action = 'none';
1081
            if ($post_change_request === 'reset_user_password_expected') {
1082
                $session->set('user-private_key', decryptPrivateKey($post_current_password, $dataUser['private_key']));
1083
            }
1084
1085
            // update DB
1086
            DB::update(
1087
                prefixTable('users'),
1088
                array(
1089
                    'pw' => $post_new_password_hashed,
1090
                    'last_pw_change' => mktime(0, 0, 0, (int) date('m'), (int) date('d'), (int) date('y')),
1091
                    'last_pw' => $post_current_password,
1092
                    'special' => $special_action,
1093
                    'private_key' => encryptPrivateKey($post_new_password, $session->get('user-private_key')),
1094
                ),
1095
                'id = %i',
1096
                $post_user_id
1097
            );
1098
            // update LOG
1099
            logEvents($SETTINGS, 'user_mngt', 'at_user_pwd_changed', (string) $session->get('user-id'), $session->get('user-login'), $post_user_id);
1100
1101
            // Send back
1102
            return prepareExchangedData(
1103
                array(
1104
                    'error' => false,
1105
                    'message' => '',
1106
                ),
1107
                'encode'
1108
            );
1109
        }
1110
        // Send back
1111
        return prepareExchangedData(
1112
            array(
1113
                'error' => true,
1114
                'message' => $lang->get('pw_hash_not_correct'),
1115
            ),
1116
            'encode'
1117
        );
1118
    }
1119
    return prepareExchangedData(
1120
        array(
1121
            'error' => true,
1122
            'message' => $lang->get('error_not_allowed_to'),
1123
        ),
1124
        'encode'
1125
    );
1126
}
1127
1128
function generateQRCode(
1129
    $post_id,
1130
    $post_demand_origin,
1131
    $post_send_mail,
1132
    $post_login,
1133
    $post_pwd,
1134
    $post_token,
1135
    array $SETTINGS
1136
): string
1137
{
1138
    // Load user's language
1139
    $session = SessionManager::getSession();
1140
    $lang = new Language($session->get('user-language') ?? 'english');
1141
1142
    // is this allowed by setting
1143
    if (isKeyExistingAndEqual('ga_reset_by_user', 0, $SETTINGS) === true
1144
        && (null === $post_demand_origin || $post_demand_origin !== 'users_management_list')
1145
    ) {
1146
        // User cannot ask for a new code
1147
        return prepareExchangedData(
1148
            array(
1149
                'error' => true,
1150
                'message' => "113 ".$lang->get('error_not_allowed_to')." - ".isKeyExistingAndEqual('ga_reset_by_user', 1, $SETTINGS),
1151
            ),
1152
            'encode'
1153
        );
1154
    }
1155
    
1156
    // Check if user exists
1157
    if (isValueSetNullEmpty($post_id) === true) {
1158
        // Get data about user
1159
        $dataUser = DB::queryfirstrow(
1160
            'SELECT id, email, pw
1161
            FROM ' . prefixTable('users') . '
1162
            WHERE login = %s',
1163
            $post_login
1164
        );
1165
    } else {
1166
        $dataUser = DB::queryfirstrow(
1167
            'SELECT id, login, email, pw
1168
            FROM ' . prefixTable('users') . '
1169
            WHERE id = %i',
1170
            $post_id
1171
        );
1172
        $post_login = $dataUser['login'];
1173
    }
1174
    // Get number of returned users
1175
    $counter = DB::count();
1176
1177
    // Do treatment
1178
    if ($counter === 0) {
1179
        // Not a registered user !
1180
        logEvents($SETTINGS, 'failed_auth', 'user_not_exists', '', stripslashes($post_login), stripslashes($post_login));
1181
        return prepareExchangedData(
1182
            array(
1183
                'error' => true,
1184
                'message' => $lang->get('no_user'),
1185
                'tst' => 1,
1186
            ),
1187
            'encode'
1188
        );
1189
    }
1190
1191
    $passwordManager = new PasswordManager();
1192
    if (
1193
        isSetArrayOfValues([$post_pwd, $dataUser['pw']]) === true
1194
        && $passwordManager->verifyPassword($dataUser['pw'], $post_pwd) === false
1195
        && $post_demand_origin !== 'users_management_list'
1196
    ) {
1197
        // checked the given password
1198
        logEvents($SETTINGS, 'failed_auth', 'password_is_not_correct', '', stripslashes($post_login), stripslashes($post_login));
1199
        return prepareExchangedData(
1200
            array(
1201
                'error' => true,
1202
                'message' => $lang->get('no_user'),
1203
                'tst' => $post_demand_origin,
1204
            ),
1205
            'encode'
1206
        );
1207
    }
1208
    
1209
    if (empty($dataUser['email']) === true) {
1210
        return prepareExchangedData(
1211
            array(
1212
                'error' => true,
1213
                'message' => $lang->get('no_email_set'),
1214
            ),
1215
            'encode'
1216
        );
1217
    }
1218
1219
    // Check if token already used
1220
    $dataToken = DB::queryfirstrow(
1221
        'SELECT end_timestamp, reason
1222
        FROM ' . prefixTable('tokens') . '
1223
        WHERE token = %s AND user_id = %i',
1224
        $post_token,
1225
        $dataUser['id']
1226
    );
1227
    $tokenId = '';
1228
    if (DB::count() > 0 && is_null($dataToken['end_timestamp']) === false && $dataToken['reason'] === 'auth_qr_code') {
1229
        // This token has already been used
1230
        return prepareExchangedData(
1231
            array(
1232
                'error' => true,
1233
                'message' => 'TOKEN already used',//$lang->get('no_email_set'),
1234
            ),
1235
            'encode'
1236
        );
1237
    } elseif(DB::count() === 0) {
1238
        // Store token for this action
1239
        DB::insert(
1240
            prefixTable('tokens'),
1241
            array(
1242
                'user_id' => (int) $dataUser['id'],
1243
                'token' => $post_token,
1244
                'reason' => 'auth_qr_code',
1245
                'creation_timestamp' => time(),
1246
            )
1247
        );
1248
        $tokenId = DB::insertId();
1249
    }
1250
    
1251
    // generate new GA user code
1252
    $tfa = new TwoFactorAuth($SETTINGS['ga_website_name']);
1253
    $gaSecretKey = $tfa->createSecret();
1254
    $gaTemporaryCode = GenerateCryptKey(12, false, true, true, false, true);
1255
1256
    DB::update(
1257
        prefixTable('users'),
1258
        [
1259
            'ga' => $gaSecretKey,
1260
            'ga_temporary_code' => $gaTemporaryCode,
1261
        ],
1262
        'id = %i',
1263
        $dataUser['id']
1264
    );
1265
1266
    // Log event
1267
    logEvents($SETTINGS, 'user_connection', 'at_2fa_google_code_send_by_email', (string) $dataUser['id'], stripslashes($post_login), stripslashes($post_login));
1268
1269
    // Update token status
1270
    DB::update(
1271
        prefixTable('tokens'),
1272
        [
1273
            'end_timestamp' => time(),
1274
        ],
1275
        'id = %i',
1276
        $tokenId
1277
    );
1278
1279
    // send mail?
1280
    if ((int) $post_send_mail === 1) {
1281
        prepareSendingEmail(
1282
            $lang->get('email_ga_subject'),
1283
            str_replace(
1284
                '#2FACode#',
1285
                $gaTemporaryCode,
1286
                $lang->get('email_ga_text')
1287
            ),
1288
            $dataUser['email']
1289
        );
1290
1291
        // send back
1292
        return prepareExchangedData(
1293
            array(
1294
                'error' => false,
1295
                'message' => $post_send_mail,
1296
                'email' => $dataUser['email'],
1297
                'email_result' => str_replace(
1298
                    '#email#',
1299
                    '<b>' . obfuscateEmail($dataUser['email']) . '</b>',
1300
                    addslashes($lang->get('admin_email_result_ok'))
1301
                ),
1302
            ),
1303
            'encode'
1304
        );
1305
    }
1306
    
1307
    // send back
1308
    return prepareExchangedData(
1309
        array(
1310
            'error' => false,
1311
            'message' => '',
1312
            'email' => $dataUser['email'],
1313
            'email_result' => str_replace(
1314
                '#email#',
1315
                '<b>' . obfuscateEmail($dataUser['email']) . '</b>',
1316
                addslashes($lang->get('admin_email_result_ok'))
1317
            ),
1318
        ),
1319
        'encode'
1320
    );
1321
}
1322
1323
function sendEmailsNotSent(
1324
    array $SETTINGS
1325
)
1326
{
1327
    $emailSettings = new EmailSettings($SETTINGS);
1328
    $emailService = new EmailService();
1329
1330
    if (isKeyExistingAndEqual('enable_send_email_on_user_login', 1, $SETTINGS) === true) {
1331
        $row = DB::queryFirstRow(
1332
            'SELECT valeur FROM ' . prefixTable('misc') . ' WHERE type = %s AND intitule = %s',
1333
            'cron',
1334
            'sending_emails'
1335
        );
1336
1337
        if ((int) (time() - $row['valeur']) >= 300 || (int) $row['valeur'] === 0) {
1338
            $rows = DB::query(
1339
                'SELECT *
1340
                FROM ' . prefixTable('emails') .
1341
                ' WHERE status != %s',
1342
                'sent'
1343
            );
1344
            foreach ($rows as $record) {
1345
                // Send email
1346
                $ret = json_decode(
1347
                    $emailService->sendMail(
1348
                        $record['subject'],
1349
                        $record['body'],
1350
                        $record['receivers'],
1351
                        $emailSettings
1352
                    ),
1353
                    true
1354
                );
1355
1356
                // update item_id in files table
1357
                DB::update(
1358
                    prefixTable('emails'),
1359
                    array(
1360
                        'status' => $ret['error'] === 'error_mail_not_send' ? 'not_sent' : 'sent',
1361
                    ),
1362
                    'timestamp = %s',
1363
                    $record['timestamp']
1364
                );
1365
            }
1366
        }
1367
        // update cron time
1368
        DB::update(
1369
            prefixTable('misc'),
1370
            array(
1371
                'valeur' => time(),
1372
                'updated_at' => time(),
1373
            ),
1374
            'intitule = %s AND type = %s',
1375
            'sending_emails',
1376
            'cron'
1377
        );
1378
    }
1379
}
1380
1381
1382
function refreshUserItemsSeenList(
1383
    array $SETTINGS
1384
): string
1385
{
1386
    $session = SessionManager::getSession();
1387
1388
    // get list of last items seen
1389
    $arr_html = array();
1390
    $rows = DB::query(
1391
        '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
1392
        FROM ' . prefixTable('log_items') . ' AS l
1393
        RIGHT JOIN ' . prefixTable('items') . ' AS i ON (l.id_item = i.id)
1394
        WHERE l.action = %s AND l.id_user = %i
1395
        ORDER BY l.date DESC
1396
        LIMIT 0, 100',
1397
        'at_shown',
1398
        $session->get('user-id')
1399
    );
1400
    if (DB::count() > 0) {
1401
        foreach ($rows as $record) {
1402
            if (in_array($record['id']->id, array_column($arr_html, 'id')) === false) {
1403
                array_push(
1404
                    $arr_html,
1405
                    array(
1406
                        'id' => $record['id'],
1407
                        'label' => htmlspecialchars(stripslashes(htmlspecialchars_decode($record['label'], ENT_QUOTES)), ENT_QUOTES),
1408
                        'tree_id' => $record['id_tree'],
1409
                        'perso' => $record['perso'],
1410
                        'restricted' => $record['restricted'],
1411
                    )
1412
                );
1413
                if (count($arr_html) >= (int) $SETTINGS['max_latest_items']) {
1414
                    break;
1415
                }
1416
            }
1417
        }
1418
    }
1419
1420
    // get wainting suggestions
1421
    $nb_suggestions_waiting = 0;
1422
    if (isKeyExistingAndEqual('enable_suggestion', 1, $SETTINGS) === true
1423
        && ((int) $session->get('user-admin') === 1 || (int) $session->get('user-manager') === 1)
1424
    ) {
1425
        DB::query('SELECT * FROM ' . prefixTable('suggestion'));
1426
        $nb_suggestions_waiting = DB::count();
1427
    }
1428
1429
    return json_encode(
1430
        array(
1431
            'error' => '',
1432
            'existing_suggestions' => $nb_suggestions_waiting,
1433
            'html_json' => $arr_html,
1434
        ),
1435
        JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
1436
    );
1437
}
1438
1439
function sendingStatistics(
1440
    array $SETTINGS
1441
): void
1442
{
1443
    $session = SessionManager::getSession();
1444
    if (
1445
        isSetArrayOfValues([$SETTINGS['send_statistics_items'], $SETTINGS['send_stats_time']]) === true
1446
        && isKeyExistingAndEqual('send_stats', 1, $SETTINGS) === true
1447
        && (int) ($SETTINGS['send_stats_time'] + TP_ONE_DAY_SECONDS) > time()
1448
    ) {
1449
        // get statistics data
1450
        $stats_data = getStatisticsData($SETTINGS);
1451
1452
        // get statistics items to share
1453
        $statsToSend = [];
1454
        $statsToSend['ip'] = $_SERVER['SERVER_ADDR'];
1455
        $statsToSend['timestamp'] = time();
1456
        foreach (array_filter(explode(';', $SETTINGS['send_statistics_items'])) as $data) {
1457
            if ($data === 'stat_languages') {
1458
                $tmp = '';
1459
                foreach ($stats_data[$data] as $key => $value) {
1460
                    $tmp .= $tmp === '' ? $key . '-' . $value : ',' . $key . '-' . $value;
1461
                }
1462
                $statsToSend[$data] = $tmp;
1463
            } elseif ($data === 'stat_country') {
1464
                $tmp = '';
1465
                foreach ($stats_data[$data] as $key => $value) {
1466
                    $tmp .= $tmp === '' ? $key . '-' . $value : ',' . $key . '-' . $value;
1467
                }
1468
                $statsToSend[$data] = $tmp;
1469
            } else {
1470
                $statsToSend[$data] = $stats_data[$data];
1471
            }
1472
        }
1473
1474
        // connect to Teampass Statistics database
1475
        $link2 = new MeekroDB(
1476
            'teampass.pw',
1477
            'teampass_user',
1478
            'ZMlEfRzKzFLZNzie',
1479
            'teampass_followup',
1480
            '3306',
1481
            'utf8'
1482
        );
1483
1484
        $link2->insert(
1485
            'statistics',
1486
            $statsToSend
1487
        );
1488
1489
        // update table misc with current timestamp
1490
        DB::update(
1491
            prefixTable('misc'),
1492
            array(
1493
                'valeur' => time(),
1494
                'updated_at' => time(),
1495
            ),
1496
            'type = %s AND intitule = %s',
1497
            'admin',
1498
            'send_stats_time'
1499
        );
1500
1501
        //permits to test only once by session
1502
        $session->set('system-send_stats_done', 1);
1503
        $SETTINGS['send_stats_time'] = time();
1504
    }
1505
}
1506
1507
function generateBugReport(
1508
    array $data,
1509
    array $SETTINGS
1510
): string
1511
{
1512
    $config_exclude_vars = array(
1513
        'bck_script_passkey',
1514
        'email_smtp_server',
1515
        'email_auth_username',
1516
        'email_auth_pwd',
1517
        'email_from',
1518
        'onthefly-restore-key',
1519
        'onthefly-backup-key',
1520
        'ldap_password',
1521
        'ldap_hosts',
1522
        'proxy_ip',
1523
        'ldap_bind_passwd',
1524
        'syslog_host',
1525
        'duo_akey',
1526
        'duo_ikey',
1527
        'duo_skey',
1528
        'duo_host',
1529
        'oauth2_client_id',
1530
        'oauth2_tenant_id',
1531
        'oauth2_client_secret',
1532
    );
1533
1534
    // Load user's language
1535
    $session = SessionManager::getSession();
1536
    $lang = new Language($session->get('user-language') ?? 'english');
1537
1538
    // Read config file
1539
    $list_of_options = '';
1540
    $url_found = '';
1541
    $anonym_url = '';
1542
    $sortedSettings = $SETTINGS;
1543
    ksort($sortedSettings);
1544
1545
    foreach ($sortedSettings as $key => $value) {
1546
        // Identify url to anonymize it
1547
        if ($key === 'cpassman_url' && empty($url_found) === true) {
1548
            $url_found = $value;
1549
            if (empty($url_found) === false) {
1550
                $tmp = parse_url($url_found);
1551
                $anonym_url = $tmp['scheme'] . '://<anonym_url>' . (isset($tmp['path']) === true ? $tmp['path'] : '');
1552
                $value = $anonym_url;
1553
            } else {
1554
                $value = '';
1555
            }
1556
        }
1557
1558
        // Anonymize all urls
1559
        if (empty($anonym_url) === false) {
1560
            $value = str_replace($url_found, $anonym_url, (string) $value);
1561
        }
1562
1563
        // Clear some vars
1564
        foreach ($config_exclude_vars as $var) {
1565
            if ($key === $var) {
1566
                $value = '<removed>';
1567
            }
1568
        }
1569
1570
        // Complete line to display
1571
        $list_of_options .= "'$key' => '$value'\n";
1572
    }
1573
1574
    // Get error
1575
    $err = error_get_last();
1576
1577
    // Get 10 latest errors in Teampass
1578
    $teampass_errors = '';
1579
    $rows = DB::query(
1580
        'SELECT label, date AS error_date
1581
        FROM ' . prefixTable('log_system') . "
1582
        WHERE `type` LIKE 'error'
1583
        ORDER BY `date` DESC
1584
        LIMIT 0, 10"
1585
    );
1586
    if (DB::count() > 0) {
1587
        foreach ($rows as $record) {
1588
            if (empty($teampass_errors) === true) {
1589
                $teampass_errors = ' * ' . date($SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'], (int) $record['error_date']) . ' - ' . $record['label'];
1590
            } else {
1591
                $teampass_errors .= ' * ' . date($SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'], (int) $record['error_date']) . ' - ' . $record['label'];
1592
            }
1593
        }
1594
    }
1595
1596
    $link = mysqli_connect(DB_HOST, DB_USER, DB_PASSWD_CLEAR, DB_NAME, (int) DB_PORT, null);
1597
1598
    // Now prepare text
1599
    $txt = '### Page on which it happened
1600
' . $data['current_page'] . '
1601
1602
### Steps to reproduce
1603
1.
1604
2.
1605
3.
1606
1607
### Expected behaviour
1608
Tell us what should happen
1609
1610
1611
### Actual behaviour
1612
Tell us what happens instead
1613
1614
### Server configuration
1615
**Operating system**: ' . php_uname() . '
1616
1617
**Web server:** ' . $_SERVER['SERVER_SOFTWARE'] . '
1618
1619
**Database:** ' . ($link === false ? $lang->get('undefined') : mysqli_get_server_info($link)) . '
1620
1621
**PHP version:** ' . PHP_VERSION . '
1622
1623
**Teampass version:** ' . TP_VERSION . '
1624
1625
**Teampass configuration file:**
1626
```
1627
' . $list_of_options . '
1628
```
1629
1630
**Updated from an older Teampass or fresh install:**
1631
1632
### Client configuration
1633
1634
**Browser:** ' . $data['browser_name'] . ' - ' . $data['browser_version'] . '
1635
1636
**Operating system:** ' . $data['os'] . ' - ' . $data['os_archi'] . 'bits
1637
1638
### Logs
1639
1640
#### Web server error log
1641
```
1642
' . $err['message'] . ' - ' . $err['file'] . ' (' . $err['line'] . ')
1643
```
1644
1645
#### Teampass 10 last system errors
1646
```
1647
' . $teampass_errors . '
1648
```
1649
1650
#### Log from the web-browser developer console (CTRL + SHIFT + i)
1651
```
1652
Insert the log here and especially the answer of the query that failed.
1653
```
1654
';
1655
1656
    return prepareExchangedData(
1657
        array(
1658
            'html' => $txt,
1659
            'error' => '',
1660
        ),
1661
        'encode'
1662
    );
1663
}
1664
1665
/**
1666
 * Check that the user password is valid
1667
 *
1668
 * @param integer $post_user_id
1669
 * @param string $post_user_password
1670
 * @param array $SETTINGS
1671
 * @return string
1672
 */
1673
function isUserPasswordCorrect(
1674
    int $post_user_id,
1675
    string $post_user_password,
1676
    array $SETTINGS
1677
): string
1678
{
1679
    $session = SessionManager::getSession();
1680
    // Load user's language
1681
    $lang = new Language($session->get('user-language') ?? 'english');
1682
    
1683
    if (isUserIdValid($post_user_id) === true) {
1684
        // Check if user exists
1685
        $userInfo = DB::queryFirstRow(
1686
            'SELECT public_key, private_key, pw, auth_type
1687
            FROM ' . prefixTable('users') . '
1688
            WHERE id = %i',
1689
            $post_user_id
1690
        );
1691
        if (DB::count() > 0 && empty($userInfo['private_key']) === false) {
1692
            // Get itemKey from current user
1693
            // Get one item
1694
            $currentUserKey = DB::queryFirstRow(
1695
                'SELECT object_id, share_key, increment_id
1696
                FROM ' . prefixTable('sharekeys_items') . ' AS si
1697
                INNER JOIN ' . prefixTable('items') . ' AS i ON  (i.id = si.object_id)
1698
                INNER JOIN ' . prefixTable('nested_tree') . ' AS nt ON  (i.id_tree = nt.id)
1699
                WHERE user_id = %i AND nt.personal_folder = %i',
1700
                $post_user_id,
1701
                0
1702
            );
1703
            
1704
            if (DB::count() === 0) {
1705
                // This user has no items
1706
                // let's consider no items in DB
1707
                return prepareExchangedData(
1708
                    array(
1709
                        'error' => false,
1710
                        'message' => '',
1711
                        'debug' => '',
1712
                    ),
1713
                    'encode'
1714
                );
1715
            }
1716
1717
            if ($currentUserKey !== null) {
1718
                // Decrypt itemkey with user key
1719
                // use old password to decrypt private_key
1720
                $session->set('user-private_key', decryptPrivateKey($post_user_password, $userInfo['private_key']));
1721
                $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $session->get('user-private_key'));
1722
1723
                //echo $post_user_password."  --  ".$userInfo['private_key']. ";;";
1724
1725
                if (empty(base64_decode($itemKey)) === false) {
1726
                    // GOOD password
1727
                    return prepareExchangedData(
1728
                        array(
1729
                            'error' => false,
1730
                            'message' => '',
1731
                            'debug' => '',
1732
                        ),
1733
                        'encode'
1734
                    );
1735
                }
1736
            }
1737
1738
            // use the password check
1739
            $passwordManager = new PasswordManager();
1740
            
1741
            if ($passwordManager->verifyPassword($userInfo['pw'], htmlspecialchars_decode($post_user_password)) === true) {
1742
                // GOOD password
1743
                return prepareExchangedData(
1744
                    array(
1745
                        'error' => false,
1746
                        'message' => '',
1747
                        'debug' => '',
1748
                    ),
1749
                    'encode'
1750
                );
1751
            }
1752
        }
1753
    }
1754
1755
    return prepareExchangedData(
1756
        array(
1757
            'error' => true,
1758
            'message' => $lang->get('password_is_not_correct'),
1759
        ),
1760
        'encode'
1761
    );
1762
}
1763
1764
function changePrivateKeyEncryptionPassword(
1765
    int $post_user_id,
1766
    string $post_current_code,
1767
    string $post_new_code,
1768
    string $post_action_type,
1769
    array $SETTINGS
1770
): string
1771
{
1772
    $session = SessionManager::getSession();
1773
    // Load user's language
1774
    $lang = new Language($session->get('user-language') ?? 'english');
1775
    
1776
    if (empty($post_new_code) === true) {
1777
        if (empty($session->get('user-password')) === false) {
1778
            $post_new_code = $session->get('user-password');
1779
        } else {
1780
            // no user password???
1781
            return prepareExchangedData(
1782
                array(
1783
                    'error' => true,
1784
                    'message' => $lang->get('error_no_user_password_exists'),
1785
                    'debug' => '',
1786
                ),
1787
                'encode'
1788
            );
1789
        }
1790
    }
1791
1792
    if (isUserIdValid($post_user_id) === true) {
1793
        // Get user info
1794
        $userData = DB::queryFirstRow(
1795
            'SELECT private_key
1796
            FROM ' . prefixTable('users') . '
1797
            WHERE id = %i',
1798
            $post_user_id
1799
        );
1800
        if (DB::count() > 0 && empty($userData['private_key']) === false) {
1801
            if ($post_action_type === 'encrypt_privkey_with_user_password') {
1802
                // Here the user has his private key encrypted with an OTC.
1803
                // We need to encrypt it with his real password
1804
                $privateKey = decryptPrivateKey($post_new_code, $userData['private_key']);
1805
                $hashedPrivateKey = encryptPrivateKey($post_current_code, $privateKey);
1806
            } else {
1807
                $privateKey = decryptPrivateKey($post_current_code, $userData['private_key']);
1808
                $hashedPrivateKey = encryptPrivateKey($post_new_code, $privateKey);
1809
            }
1810
1811
            // Should fail here to avoid break user private key.
1812
            if (strlen($privateKey) === 0 || strlen($hashedPrivateKey) < 30) {
1813
                if (defined('LOG_TO_SERVER') && LOG_TO_SERVER === true) {
1814
                    error_log("Error reencrypt user private key. User ID: {$post_user_id}, Given OTP: '{$post_current_code}'");
1815
                }
1816
                return prepareExchangedData(
1817
                    array(
1818
                        'error' => true,
1819
                        'message' => $lang->get('error_otp_secret'),
1820
                        'debug' => '',
1821
                    ),
1822
                    'encode'
1823
                );
1824
            }
1825
1826
            // Update user account
1827
            DB::update(
1828
                prefixTable('users'),
1829
                array(
1830
                    'private_key' => $hashedPrivateKey,
1831
                    'special' => 'none',
1832
                    'otp_provided' => 1,
1833
                ),
1834
                'id = %i',
1835
                $post_user_id
1836
            );
1837
1838
            $session->set('user-private_key', $privateKey);
1839
        }
1840
1841
        // Return
1842
        return prepareExchangedData(
1843
            array(
1844
                'error' => false,
1845
                'message' => '',
1846
            ),
1847
            'encode'
1848
        );
1849
    }
1850
    
1851
    return prepareExchangedData(
1852
        array(
1853
            'error' => true,
1854
            'message' => $lang->get('error_no_user'),
1855
            'debug' => '',
1856
        ),
1857
        'encode'
1858
    );
1859
}
1860
1861
function initializeUserPassword(
1862
    int $post_user_id,
1863
    string $post_special,
1864
    string $post_user_password,
1865
    bool $post_self_change,
1866
    array $SETTINGS
1867
): string
1868
{
1869
    // Load user's language
1870
    $session = SessionManager::getSession();
1871
    $lang = new Language($session->get('user-language') ?? 'english');
1872
    
1873
    if (isUserIdValid($post_user_id) === true) {
1874
        // Get user info
1875
        $userData = DB::queryFirstRow(
1876
            'SELECT email, auth_type, login
1877
            FROM ' . prefixTable('users') . '
1878
            WHERE id = %i',
1879
            $post_user_id
1880
        );
1881
        if (DB::count() > 0 && empty($userData['email']) === false) {
1882
            // If user pwd is empty then generate a new one and send it to user
1883
            if (isset($post_user_password) === false || empty($post_user_password) === true) {
1884
                // Generate new password
1885
                $post_user_password = generateQuickPassword();
1886
            }
1887
1888
            // If LDAP enabled, then
1889
            // check that this password is correct
1890
            $continue = true;
1891
            if ($userData['auth_type'] === 'ldap' && (int) $SETTINGS['ldap_mode'] === 1) {
1892
                $continue = ldapCheckUserPassword(
1893
                    $userData['login'],
1894
                    $post_user_password,
1895
                    $SETTINGS
1896
                );
1897
            }
1898
1899
            if ($continue === true) {
1900
                // Only change if email is successfull
1901
                $passwordManager = new PasswordManager();
1902
                // GEnerate new keys
1903
                $userKeys = generateUserKeys($post_user_password);
1904
1905
                // Update user account
1906
                DB::update(
1907
                    prefixTable('users'),
1908
                    array(
1909
                        'special' => $post_special,
1910
                        'pw' => $passwordManager->hashPassword($post_user_password),
1911
                        'public_key' => $userKeys['public_key'],
1912
                        'private_key' => $userKeys['private_key'],
1913
                        'last_pw_change' => time(),
1914
                    ),
1915
                    'id = %i',
1916
                    $post_user_id
1917
                );
1918
1919
                // Return
1920
                return prepareExchangedData(
1921
                    array(
1922
                        'error' => false,
1923
                        'message' => '',
1924
                        'user_pwd' => $post_user_password,
1925
                        'user_email' => $userData['email'],
1926
                    ),
1927
                    'encode'
1928
                );
1929
            }
1930
            // Return error
1931
            return prepareExchangedData(
1932
                array(
1933
                    'error' => true,
1934
                    'message' => $lang->get('no_email_set'),
1935
                    'debug' => '',
1936
                    'self_change' => $post_self_change,
1937
                ),
1938
                'encode'
1939
            );
1940
        }
1941
1942
        // Error
1943
        return prepareExchangedData(
1944
            array(
1945
                'error' => true,
1946
                'message' => $lang->get('no_email_set'),
1947
                'debug' => '',
1948
            ),
1949
            'encode'
1950
        );
1951
    }
1952
    
1953
    return prepareExchangedData(
1954
        array(
1955
            'error' => true,
1956
            'message' => $lang->get('error_no_user'),
1957
            'debug' => '',
1958
        ),
1959
        'encode'
1960
    );
1961
}
1962
1963
function generateOneTimeCode(
1964
    int $post_user_id
1965
): string
1966
{
1967
    // Load user's language
1968
    $session = SessionManager::getSession();
1969
    $lang = new Language($session->get('user-language') ?? 'english');
1970
    
1971
    if (isUserIdValid($post_user_id) === true) {
1972
        // Get user info
1973
        $userData = DB::queryFirstRow(
1974
            'SELECT email, auth_type, login
1975
            FROM ' . prefixTable('users') . '
1976
            WHERE id = %i',
1977
            $post_user_id
1978
        );
1979
        if (DB::count() > 0 && empty($userData['email']) === false) {
1980
            // Generate pwd
1981
            $password = generateQuickPassword();
1982
1983
            // GEnerate new keys
1984
            $userKeys = generateUserKeys($password);
1985
1986
            // Save in DB
1987
            DB::update(
1988
                prefixTable('users'),
1989
                array(
1990
                    'public_key' => $userKeys['public_key'],
1991
                    'private_key' => $userKeys['private_key'],
1992
                    'special' => 'generate-keys',
1993
                ),
1994
                'id=%i',
1995
                $post_user_id
1996
            );
1997
1998
            return prepareExchangedData(
1999
                array(
2000
                    'error' => false,
2001
                    'message' => '',
2002
                    'code' => $password,
2003
                    'visible_otp' => ADMIN_VISIBLE_OTP_ON_LDAP_IMPORT,
2004
                ),
2005
                'encode'
2006
            );
2007
        }
2008
        
2009
        return prepareExchangedData(
2010
            array(
2011
                'error' => true,
2012
                'message' => $lang->get('no_email_set'),
2013
            ),
2014
            'encode'
2015
        );
2016
    }
2017
        
2018
    return prepareExchangedData(
2019
        array(
2020
            'error' => true,
2021
            'message' => $lang->get('error_no_user'),
2022
        ),
2023
        'encode'
2024
    );
2025
}
2026
2027
function startReEncryptingUserSharekeys(
2028
    int $post_user_id,
2029
    bool $post_self_change,
2030
    array $SETTINGS
2031
): string
2032
{
2033
    // Load user's language
2034
    $session = SessionManager::getSession();
2035
    $lang = new Language($session->get('user-language') ?? 'english');
2036
    
2037
    if (isUserIdValid($post_user_id) === true) {
2038
        // Check if user exists
2039
        DB::queryFirstRow(
2040
            'SELECT *
2041
            FROM ' . prefixTable('users') . '
2042
            WHERE id = %i',
2043
            $post_user_id
2044
        );
2045
        if (DB::count() > 0) {
2046
            // CLear old sharekeys
2047
            if ($post_self_change === false) {
2048
                deleteUserObjetsKeys($post_user_id, $SETTINGS);
2049
            }
2050
2051
            // Continu with next step
2052
            return prepareExchangedData(
2053
                array(
2054
                    'error' => false,
2055
                    'message' => '',
2056
                    'step' => 'step1',
2057
                    'userId' => $post_user_id,
2058
                    'start' => 0,
2059
                    'self_change' => $post_self_change,
2060
                ),
2061
                'encode'
2062
            );
2063
        }
2064
        // Nothing to do
2065
        return prepareExchangedData(
2066
            array(
2067
                'error' => true,
2068
                'message' => $lang->get('error_no_user'),
2069
            ),
2070
            'encode'
2071
        );
2072
    }
2073
2074
    return prepareExchangedData(
2075
        array(
2076
            'error' => true,
2077
            'message' => $lang->get('error_no_user'),
2078
        ),
2079
        'encode'
2080
    );
2081
}
2082
2083
/**
2084
 * Permits to encrypt user's keys
2085
 *
2086
 * @param integer $post_user_id
2087
 * @param boolean $post_self_change
2088
 * @param string $post_action
2089
 * @param integer $post_start
2090
 * @param integer $post_length
2091
 * @param array $SETTINGS
2092
 * @return string
2093
 */
2094
function continueReEncryptingUserSharekeys(
2095
    int     $post_user_id,
2096
    bool    $post_self_change,
2097
    string  $post_action,
2098
    int     $post_start,
2099
    int     $post_length,
2100
    array   $SETTINGS
2101
): string
2102
{
2103
    // Load user's language
2104
    $session = SessionManager::getSession();
2105
    $lang = new Language($session->get('user-language') ?? 'english');
2106
    
2107
    if (isUserIdValid($post_user_id) === true) {
2108
        // Check if user exists
2109
        $userInfo = DB::queryFirstRow(
2110
            'SELECT public_key
2111
            FROM ' . prefixTable('users') . '
2112
            WHERE id = %i',
2113
            $post_user_id
2114
        );
2115
        if (isset($userInfo['public_key']) === true) {
2116
            $return = [];
2117
2118
            // WHAT STEP TO PERFORM?
2119
            if ($post_action === 'step0') {
2120
                // CLear old sharekeys
2121
                if ($post_self_change === false) {
2122
                    deleteUserObjetsKeys($post_user_id, $SETTINGS);
2123
                }
2124
2125
                $return['post_action'] = 'step10';
2126
            }
2127
            
2128
            // STEP 1 - ITEMS
2129
            elseif ($post_action === 'step10') {
2130
                $return = continueReEncryptingUserSharekeysStep10(
2131
                    $post_user_id,
2132
                    $post_self_change,
2133
                    $post_action,
2134
                    $post_start,
2135
                    $post_length,
2136
                    $userInfo['public_key'],
2137
                    $SETTINGS
2138
                );
2139
            }
2140
2141
            // STEP 2 - LOGS
2142
            elseif ($post_action === 'step20') {
2143
                $return = continueReEncryptingUserSharekeysStep20(
2144
                    $post_user_id,
2145
                    $post_self_change,
2146
                    $post_action,
2147
                    $post_start,
2148
                    $post_length,
2149
                    $userInfo['public_key'],
2150
                    $SETTINGS
2151
                );
2152
            }
2153
2154
            // STEP 3 - FIELDS
2155
            elseif ($post_action === 'step30') {
2156
                $return = continueReEncryptingUserSharekeysStep30(
2157
                    $post_user_id,
2158
                    $post_self_change,
2159
                    $post_action,
2160
                    $post_start,
2161
                    $post_length,
2162
                    $userInfo['public_key'],
2163
                    $SETTINGS
2164
                );
2165
            }
2166
            
2167
            // STEP 4 - SUGGESTIONS
2168
            elseif ($post_action === 'step40') {
2169
                $return = continueReEncryptingUserSharekeysStep40(
2170
                    $post_user_id,
2171
                    $post_self_change,
2172
                    $post_action,
2173
                    $post_start,
2174
                    $post_length,
2175
                    $userInfo['public_key'],
2176
                    $SETTINGS
2177
                );
2178
            }
2179
            
2180
            // STEP 5 - FILES
2181
            elseif ($post_action === 'step50') {
2182
                $return = continueReEncryptingUserSharekeysStep50(
2183
                    $post_user_id,
2184
                    $post_self_change,
2185
                    $post_action,
2186
                    $post_start,
2187
                    $post_length,
2188
                    $userInfo['public_key'],
2189
                    $SETTINGS
2190
                );
2191
            }
2192
            
2193
            // STEP 6 - PERSONAL ITEMS
2194
            elseif ($post_action === 'step60') {
2195
                $return = continueReEncryptingUserSharekeysStep60(
2196
                    $post_user_id,
2197
                    $post_self_change,
2198
                    $post_action,
2199
                    $post_start,
2200
                    $post_length,
2201
                    $userInfo['public_key'],
2202
                    $SETTINGS
2203
                );
2204
            }
2205
            
2206
            // Continu with next step
2207
            return prepareExchangedData(
2208
                array(
2209
                    'error' => false,
2210
                    'message' => '',
2211
                    'step' => isset($return['post_action']) === true ? $return['post_action'] : '',
2212
                    'start' => isset($return['next_start']) === true ? $return['next_start'] : 0,
2213
                    'userId' => $post_user_id,
2214
                    'self_change' => $post_self_change,
2215
                ),
2216
                'encode'
2217
            );
2218
        }
2219
        
2220
        // Nothing to do
2221
        return prepareExchangedData(
2222
            array(
2223
                'error' => false,
2224
                'message' => '',
2225
                'step' => 'finished',
2226
                'start' => 0,
2227
                'userId' => $post_user_id,
2228
                'self_change' => $post_self_change,
2229
            ),
2230
            'encode'
2231
        );
2232
    }
2233
    
2234
    // Nothing to do
2235
    return prepareExchangedData(
2236
        array(
2237
            'error' => true,
2238
            'message' => $lang->get('error_no_user'),
2239
            'extra' => $post_user_id,
2240
        ),
2241
        'encode'
2242
    );
2243
}
2244
2245
function continueReEncryptingUserSharekeysStep10(
2246
    int $post_user_id,
2247
    bool $post_self_change,
2248
    string $post_action,
2249
    int $post_start,
2250
    int $post_length,
2251
    string $user_public_key,
2252
    array $SETTINGS
2253
): array 
2254
{
2255
    $session = SessionManager::getSession();
2256
    // Loop on items
2257
    $rows = DB::query(
2258
        'SELECT id, pw
2259
        FROM ' . prefixTable('items') . '
2260
        WHERE perso = 0
2261
        LIMIT ' . $post_start . ', ' . $post_length
2262
    );
2263
    foreach ($rows as $record) {
2264
        // Get itemKey from current user
2265
        $currentUserKey = DB::queryFirstRow(
2266
            'SELECT share_key, increment_id
2267
            FROM ' . prefixTable('sharekeys_items') . '
2268
            WHERE object_id = %i AND user_id = %i',
2269
            $record['id'],
2270
            $session->get('user-id')
2271
        );
2272
2273
        // do we have any input? (#3481)
2274
        if ($currentUserKey === null || count($currentUserKey) === 0) {
2275
            continue;
2276
        }
2277
2278
        // Decrypt itemkey with admin key
2279
        $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $session->get('user-private_key'));
2280
        
2281
        // Encrypt Item key
2282
        $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
2283
        
2284
        // Save the key in DB
2285
        if ($post_self_change === false) {
2286
            DB::insert(
2287
                prefixTable('sharekeys_items'),
2288
                array(
2289
                    'object_id' => (int) $record['id'],
2290
                    'user_id' => (int) $post_user_id,
2291
                    'share_key' => $share_key_for_item,
2292
                )
2293
            );
2294
        } else {
2295
            // Get itemIncrement from selected user
2296
            if ((int) $post_user_id !== (int) $session->get('user-id')) {
2297
                $currentUserKey = DB::queryFirstRow(
2298
                    'SELECT increment_id
2299
                    FROM ' . prefixTable('sharekeys_items') . '
2300
                    WHERE object_id = %i AND user_id = %i',
2301
                    $record['id'],
2302
                    $post_user_id
2303
                );
2304
2305
                if (DB::count() > 0) {
2306
                    // NOw update
2307
                    DB::update(
2308
                        prefixTable('sharekeys_items'),
2309
                        array(
2310
                            'share_key' => $share_key_for_item,
2311
                        ),
2312
                        'increment_id = %i',
2313
                        $currentUserKey['increment_id']
2314
                    );
2315
                } else {
2316
                    DB::insert(
2317
                        prefixTable('sharekeys_items'),
2318
                        array(
2319
                            'object_id' => (int) $record['id'],
2320
                            'user_id' => (int) $post_user_id,
2321
                            'share_key' => $share_key_for_item,
2322
                        )
2323
                    );
2324
                }
2325
            }
2326
        }
2327
    }
2328
2329
    // SHould we change step?
2330
    DB::query(
2331
        'SELECT *
2332
        FROM ' . prefixTable('items') . '
2333
        WHERE perso = 0'
2334
    );
2335
2336
    $next_start = (int) $post_start + (int) $post_length;
2337
    return [
2338
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2339
        'post_action' => $next_start > DB::count() ? 'step20' : 'step10',
2340
    ];
2341
}
2342
2343
function continueReEncryptingUserSharekeysStep20(
2344
    int $post_user_id,
2345
    bool $post_self_change,
2346
    string $post_action,
2347
    int $post_start,
2348
    int $post_length,
2349
    string $user_public_key,
2350
    array $SETTINGS
2351
): array
2352
{
2353
    $session = SessionManager::getSession();
2354
    // Loop on logs
2355
    $rows = DB::query(
2356
        'SELECT increment_id
2357
        FROM ' . prefixTable('log_items') . '
2358
        WHERE raison LIKE "at_pw :%" AND encryption_type = "teampass_aes"
2359
        LIMIT ' . $post_start . ', ' . $post_length
2360
    );
2361
    foreach ($rows as $record) {
2362
        // Get itemKey from current user
2363
        $currentUserKey = DB::queryFirstRow(
2364
            'SELECT share_key
2365
            FROM ' . prefixTable('sharekeys_logs') . '
2366
            WHERE object_id = %i AND user_id = %i',
2367
            $record['increment_id'],
2368
            $session->get('user-id')
2369
        );
2370
2371
        // do we have any input? (#3481)
2372
        if ($currentUserKey === null || count($currentUserKey) === 0) {
2373
            continue;
2374
        }
2375
2376
        // Decrypt itemkey with admin key
2377
        $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $session->get('user-private_key'));
2378
2379
        // Encrypt Item key
2380
        $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
2381
2382
        // Save the key in DB
2383
        if ($post_self_change === false) {
2384
            DB::insert(
2385
                prefixTable('sharekeys_logs'),
2386
                array(
2387
                    'object_id' => (int) $record['increment_id'],
2388
                    'user_id' => (int) $post_user_id,
2389
                    'share_key' => $share_key_for_item,
2390
                )
2391
            );
2392
        } else {
2393
            // Get itemIncrement from selected user
2394
            if ((int) $post_user_id !== (int) $session->get('user-id')) {
2395
                $currentUserKey = DB::queryFirstRow(
2396
                    'SELECT increment_id
2397
                    FROM ' . prefixTable('sharekeys_items') . '
2398
                    WHERE object_id = %i AND user_id = %i',
2399
                    $record['id'],
2400
                    $post_user_id
2401
                );
2402
            }
2403
2404
            // NOw update
2405
            DB::update(
2406
                prefixTable('sharekeys_logs'),
2407
                array(
2408
                    'share_key' => $share_key_for_item,
2409
                ),
2410
                'increment_id = %i',
2411
                $currentUserKey['increment_id']
2412
            );
2413
        }
2414
    }
2415
2416
    // SHould we change step?
2417
    DB::query(
2418
        'SELECT increment_id
2419
        FROM ' . prefixTable('log_items') . '
2420
        WHERE raison LIKE "at_pw :%" AND encryption_type = "teampass_aes"'
2421
    );
2422
2423
    $next_start = (int) $post_start + (int) $post_length;
2424
    return [
2425
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2426
        'post_action' => $next_start > DB::count() ? 'step30' : 'step20',
2427
    ];
2428
}
2429
2430
function continueReEncryptingUserSharekeysStep30(
2431
    int $post_user_id,
2432
    bool $post_self_change,
2433
    string $post_action,
2434
    int $post_start,
2435
    int $post_length,
2436
    string $user_public_key,
2437
    array $SETTINGS
2438
): array
2439
{
2440
    $session = SessionManager::getSession();
2441
    // Loop on fields
2442
    $rows = DB::query(
2443
        'SELECT id
2444
        FROM ' . prefixTable('categories_items') . '
2445
        WHERE encryption_type = "teampass_aes"
2446
        LIMIT ' . $post_start . ', ' . $post_length
2447
    );
2448
    foreach ($rows as $record) {
2449
        // Get itemKey from current user
2450
        $currentUserKey = DB::queryFirstRow(
2451
            'SELECT share_key
2452
            FROM ' . prefixTable('sharekeys_fields') . '
2453
            WHERE object_id = %i AND user_id = %i',
2454
            $record['id'],
2455
            $session->get('user-id')
2456
        );
2457
2458
        // do we have any input? (#3481)
2459
        if ($currentUserKey === null || count($currentUserKey) === 0) {
2460
            continue;
2461
        }
2462
2463
        // Decrypt itemkey with admin key
2464
        $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $session->get('user-private_key'));
2465
2466
        // Encrypt Item key
2467
        $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
2468
2469
        // Save the key in DB
2470
        if ($post_self_change === false) {
2471
            DB::insert(
2472
                prefixTable('sharekeys_fields'),
2473
                array(
2474
                    'object_id' => (int) $record['id'],
2475
                    'user_id' => (int) $post_user_id,
2476
                    'share_key' => $share_key_for_item,
2477
                )
2478
            );
2479
        } else {
2480
            // Get itemIncrement from selected user
2481
            if ((int) $post_user_id !== (int) $session->get('user-id')) {
2482
                $currentUserKey = DB::queryFirstRow(
2483
                    'SELECT increment_id
2484
                    FROM ' . prefixTable('sharekeys_items') . '
2485
                    WHERE object_id = %i AND user_id = %i',
2486
                    $record['id'],
2487
                    $post_user_id
2488
                );
2489
            }
2490
2491
            // NOw update
2492
            DB::update(
2493
                prefixTable('sharekeys_fields'),
2494
                array(
2495
                    'share_key' => $share_key_for_item,
2496
                ),
2497
                'increment_id = %i',
2498
                $currentUserKey['increment_id']
2499
            );
2500
        }
2501
    }
2502
2503
    // SHould we change step?
2504
    DB::query(
2505
        'SELECT *
2506
        FROM ' . prefixTable('categories_items') . '
2507
        WHERE encryption_type = "teampass_aes"'
2508
    );
2509
2510
    $next_start = (int) $post_start + (int) $post_length;
2511
    return [
2512
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2513
        'post_action' => $next_start > DB::count() ? 'step40' : 'step30',
2514
    ];
2515
}
2516
2517
function continueReEncryptingUserSharekeysStep40(
2518
    int $post_user_id,
2519
    bool $post_self_change,
2520
    string $post_action,
2521
    int $post_start,
2522
    int $post_length,
2523
    string $user_public_key,
2524
    array $SETTINGS
2525
): array
2526
{
2527
    $session = SessionManager::getSession();
2528
    // Loop on suggestions
2529
    $rows = DB::query(
2530
        'SELECT id
2531
        FROM ' . prefixTable('suggestion') . '
2532
        LIMIT ' . $post_start . ', ' . $post_length
2533
    );
2534
    foreach ($rows as $record) {
2535
        // Get itemKey from current user
2536
        $currentUserKey = DB::queryFirstRow(
2537
            'SELECT share_key
2538
            FROM ' . prefixTable('sharekeys_suggestions') . '
2539
            WHERE object_id = %i AND user_id = %i',
2540
            $record['id'],
2541
            $session->get('user-id')
2542
        );
2543
2544
        // do we have any input? (#3481)
2545
        if ($currentUserKey === null || count($currentUserKey) === 0) {
2546
            continue;
2547
        }
2548
2549
        // Decrypt itemkey with admin key
2550
        $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $session->get('user-private_key'));
2551
2552
        // Encrypt Item key
2553
        $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
2554
2555
        // Save the key in DB
2556
        if ($post_self_change === false) {
2557
            DB::insert(
2558
                prefixTable('sharekeys_suggestions'),
2559
                array(
2560
                    'object_id' => (int) $record['id'],
2561
                    'user_id' => (int) $post_user_id,
2562
                    'share_key' => $share_key_for_item,
2563
                )
2564
            );
2565
        } else {
2566
            // Get itemIncrement from selected user
2567
            if ((int) $post_user_id !== (int) $session->get('user-id')) {
2568
                $currentUserKey = DB::queryFirstRow(
2569
                    'SELECT increment_id
2570
                    FROM ' . prefixTable('sharekeys_items') . '
2571
                    WHERE object_id = %i AND user_id = %i',
2572
                    $record['id'],
2573
                    $post_user_id
2574
                );
2575
            }
2576
2577
            // NOw update
2578
            DB::update(
2579
                prefixTable('sharekeys_suggestions'),
2580
                array(
2581
                    'share_key' => $share_key_for_item,
2582
                ),
2583
                'increment_id = %i',
2584
                $currentUserKey['increment_id']
2585
            );
2586
        }
2587
    }
2588
2589
    // SHould we change step?
2590
    DB::query(
2591
        'SELECT *
2592
        FROM ' . prefixTable('suggestion')
2593
    );
2594
2595
    $next_start = (int) $post_start + (int) $post_length;
2596
    return [
2597
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2598
        'post_action' => $next_start > DB::count() ? 'step50' : 'step40',
2599
    ];
2600
}
2601
2602
function continueReEncryptingUserSharekeysStep50(
2603
    int $post_user_id,
2604
    bool $post_self_change,
2605
    string $post_action,
2606
    int $post_start,
2607
    int $post_length,
2608
    string $user_public_key,
2609
    array $SETTINGS
2610
): array
2611
{
2612
    $session = SessionManager::getSession();
2613
    // Loop on files
2614
    $rows = DB::query(
2615
        'SELECT id
2616
        FROM ' . prefixTable('files') . '
2617
        WHERE status = "' . TP_ENCRYPTION_NAME . '"
2618
        LIMIT ' . $post_start . ', ' . $post_length
2619
    ); //aes_encryption
2620
    foreach ($rows as $record) {
2621
        // Get itemKey from current user
2622
        $currentUserKey = DB::queryFirstRow(
2623
            'SELECT share_key
2624
            FROM ' . prefixTable('sharekeys_files') . '
2625
            WHERE object_id = %i AND user_id = %i',
2626
            $record['id'],
2627
            $session->get('user-id')
2628
        );
2629
2630
        // do we have any input? (#3481)
2631
        if ($currentUserKey === null || count($currentUserKey) === 0) {
2632
            continue;
2633
        }
2634
2635
        // Decrypt itemkey with admin key
2636
        $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $session->get('user-private_key'));
2637
2638
        // Encrypt Item key
2639
        $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
2640
2641
        // Save the key in DB
2642
        if ($post_self_change === false) {
2643
            DB::insert(
2644
                prefixTable('sharekeys_files'),
2645
                array(
2646
                    'object_id' => (int) $record['id'],
2647
                    'user_id' => (int) $post_user_id,
2648
                    'share_key' => $share_key_for_item,
2649
                )
2650
            );
2651
        } else {
2652
            // Get itemIncrement from selected user
2653
            if ((int) $post_user_id !== (int) $session->get('user-id')) {
2654
                $currentUserKey = DB::queryFirstRow(
2655
                    'SELECT increment_id
2656
                    FROM ' . prefixTable('sharekeys_items') . '
2657
                    WHERE object_id = %i AND user_id = %i',
2658
                    $record['id'],
2659
                    $post_user_id
2660
                );
2661
            }
2662
2663
            // NOw update
2664
            DB::update(
2665
                prefixTable('sharekeys_files'),
2666
                array(
2667
                    'share_key' => $share_key_for_item,
2668
                ),
2669
                'increment_id = %i',
2670
                $currentUserKey['increment_id']
2671
            );
2672
        }
2673
    }
2674
2675
    // SHould we change step?
2676
    DB::query(
2677
        'SELECT *
2678
        FROM ' . prefixTable('files') . '
2679
        WHERE status = "' . TP_ENCRYPTION_NAME . '"'
2680
    );
2681
2682
    $next_start = (int) $post_start + (int) $post_length;
2683
    return [
2684
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2685
        'post_action' => $next_start > DB::count() ? 'step60' : 'step50',
2686
    ];
2687
}
2688
2689
function continueReEncryptingUserSharekeysStep60(
2690
    int $post_user_id,
2691
    bool $post_self_change,
2692
    string $post_action,
2693
    int $post_start,
2694
    int $post_length,
2695
    string $user_public_key,
2696
    array $SETTINGS
2697
): array
2698
{
2699
    $session = SessionManager::getSession();
2700
    // IF USER IS NOT THE SAME
2701
    if ((int) $post_user_id === (int) $session->get('user-id')) {
2702
        return [
2703
            'next_start' => 0,
2704
            'post_action' => 'finished',
2705
        ];
2706
    }
2707
    
2708
    // Loop on persoanl items
2709
    if (count($session->get('user-personal_folders')) > 0) {
2710
        $rows = DB::query(
2711
            'SELECT id, pw
2712
            FROM ' . prefixTable('items') . '
2713
            WHERE perso = 1 AND id_tree IN %ls AND encryption_type = %s
2714
            LIMIT ' . $post_start . ', ' . $post_length,
2715
            $session->get('user-personal_folders'),
2716
            "defuse"
2717
        );
2718
        foreach ($rows as $record) {
2719
            // Get itemKey from current user
2720
            $currentUserKey = DB::queryFirstRow(
2721
                'SELECT share_key, increment_id
2722
                FROM ' . prefixTable('sharekeys_items') . '
2723
                WHERE object_id = %i AND user_id = %i',
2724
                $record['id'],
2725
                $session->get('user-id')
2726
            );
2727
2728
            // Decrypt itemkey with admin key
2729
            $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $session->get('user-private_key'));
2730
2731
            // Encrypt Item key
2732
            $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
2733
2734
            // Save the key in DB
2735
            if ($post_self_change === false) {
2736
                DB::insert(
2737
                    prefixTable('sharekeys_items'),
2738
                    array(
2739
                        'object_id' => (int) $record['id'],
2740
                        'user_id' => (int) $post_user_id,
2741
                        'share_key' => $share_key_for_item,
2742
                    )
2743
                );
2744
            } else {
2745
                // Get itemIncrement from selected user
2746
                if ((int) $post_user_id !== (int) $session->get('user-id')) {
2747
                    $currentUserKey = DB::queryFirstRow(
2748
                        'SELECT increment_id
2749
                        FROM ' . prefixTable('sharekeys_items') . '
2750
                        WHERE object_id = %i AND user_id = %i',
2751
                        $record['id'],
2752
                        $post_user_id
2753
                    );
2754
                }
2755
2756
                // NOw update
2757
                DB::update(
2758
                    prefixTable('sharekeys_items'),
2759
                    array(
2760
                        'share_key' => $share_key_for_item,
2761
                    ),
2762
                    'increment_id = %i',
2763
                    $currentUserKey['increment_id']
2764
                );
2765
            }
2766
        }
2767
    }
2768
2769
    // SHould we change step?
2770
    DB::query(
2771
        'SELECT *
2772
        FROM ' . prefixTable('items') . '
2773
        WHERE perso = 0'
2774
    );
2775
2776
    $next_start = (int) $post_start + (int) $post_length;
2777
    return [
2778
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2779
        'post_action' => $next_start > DB::count() ? 'finished' : 'step60',
2780
    ];
2781
}
2782
2783
function migrateTo3_DoUserPersonalItemsEncryption(
2784
    int $post_user_id,
2785
    int $post_start,
2786
    int $post_length,
2787
    int $post_counterItemsToTreat,
2788
    string $post_user_psk,
2789
    array $SETTINGS
2790
) {
2791
    $next_step = 'psk';
2792
    
2793
    $session = SessionManager::getSession();
2794
    $lang = new Language($session->get('user-language') ?? 'english');
2795
    
2796
    if (isUserIdValid($post_user_id) === true) {
2797
        // Check if user exists
2798
        $userInfo = DB::queryFirstRow(
2799
            'SELECT public_key, encrypted_psk
2800
            FROM ' . prefixTable('users') . '
2801
            WHERE id = %i',
2802
            $post_user_id
2803
        );
2804
        if (DB::count() > 0) {
2805
            // check if psk is correct.
2806
            if (empty($userInfo['encrypted_psk']) === false) {//echo $post_user_psk." ;; ".$userInfo['encrypted_psk']." ;; ";
2807
                $user_key_encoded = defuse_validate_personal_key(
2808
                    html_entity_decode($post_user_psk), // convert tspecial string back to their original characters due to FILTER_SANITIZE_FULL_SPECIAL_CHARS
2809
                    $userInfo['encrypted_psk']
2810
                );
2811
2812
                if (strpos($user_key_encoded, "Error ") !== false) {
2813
                    return prepareExchangedData(
2814
                        array(
2815
                            'error' => true,
2816
                            'message' => $lang->get('bad_psk'),
2817
                        ),
2818
                        'encode'
2819
                    );
2820
                }
2821
2822
                // Get number of user's personal items with no AES encryption
2823
                if ($post_counterItemsToTreat === -1) {
2824
                    DB::query(
2825
                        'SELECT id
2826
                        FROM ' . prefixTable('items') . '
2827
                        WHERE perso = 1 AND id_tree IN %ls AND encryption_type != %s',
2828
                        $session->get('user-personal_folders'),
2829
                        'teampass_aes'
2830
                    );
2831
                    $countUserPersonalItems = DB::count();
2832
                } else {
2833
                    $countUserPersonalItems = $post_counterItemsToTreat;
2834
                }
2835
2836
                // Loop on persoanl items
2837
                $rows = DB::query(
2838
                    'SELECT id, pw
2839
                    FROM ' . prefixTable('items') . '
2840
                    WHERE perso = 1 AND id_tree IN %ls AND encryption_type != %s
2841
                    LIMIT ' . $post_length,
2842
                    $session->get('user-personal_folders'),
2843
                    'teampass_aes'
2844
                );
2845
                foreach ($rows as $record) {
2846
                    // Decrypt with Defuse
2847
                    $passwd = cryption(
2848
                        $record['pw'],
2849
                        $user_key_encoded,
2850
                        'decrypt',
2851
                        $SETTINGS
2852
                    );
2853
2854
                    // Encrypt with Object Key
2855
                    $cryptedStuff = doDataEncryption(html_entity_decode($passwd['string']));
2856
2857
                    // Store new password in DB
2858
                    DB::update(
2859
                        prefixTable('items'),
2860
                        array(
2861
                            'pw' => $cryptedStuff['encrypted'],
2862
                            'encryption_type' => 'teampass_aes',
2863
                        ),
2864
                        'id = %i',
2865
                        $record['id']
2866
                    );
2867
2868
                    // Insert in DB the new object key for this item by user
2869
                    DB::insert(
2870
                        prefixTable('sharekeys_items'),
2871
                        array(
2872
                            'object_id' => (int) $record['id'],
2873
                            'user_id' => (int) $post_user_id,
2874
                            'share_key' => encryptUserObjectKey($cryptedStuff['objectKey'], $userInfo['public_key']),
2875
                        )
2876
                    );
2877
2878
2879
                    // Does this item has Files?
2880
                    // Loop on files
2881
                    $rows = DB::query(
2882
                        'SELECT id, file
2883
                        FROM ' . prefixTable('files') . '
2884
                        WHERE status != %s
2885
                        AND id_item = %i',
2886
                        TP_ENCRYPTION_NAME,
2887
                        $record['id']
2888
                    );
2889
                    //aes_encryption
2890
                    foreach ($rows as $record2) {
2891
                        // Now decrypt the file
2892
                        prepareFileWithDefuse(
2893
                            'decrypt',
2894
                            $SETTINGS['path_to_upload_folder'] . '/' . $record2['file'],
2895
                            $SETTINGS['path_to_upload_folder'] . '/' . $record2['file'] . '.delete',
2896
                            $SETTINGS,
2897
                            $post_user_psk
2898
                        );
2899
2900
                        // Encrypt the file
2901
                        $encryptedFile = encryptFile($record2['file'] . '.delete', $SETTINGS['path_to_upload_folder']);
2902
2903
                        DB::update(
2904
                            prefixTable('files'),
2905
                            array(
2906
                                'file' => $encryptedFile['fileHash'],
2907
                                'status' => TP_ENCRYPTION_NAME,
2908
                            ),
2909
                            'id = %i',
2910
                            $record2['id']
2911
                        );
2912
2913
                        // Save key
2914
                        DB::insert(
2915
                            prefixTable('sharekeys_files'),
2916
                            array(
2917
                                'object_id' => (int) $record2['id'],
2918
                                'user_id' => (int) $session->get('user-id'),
2919
                                'share_key' => encryptUserObjectKey($encryptedFile['objectKey'], $session->get('user-public_key')),
2920
                            )
2921
                        );
2922
2923
                        // Unlink original file
2924
                        unlink($SETTINGS['path_to_upload_folder'] . '/' . $record2['file']);
2925
                    }
2926
                }
2927
2928
                // SHould we change step?
2929
                $next_start = (int) $post_start + (int) $post_length;
2930
                DB::query(
2931
                    'SELECT id
2932
                    FROM ' . prefixTable('items') . '
2933
                    WHERE perso = 1 AND id_tree IN %ls AND encryption_type != %s',
2934
                    $session->get('user-personal_folders'),
2935
                    'teampass_aes'
2936
                );
2937
                if (DB::count() === 0 || ($next_start - $post_length) >= $countUserPersonalItems) {
2938
                    // Now update user
2939
                    DB::update(
2940
                        prefixTable('users'),
2941
                        array(
2942
                            'special' => 'none',
2943
                            'upgrade_needed' => 0,
2944
                            'encrypted_psk' => '',
2945
                        ),
2946
                        'id = %i',
2947
                        $post_user_id
2948
                    );
2949
2950
                    $next_step = 'finished';
2951
                    $next_start = 0;
2952
                }
2953
2954
                // Continu with next step
2955
                return prepareExchangedData(
2956
                    array(
2957
                        'error' => false,
2958
                        'message' => '',
2959
                        'step' => $next_step,
2960
                        'start' => $next_start,
2961
                        'userId' => $post_user_id
2962
                    ),
2963
                    'encode'
2964
                );
2965
            }
2966
        }
2967
        
2968
        // Nothing to do
2969
        return prepareExchangedData(
2970
            array(
2971
                'error' => true,
2972
                'message' => $lang->get('error_no_user'),
2973
            ),
2974
            'encode'
2975
        );
2976
    }
2977
    
2978
    // Nothing to do
2979
    return prepareExchangedData(
2980
        array(
2981
            'error' => true,
2982
            'message' => $lang->get('error_no_user'),
2983
        ),
2984
        'encode'
2985
    );
2986
}
2987
2988
2989
function getUserInfo(
2990
    int $post_user_id,
2991
    string $post_fields,
2992
    array $SETTINGS
2993
)
2994
{
2995
    // Load user's language
2996
    $session = SessionManager::getSession();
2997
    $lang = new Language($session->get('user-language') ?? 'english');
2998
    
2999
    if (isUserIdValid($post_user_id) === true) {
3000
        // Get user info
3001
        $userData = DB::queryFirstRow(
3002
            'SELECT '.$post_fields.'
3003
            FROM ' . prefixTable('users') . '
3004
            WHERE id = %i',
3005
            $post_user_id
3006
        );
3007
        if (DB::count() > 0) {
3008
            return prepareExchangedData(
3009
                array(
3010
                    'error' => false,
3011
                    'message' => '',
3012
                    'queryResults' => $userData,
3013
                ),
3014
                'encode'
3015
            );
3016
        }
3017
    }
3018
    return prepareExchangedData(
3019
        array(
3020
            'error' => true,
3021
            'message' => $lang->get('error_no_user'),
3022
        ),
3023
        'encode'
3024
    );
3025
}
3026
3027
/**
3028
 * Change user auth password
3029
 *
3030
 * @param integer $post_user_id
3031
 * @param string $post_current_pwd
3032
 * @param string $post_new_pwd
3033
 * @param array $SETTINGS
3034
 * @return string
3035
 */
3036
function changeUserAuthenticationPassword(
3037
    int $post_user_id,
3038
    string $post_current_pwd,
3039
    string $post_new_pwd,
3040
    array $SETTINGS
3041
)
3042
{
3043
    $session = SessionManager::getSession();
3044
    $lang = new Language($session->get('user-language') ?? 'english');
3045
    
3046
    if (isUserIdValid($post_user_id) === true) {
3047
        // Get user info
3048
        $userData = DB::queryFirstRow(
3049
            'SELECT auth_type, login, private_key
3050
            FROM ' . prefixTable('users') . '
3051
            WHERE id = %i',
3052
            $post_user_id
3053
        );
3054
        if (DB::count() > 0 && empty($userData['private_key']) === false) {
3055
            // Now check if current password is correct
3056
            // For this, just check if it is possible to decrypt the privatekey
3057
            // And compare it to the one in session
3058
            try {
3059
                $privateKey = decryptPrivateKey($post_current_pwd, $userData['private_key']);
3060
            } catch (Exception $e) {
3061
                return prepareExchangedData(
3062
                    array(
3063
                        'error' => true,
3064
                        'message' => $lang->get('bad_password'),
3065
                    ),
3066
                    'encode'
3067
                );
3068
            }
3069
3070
            $lang = new Language($session->get('user-language') ?? 'english');
3071
3072
            if ($session->get('user-private_key') === $privateKey) {
3073
                // Encrypt it with new password
3074
                $hashedPrivateKey = encryptPrivateKey($post_new_pwd, $privateKey);
3075
3076
                // Generate new hash for auth password
3077
                $passwordManager = new PasswordManager();
3078
3079
                // Prepare variables
3080
                $newPw = $passwordManager->hashPassword($post_new_pwd);
3081
3082
                // Update user account
3083
                DB::update(
3084
                    prefixTable('users'),
3085
                    array(
3086
                        'private_key' => $hashedPrivateKey,
3087
                        'pw' => $newPw,
3088
                        'special' => 'none',
3089
                        'last_pw_change' => time(),
3090
                    ),
3091
                    'id = %i',
3092
                    $post_user_id
3093
                );
3094
3095
                $session->set('user-private_key', $privateKey);
3096
3097
                return prepareExchangedData(
3098
                    array(
3099
                        'error' => false,
3100
                        'message' => $lang->get('done'),'',
3101
                    ),
3102
                    'encode'
3103
                );
3104
            }
3105
            
3106
            // ERROR
3107
            return prepareExchangedData(
3108
                array(
3109
                    'error' => true,
3110
                    'message' => $lang->get('bad_password'),
3111
                ),
3112
                'encode'
3113
            );
3114
        }
3115
    }
3116
        
3117
    return prepareExchangedData(
3118
        array(
3119
            'error' => true,
3120
            'message' => $lang->get('error_no_user'),
3121
        ),
3122
        'encode'
3123
    );
3124
}
3125
3126
/**
3127
 * Change user LDAP auth password
3128
 *
3129
 * @param integer $post_user_id
3130
 * @param string $post_previous_pwd
3131
 * @param string $post_current_pwd
3132
 * @param array $SETTINGS
3133
 * @return string
3134
 */            
3135
function changeUserLDAPAuthenticationPassword(
3136
    int $post_user_id,
3137
    string $post_previous_pwd,
3138
    string $post_current_pwd,
3139
    array $SETTINGS
3140
)
3141
{
3142
    $session = SessionManager::getSession();
3143
    // Load user's language
3144
    $lang = new Language($session->get('user-language') ?? 'english');
3145
    
3146
    if (isUserIdValid($post_user_id) === true) {
3147
        // Get user info
3148
        $userData = DB::queryFirstRow(
3149
            'SELECT auth_type, login, private_key, special
3150
            FROM ' . prefixTable('users') . '
3151
            WHERE id = %i',
3152
            $post_user_id
3153
        );
3154
        
3155
        if (DB::count() > 0 && empty($userData['private_key']) === false) {
3156
            // Now check if current password is correct (only if not ldap)
3157
            if ($userData['auth_type'] === 'ldap' && $userData['special'] === 'auth-pwd-change') {
3158
                // As it is a change for an LDAP user
3159
                
3160
                // Now check if current password is correct
3161
                // For this, just check if it is possible to decrypt the privatekey
3162
                // And compare it to the one in session
3163
                $privateKey = decryptPrivateKey($post_previous_pwd, $userData['private_key']);
3164
3165
                // Encrypt it with new password
3166
                $hashedPrivateKey = encryptPrivateKey($post_current_pwd, $privateKey);
3167
3168
                // Update user account
3169
                DB::update(
3170
                    prefixTable('users'),
3171
                    array(
3172
                        'private_key' => $hashedPrivateKey,
3173
                        'special' => 'none',
3174
                    ),
3175
                    'id = %i',
3176
                    $post_user_id
3177
                );
3178
3179
                $session->set('user-private_key', $privateKey);
3180
3181
                return prepareExchangedData(
3182
                    array(
3183
                        'error' => false,
3184
                        'message' => $lang->get('done'),'',
3185
                    ),
3186
                    'encode'
3187
                );
3188
            }
3189
3190
            // For this, just check if it is possible to decrypt the privatekey
3191
            // And try to decrypt one existing key
3192
            $privateKey = decryptPrivateKey($post_previous_pwd, $userData['private_key']);
3193
3194
            if (empty($privateKey) === true) {
3195
                return prepareExchangedData(
3196
                    array(
3197
                        'error' => true,
3198
                        'message' => $lang->get('password_is_not_correct'),
3199
                    ),
3200
                    'encode'
3201
                );
3202
            }
3203
3204
            // Get one itemKey from current user
3205
            $currentUserKey = DB::queryFirstRow(
3206
                'SELECT share_key, increment_id
3207
                FROM ' . prefixTable('sharekeys_items') . '
3208
                WHERE user_id = %i
3209
                LIMIT 1',
3210
                $post_user_id
3211
            );
3212
3213
            if (is_countable($currentUserKey) && count($currentUserKey) > 0) {
3214
                // Decrypt itemkey with user key
3215
                // use old password to decrypt private_key
3216
                $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $privateKey);
3217
                
3218
                if (empty(base64_decode($itemKey)) === false) {
3219
                    // GOOD password
3220
                    // Encrypt it with current password
3221
                    $hashedPrivateKey = encryptPrivateKey($post_current_pwd, $privateKey);
3222
                    
3223
                    // Update user account
3224
                    DB::update(
3225
                        prefixTable('users'),
3226
                        array(
3227
                            'private_key' => $hashedPrivateKey,
3228
                            'special' => 'none',
3229
                        ),
3230
                        'id = %i',
3231
                        $post_user_id
3232
                    );
3233
                    
3234
                    $lang = new Language($session->get('user-language') ?? 'english');
3235
                    $session->set('user-private_key', $privateKey);
3236
3237
                    return prepareExchangedData(
3238
                        array(
3239
                            'error' => false,
3240
                            'message' => $lang->get('done'),
3241
                        ),
3242
                        'encode'
3243
                    );
3244
                }
3245
            }
3246
            
3247
            // ERROR
3248
            return prepareExchangedData(
3249
                array(
3250
                    'error' => true,
3251
                    'message' => $lang->get('bad_password'),
3252
                ),
3253
                'encode'
3254
            );
3255
        }
3256
    }
3257
3258
    // ERROR
3259
    return prepareExchangedData(
3260
        array(
3261
            'error' => true,
3262
            'message' => $lang->get('error_no_user'),
3263
        ),
3264
        'encode'
3265
    );
3266
}
3267
3268
/**
3269
 * Change user LDAP auth password
3270
 *
3271
 * @param integer $post_user_id
3272
 * @param string $post_current_pwd
3273
 * @param string $post_new_pwd
3274
 * @param array $SETTINGS
3275
 * @return string
3276
 */
3277
function increaseSessionDuration(
3278
    int $duration
3279
): string
3280
{
3281
    $session = SessionManager::getSession();
3282
    // check if session is not already expired.
3283
    if ($session->get('user-session_duration') > time()) {
3284
        // Calculate end of session
3285
        $session->set('user-session_duration', (int) $session->get('user-session_duration') + $duration);
3286
        // Update table
3287
        DB::update(
3288
            prefixTable('users'),
3289
            array(
3290
                'session_end' => $session->get('user-session_duration'),
3291
            ),
3292
            'id = %i',
3293
            $session->get('user-id')
3294
        );
3295
        // Return data
3296
        return '[{"new_value":"' . $session->get('user-session_duration') . '"}]';
3297
    }
3298
    
3299
    return '[{"new_value":"expired"}]';
3300
}
3301
3302
function generateAnOTP(string $label, bool $with_qrcode = false, string $secretKey = ''): string
3303
{
3304
    // generate new secret
3305
    $tfa = new TwoFactorAuth();
3306
    if ($secretKey === '') {
3307
        $secretKey = $tfa->createSecret();
3308
    }
3309
3310
    // generate new QR
3311
    if ($with_qrcode === true) {
3312
        $qrcode = $tfa->getQRCodeImageAsDataUri(
3313
            $label,
3314
            $secretKey
3315
        );
3316
    }
3317
3318
    // ERROR
3319
    return prepareExchangedData(
3320
        array(
3321
            'error' => false,
3322
            'message' => '',
3323
            'secret' => $secretKey,
3324
            'qrcode' => $qrcode ?? '',
3325
        ),
3326
        'encode'
3327
    );
3328
}