Issues (26)

Security Analysis    no vulnerabilities found

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  Header Injection
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

sources/main.queries.php (1 issue)

Severity
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
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
 * @param string $post_action_type
3241
 * @return string
3242
 */            
3243
function changeUserLDAPAuthenticationPassword(
3244
    int $post_user_id,
3245
    string $post_previous_pwd,
3246
    string $post_current_pwd
3247
)
3248
{
3249
    $session = SessionManager::getSession();
3250
    // Load user's language
3251
    $lang = new Language($session->get('user-language') ?? 'english');
3252
    
3253
    if (isUserIdValid($post_user_id) === true) {
3254
        // Get user info
3255
        $userData = DB::queryFirstRow(
3256
            'SELECT u.auth_type, u.login, u.private_key, u.special
3257
            FROM ' . prefixTable('users') . ' AS u
3258
            WHERE u.id = %i',
3259
            $post_user_id
3260
        );
3261
3262
        if (DB::count() > 0) {
3263
            // 
3264
            if ($userData['auth_type'] === 'ldap' && empty($userData['private_key']) === false && $userData['special'] === 'encrypt_personal_items') {
3265
                // Now check if current password is correct (only if not ldap)
3266
                if ($userData['special'] === 'auth-pwd-change') {
3267
                    // As it is a change for an LDAP user
3268
                    
3269
                    // Now check if current password is correct
3270
                    // For this, just check if it is possible to decrypt the privatekey
3271
                    // And compare it to the one in session
3272
                    $privateKey = decryptPrivateKey($post_previous_pwd, $userData['private_key']);
3273
3274
                    // Encrypt it with new password
3275
                    $hashedPrivateKey = encryptPrivateKey($post_current_pwd, $privateKey);
3276
3277
                    // Update user account
3278
                    DB::update(
3279
                        prefixTable('users'),
3280
                        array(
3281
                            'private_key' => $hashedPrivateKey,
3282
                            'special' => 'none',
3283
                        ),
3284
                        'id = %i',
3285
                        $post_user_id
3286
                    );
3287
3288
                    $session->set('user-private_key', $privateKey);
3289
3290
                    return prepareExchangedData(
3291
                        array(
3292
                            'error' => false,
3293
                            'message' => $lang->get('done'),'',
3294
                        ),
3295
                        'encode'
3296
                    );
3297
                }
3298
3299
                // For this, just check if it is possible to decrypt the privatekey
3300
                // And try to decrypt one existing key
3301
                $privateKey = decryptPrivateKey($post_previous_pwd, $userData['private_key']);
3302
3303
                if (empty($privateKey) === true) {
3304
                    return prepareExchangedData(
3305
                        array(
3306
                            'error' => true,
3307
                            'message' => $lang->get('password_is_not_correct'),
3308
                        ),
3309
                        'encode'
3310
                    );
3311
                }
3312
                // Get one itemKey from current user
3313
                $currentUserKey = DB::queryFirstRow(
3314
                    'SELECT ski.share_key, ski.increment_id, l.id_user
3315
                    FROM ' . prefixTable('sharekeys_items') . ' AS ski
3316
                    INNER JOIN ' . prefixTable('log_items') . ' AS l ON ski.object_id = l.id_item
3317
                    WHERE ski.user_id = %i
3318
                    ORDER BY RAND()
3319
                    LIMIT 1',
3320
                    $post_user_id
3321
                );
3322
3323
                if (is_countable($currentUserKey) && count($currentUserKey) > 0) {
3324
                    // Decrypt itemkey with user key
3325
                    // use old password to decrypt private_key
3326
                    $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $privateKey);
3327
                    
3328
                    if (empty(base64_decode($itemKey)) === false) {
3329
                        // GOOD password
3330
                        // Encrypt it with current password
3331
                        $hashedPrivateKey = encryptPrivateKey($post_current_pwd, $privateKey);
3332
                        
3333
                        // Update user account
3334
                        DB::update(
3335
                            prefixTable('users'),
3336
                            array(
3337
                                'private_key' => $hashedPrivateKey,
3338
                                'special' => 'none',
3339
                            ),
3340
                            'id = %i',
3341
                            $post_user_id
3342
                        );
3343
                        
3344
                        $lang = new Language($session->get('user-language') ?? 'english');
3345
                        $session->set('user-private_key', $privateKey);
3346
3347
                        return prepareExchangedData(
3348
                            array(
3349
                                'error' => false,
3350
                                'message' => $lang->get('done'),
3351
                            ),
3352
                            'encode'
3353
                        );
3354
                    }
3355
                }
3356
                
3357
                // ERROR
3358
                return prepareExchangedData(
3359
                    array(
3360
                        'error' => true,
3361
                        'message' => $lang->get('bad_password'),
3362
                    ),
3363
                    'encode'
3364
                );
3365
            } elseif ($userData['special'] === 'encrypt_personal_items') {
3366
                // We need to find a valid previous private key
3367
                $validPreviousKey = findValidPreviousPrivateKey(
3368
                    $post_previous_pwd,
3369
                    $post_user_id,
3370
                    $userData
3371
                );
3372
3373
                if ($validPreviousKey['private_key'] !== null) {
3374
                    // Decrypt all personal items with this key
3375
                    // Launch the re-encryption process for personal items
3376
                    // Create process
3377
                    DB::insert(
3378
                        prefixTable('background_tasks'),
3379
                        array(
3380
                            'created_at' => time(),
3381
                            'process_type' => 'create_user_keys',
3382
                            'arguments' => json_encode([
3383
                                'new_user_id' => (int) $post_user_id,
3384
                                'new_user_pwd' => cryption($post_previous_pwd, '','encrypt')['string'],
3385
                                'new_user_private_key' => cryption($validPreviousKey['private_key'], '','encrypt')['string'],
3386
                                'send_email' => 0,
3387
                                'otp_provided_new_value' => 0,
3388
                                'user_self_change' => 1,
3389
                                'only_personal_items' => 1,
3390
                            ]),
3391
                        )
3392
                    );
3393
                    $processId = DB::insertId();
3394
3395
                    // Create tasks
3396
                    createUserTasks($processId, NUMBER_ITEMS_IN_BATCH);
3397
3398
                    // update user's new status
3399
                    DB::update(
3400
                        prefixTable('users'),
3401
                        [
3402
                            'is_ready_for_usage' => 1,
3403
                            'otp_provided' => 1,
3404
                            'ongoing_process_id' => $processId,
3405
                            'special' => 'none',
3406
                        ],
3407
                        'id=%i',
3408
                        $post_user_id
3409
                    );
3410
3411
                    return prepareExchangedData(
3412
                        array(
3413
                            'error' => false,
3414
                            'message' => $lang->get('done'),
3415
                        ),
3416
                        'encode'
3417
                    );
3418
                }
3419
                return prepareExchangedData(
3420
                    array(
3421
                        'error' => true,
3422
                        'message' => $lang->get('no_previous_valide_private_key'),
3423
                    ),
3424
                    'encode'
3425
                );
3426
            }
3427
        }
3428
    }
