keyHandler()   F
last analyzed

Complexity

Conditions 28
Paths 25

Size

Total Lines 221
Code Lines 121

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 28
eloc 121
c 3
b 0
f 0
nc 25
nop 3
dl 0
loc 221
rs 3.3333

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

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