Passed
Push — master ( d2bec3...fae22c )
by Nils
07:29 queued 46s
created

keyHandler()   F

Complexity

Conditions 24
Paths 31

Size

Total Lines 186
Code Lines 103

Duplication

Lines 0
Ratio 0 %

Importance

Changes 7
Bugs 0 Features 0
Metric Value
cc 24
eloc 103
c 7
b 0
f 0
nc 31
nop 3
dl 0
loc 186
rs 3.3333

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