3429
3430
    // ERROR
3431
    return prepareExchangedData(
3432
        array(
3433
            'error' => true,
3434
            'message' => $lang->get('error_no_user'),
3435
        ),
3436
        'encode'
3437
    );
3438
}
3439
3440
/**
3441
 * Try to find a valid previous private key by testing all previous keys
3442
 * until one is able to decrypt the share_key of one item
3443
 * @param string $previousPassword
3444
 * @param int $userId
3445
 * @param array $userData
3446
 * @return array|null
3447
 */
3448
function findValidPreviousPrivateKey($previousPassword, $userId, $userData) {
3449
    // Retrieve all user's private keys in descending order (most recent first)
3450
    $privateKeys = DB::query(
3451
        "SELECT 
3452
            id,
3453
            private_key,
3454
            created_at
3455
        FROM " . prefixTable('user_private_keys') . "
3456
        WHERE user_id = %i
3457
        ORDER BY created_at DESC, id DESC",
3458
        $userId
3459
    );
3460
    
3461
    if (empty($privateKeys)) {
3462
        return null;
3463
    }
3464
    
3465
    // Loop through all private keys
3466
    foreach ($privateKeys as $row) {
3467
        $encryptedPrivateKey = $row['private_key'];
3468
        
3469
        // Attempt to decrypt the private key with the previous password
3470
        $privateKey = decryptPrivateKey($previousPassword, $encryptedPrivateKey);
3471
        
3472
        // If decryption succeeded (key not null)
3473
        if ($privateKey !== null) {
3474
            // Select one personal item share_key to test decryption
3475
            $currentUserItemKey = DB::queryFirstRow(
3476
                'SELECT si.share_key, si.increment_id, l.id_user, i.perso
3477
                FROM ' . prefixTable('sharekeys_items') . ' AS si
3478
                INNER JOIN ' . prefixTable('log_items') . ' AS l ON si.object_id = l.id_item
3479
                INNER JOIN ' . prefixTable('items') . ' AS i ON i.id = l.id_item
3480
                WHERE si.user_id = %i AND i.perso = 1 AND si.share_key != ""
3481
                ORDER BY RAND()
3482
                LIMIT 1',
3483
                $userId
3484
            );
3485
3486
            if (is_countable($currentUserItemKey) && count($currentUserItemKey) > 0) {
3487
                // Decrypt itemkey with user key
3488
                // use old password to decrypt private_key
3489
                $itemKey = decryptUserObjectKey($currentUserItemKey['share_key'], $privateKey);
3490
                if (empty(base64_decode($itemKey)) === false) {                
3491
                    return [
3492
                        'private_key' => $privateKey
3493
                    ];
3494
                }
3495
            }
3496
        }
3497
    }
3498
    
3499
    return null;
3500
}
3501
3502
/**
3503
 * Change user LDAP auth password
3504
 *
3505
 * @param integer $post_user_id
3506
 * @param string $post_current_pwd
3507
 * @param string $post_new_pwd
3508
 * @param array $SETTINGS
3509
 * @return string
3510
 */
