Passed
Pull Request — master (#4455)
by
unknown
07:19
created

changePrivateKeyEncryptionPassword()   B

Complexity

Conditions 10
Paths 9

Size

Total Lines 90
Code Lines 50

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
cc 10
eloc 50
c 2
b 0
f 1
nc 9
nop 5
dl 0
loc 90
rs 7.2242

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

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