3511
function increaseSessionDuration(
3512
    int $duration
3513
): string
3514
{
3515
    $session = SessionManager::getSession();
3516
    // check if session is not already expired.
3517
    if ($session->get('user-session_duration') > time()) {
3518
        // Calculate end of session
3519
        $session->set('user-session_duration', (int) $session->get('user-session_duration') + $duration);
3520
        // Update table
3521
        DB::update(
3522
            prefixTable('users'),
3523
            array(
3524
                'session_end' => $session->get('user-session_duration'),
3525
            ),
3526
            'id = %i',
3527
            $session->get('user-id')
3528
        );
3529
        // Return data
3530
        return '[{"new_value":"' . $session->get('user-session_duration') . '"}]';
3531
    }
3532
    
3533
    return '[{"new_value":"expired"}]';
3534
}
3535
3536
function generateAnOTP(string $label, bool $with_qrcode = false, string $secretKey = ''): string
3537
{
3538
    // generate new secret
3539
    $tfa = new TwoFactorAuth();
3540
    if ($secretKey === '') {
3541
        $secretKey = $tfa->createSecret();
3542
    }
3543
3544
    // generate new QR
3545
    if ($with_qrcode === true) {
3546
        $qrcode = $tfa->getQRCodeImageAsDataUri(
3547
            $label,
3548
            $secretKey
3549
        );
3550
    }
3551
3552
    // ERROR
3553
    return prepareExchangedData(
3554
        array(
3555
            'error' => false,
3556
            'message' => '',
3557
            'secret' => $secretKey,
3558
            'qrcode' => $qrcode ?? '',
3559
        ),
3560
        'encode'
3561
    );
3562
}