Passed
Push — teampass_3.0 ( 0b1c1e...99aa1e )
by Nils
05:25
created

changePassword()   C

Complexity

Conditions 9
Paths 12

Size

Total Lines 141
Code Lines 79

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 9
eloc 79
nc 12
nop 6
dl 0
loc 141
rs 6.9026
c 1
b 1
f 0

How to fix   Long Method   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Teampass - a collaborative passwords manager.
7
 * ---
8
 * This library is distributed in the hope that it will be useful,
9
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11
 * ---
12
 * @project   Teampass
13
 * @file      main.queries.php
14
 * ---
15
 * @author    Nils Laumaillé ([email protected])
16
 * @copyright 2009-2021 Teampass.net
17
 * @license   https://spdx.org/licenses/GPL-3.0-only.html#licenseText GPL-3.0
18
 * ---
19
 * @see       https://www.teampass.net
20
 */
21
22
if (isset($_SESSION) === false) {
23
    include_once 'SecureHandler.php';
24
    session_name('teampass_session');
25
    session_start();
26
    $_SESSION['CPM'] = 1;
27
}
28
29
if (isset($_SESSION['CPM']) === false || $_SESSION['CPM'] !== 1) {
30
    $_SESSION['error']['code'] = '1004';
31
    include '../error.php';
32
    exit();
33
}
34
35
// Load config
36
if (file_exists('../includes/config/tp.config.php')) {
37
    include '../includes/config/tp.config.php';
38
} elseif (file_exists('./includes/config/tp.config.php')) {
39
    include './includes/config/tp.config.php';
40
} else {
41
    throw new Exception("Error file '/includes/config/tp.config.php' not exists", 1);
42
}
43
44
// Define Timezone
45
date_default_timezone_set(isset($SETTINGS['timezone']) === true ? $SETTINGS['timezone'] : 'UTC');
46
47
// DO CHECKS
48
require_once $SETTINGS['cpassman_dir'] . '/includes/config/include.php';
49
require_once $SETTINGS['cpassman_dir'] . '/sources/checks.php';
50
$post_type = filter_input(INPUT_POST, 'type', FILTER_SANITIZE_STRING);
51
if (
52
    isset($post_type) === true
53
    && ($post_type === 'ga_generate_qr'
54
        //|| $post_type === 'recovery_send_pw_by_email'
55
        //|| $post_type === 'recovery_generate_new_password'
56
        || $post_type === 'get_teampass_settings')
57
) {
58
    // continue
59
    mainQuery($SETTINGS);
60
} elseif (
61
    isset($_SESSION['user_id']) === true
62
    && checkUser($_SESSION['user_id'], $_SESSION['key'], 'home', $SETTINGS) === false
63
) {
64
    $_SESSION['error']['code'] = ERR_NOT_ALLOWED; //not allowed page
65
    include $SETTINGS['cpassman_dir'] . '/error.php';
66
    exit();
67
} elseif ((isset($_SESSION['user_id']) === true
68
        && isset($_SESSION['key'])) === true
69
    || (isset($post_type) === true
70
        //&& $post_type === 'change_user_language'
71
        && null !== filter_input(INPUT_POST, 'data', FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES))
72
) {
73
    // continue
74
    mainQuery($SETTINGS);
75
} else {
76
    $_SESSION['error']['code'] = ERR_NOT_ALLOWED; //not allowed page
77
    include $SETTINGS['cpassman_dir'] . '/error.php';
78
    exit();
79
}
80
81
/**
82
 * Undocumented function.
83
 */
84
function mainQuery(array $SETTINGS)
85
{
86
    header('Content-type: text/html; charset=utf-8');
87
    header('Cache-Control: no-cache, must-revalidate');
88
    error_reporting(E_ERROR);
89
90
91
    // Includes
92
    include_once $SETTINGS['cpassman_dir'] . '/includes/language/' . $_SESSION['user_language'] . '.php';
93
    include_once $SETTINGS['cpassman_dir'] . '/includes/config/settings.php';
94
    include_once $SETTINGS['cpassman_dir'] . '/sources/main.functions.php';
95
    include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php';
96
97
    // Connect to mysql server
98
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
99
100
    DB::$host = DB_HOST;
101
    DB::$user = DB_USER;
102
    DB::$password = defined('DB_PASSWD_CLEAR') === false ? defuseReturnDecrypted(DB_PASSWD, $SETTINGS) : DB_PASSWD_CLEAR;
103
    DB::$dbName = DB_NAME;
104
    DB::$port = DB_PORT;
105
    DB::$encoding = DB_ENCODING;
106
107
    // User's language loading
108
    include_once $SETTINGS['cpassman_dir'] . '/includes/language/' . $_SESSION['user_language'] . '.php';
109
110
    // Prepare post variables
111
    $post_key = filter_input(INPUT_POST, 'key', FILTER_SANITIZE_STRING);
112
    $post_type = filter_input(INPUT_POST, 'type', FILTER_SANITIZE_STRING);
113
    $post_data = filter_input(INPUT_POST, 'data', FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES);
114
115
    // Check KEY
116
    if (isset($post_key) === false || empty($post_key) === true) {
117
        echo prepareExchangedData(
118
            array(
119
                'error' => true,
120
                'message' => langHdl('key_is_not_correct'),
121
            ),
122
            'encode'
123
        );
124
        return false;
125
    }
126
127
    // Ensure Complexity levels are translated
128
    if (defined('TP_PW_COMPLEXITY') === false) {
129
        define(
130
            'TP_PW_COMPLEXITY',
131
            array(
132
                0 => array(0, langHdl('complex_level0'), 'fas fa-bolt text-danger'),
133
                25 => array(25, langHdl('complex_level1'), 'fas fa-thermometer-empty text-danger'),
134
                50 => array(50, langHdl('complex_level2'), 'fas fa-thermometer-quarter text-warning'),
135
                60 => array(60, langHdl('complex_level3'), 'fas fa-thermometer-half text-warning'),
136
                70 => array(70, langHdl('complex_level4'), 'fas fa-thermometer-three-quarters text-success'),
137
                80 => array(80, langHdl('complex_level5'), 'fas fa-thermometer-full text-success'),
138
                90 => array(90, langHdl('complex_level6'), 'far fa-gem text-success'),
139
            )
140
        );
141
    }
142
143
    // decrypt and retreive data in JSON format
144
    $dataReceived = prepareExchangedData(
145
        $post_data,
146
        'decode'
147
    );
148
    
149
    // Manage type of action asked
150
    switch ($post_type) {
151
        case 'change_pw':
152
            $return = changePassword(
153
                (string) filter_var($dataReceived['new_pw'], FILTER_SANITIZE_STRING),
154
                isset($dataReceived['current_pw']) === true ? (string) filter_var($dataReceived['current_pw'], FILTER_SANITIZE_STRING) : '',
155
                (int) filter_var($dataReceived['complexity'], FILTER_SANITIZE_NUMBER_INT),
156
                (string) filter_var($dataReceived['change_request'], FILTER_SANITIZE_STRING),
157
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
158
                $SETTINGS
159
            );
160
161
            echo $return;
162
            
163
            break;
164
165
        /*
166
         * This will generate the QR Google Authenticator
167
         */
168
        case 'ga_generate_qr':
169
            $return = generateQRCode(
170
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
171
                (string) filter_var($dataReceived['demand_origin'], FILTER_SANITIZE_STRING),
172
                (string) filter_var($dataReceived['send_email'], FILTER_SANITIZE_STRING),
173
                (string) filter_var($dataReceived['login'], FILTER_SANITIZE_STRING),
174
                (string) filter_var($dataReceived['pwd'], FILTER_SANITIZE_STRING),
175
                $SETTINGS
176
            );
177
178
            echo $return;
179
180
            break;
181
        
182
            /*
183
         * Increase the session time of User
184
         */
185
        case 'increase_session_time':
186
            $return = increaseSessionDuration(
187
                (int) filter_input(INPUT_POST, 'duration', FILTER_SANITIZE_NUMBER_INT)
188
            );
189
190
            echo $return;
191
192
            break;
193
        
194
            /*
195
         * Hide maintenance message
196
         */
197
        case 'hide_maintenance':
198
            $_SESSION['hide_maintenance'] = 1;
199
200
            break;
201
        
202
        /*
203
         * Send emails not sent
204
         */
205
        case 'send_waiting_emails':
206
            sendEmailsNotSent(
207
                $SETTINGS
208
            );
209
            
210
            break;
211
212
        /*
213
         * Generate a password generic
214
         */
215
        case 'generate_password':
216
            $return = generateGenericPassword(
217
                (int) filter_input(INPUT_POST, 'size', FILTER_SANITIZE_NUMBER_INT),
218
                (bool) filter_input(INPUT_POST, 'secure_pwd', FILTER_SANITIZE_STRING),
219
                (bool) filter_input(INPUT_POST, 'lowercase', FILTER_SANITIZE_STRING),
220
                (bool) filter_input(INPUT_POST, 'capitalize', FILTER_SANITIZE_STRING),
221
                (bool) filter_input(INPUT_POST, 'numerals', FILTER_SANITIZE_STRING),
222
                (bool) filter_input(INPUT_POST, 'symbols', FILTER_SANITIZE_NUMBER_INT),
223
                $SETTINGS
224
            );
225
226
            echo $return;
227
            
228
            break;
229
230
        /*
231
         * Refresh list of last items seen
232
         */
233
        case 'refresh_list_items_seen':
234
            $return = refreshUserItemsSeenList(
235
                $SETTINGS
236
            );
237
238
            echo $return;
239
240
            break;
241
242
        /*
243
         * Generates a KEY with CRYPT
244
         */
245
        case 'generate_new_key':
246
            // load passwordLib library
247
            $pwdlib = new SplClassLoader('PasswordLib', '../includes/libraries');
248
            $pwdlib->register();
249
            $pwdlib = new PasswordLib\PasswordLib();
250
            // generate key
251
            $key = $pwdlib->getRandomToken(filter_input(INPUT_POST, 'size', FILTER_SANITIZE_NUMBER_INT));
252
            echo '[{"key" : "' . htmlentities($key, ENT_QUOTES) . '"}]';
253
            break;
254
255
        /*
256
         * Generates a TOKEN with CRYPT
257
         */
258
        case 'save_token':
259
            $token = GenerateCryptKey(
260
                null !== filter_input(INPUT_POST, 'size', FILTER_SANITIZE_NUMBER_INT) ? (int) filter_input(INPUT_POST, 'size', FILTER_SANITIZE_NUMBER_INT) : 20,
261
                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,
262
                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,
263
                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,
264
                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,
265
                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,
266
                $SETTINGS
267
            );
268
269
            // store in DB
270
            DB::insert(
271
                prefixTable('tokens'),
272
                array(
273
                    'user_id' => (int) $_SESSION['user_id'],
274
                    'token' => $token,
275
                    'reason' => filter_input(INPUT_POST, 'reason', FILTER_SANITIZE_STRING),
276
                    'creation_timestamp' => time(),
277
                    'end_timestamp' => time() + filter_input(INPUT_POST, 'duration', FILTER_SANITIZE_NUMBER_INT), // in secs
278
                )
279
            );
280
281
            echo '[{"token" : "' . $token . '"}]';
282
            break;
283
284
285
        /*
286
         * TODO Check if suggestions are existing
287
         */
288
        /*
289
        case 'is_existings_suggestions':
290
            if ($_SESSION['user_manager'] === '1' || $_SESSION['is_admin'] === '1') {
291
                $count = 0;
292
                DB::query('SELECT * FROM ' . prefixTable('items_change'));
293
                $count += DB::count();
294
                DB::query('SELECT * FROM ' . prefixTable('suggestion'));
295
                $count += DB::count();
296
297
                echo '[ { "error" : "" , "count" : "' . $count . '" , "show_sug_in_menu" : "0"} ]';
298
                break;
299
            }
300
            
301
            if (isset($_SESSION['nb_item_change_proposals']) && $_SESSION['nb_item_change_proposals'] > 0) {
302
                echo '[ { "error" : "" , "count" : "' . $_SESSION['nb_item_change_proposals'] . '" , "show_sug_in_menu" : "1"} ]';
303
                break;
304
            }
305
            
306
            echo '[ { "error" : "" , "count" : "" , "show_sug_in_menu" : "0"} ]';
307
308
            break;
309
        */
310
311
        /*
312
         * Sending statistics
313
         */
314
        case 'sending_statistics':
315
            sendingStatistics(
316
                $SETTINGS
317
            );
318
319
            break;
320
321
        /*
322
         * delete a file
323
         */
324
        case 'file_deletion':
325
            fileDelete(filter_input(INPUT_POST, 'filename', FILTER_SANITIZE_STRING), $SETTINGS);
326
327
            break;
328
329
        /*
330
         * Generate BUG report
331
         */
332
        case 'generate_bug_report':
333
            $return = generateBugReport(
334
                (string) $post_data,
335
                $SETTINGS
336
            );
337
338
            echo $return;
339
        
340
            break;
341
342
        /**
343
         * 
344
         */
345
        case 'update_user_field':
346
            // Prepare variables
347
            DB::update(
348
                prefixTable('users'),
349
                array(
350
                    $dataReceived['field'] => noHTML(htmlspecialchars_decode($dataReceived['new_value'])),
351
                ),
352
                'id = %i',
353
                (int) $dataReceived['user_id']
354
            );
355
356
            // Update session
357
            if ($dataReceived['field'] === 'user_api_key') {
358
                $_SESSION['user']['api-key'] = noHTML(htmlspecialchars_decode($dataReceived['new_value']));
359
            }
360
            break;
361
362
        /*
363
        * get_teampass_settings
364
        */
365
        case 'get_teampass_settings':
366
            // Encrypt data to return
367
            echo prepareExchangedData($SETTINGS, 'encode');
368
369
            break;
370
371
372
        /*
373
        * test_current_user_password_is_correct
374
        */
375
        case 'test_current_user_password_is_correct':
376
            $return = isUserPasswordCorrect(
377
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
378
                (string) filter_var($dataReceived['password'], FILTER_SANITIZE_STRING),
379
                $SETTINGS
380
            );
381
382
            echo $return;
383
384
            break;
385
386
        /*
387
         * User's public/private keys change
388
         */
389
        case 'change_private_key_encryption_password':
390
            $return = changePrivateKeyEncryptionPassword(
391
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
392
                (string) filter_var($dataReceived['current_code'], FILTER_SANITIZE_STRING),
393
                (string) filter_var($dataReceived['new_code'], FILTER_SANITIZE_STRING),
394
                (string) filter_var($dataReceived['action_type'], FILTER_SANITIZE_STRING),
395
                $SETTINGS
396
            );
397
398
            echo $return;
399
        
400
            break;
401
402
        /*
403
         * User's password has to be initialized
404
         */
405
        case 'initialize_user_password':
406
            $return = initializeUserPassword(
407
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
408
                (string) filter_var($dataReceived['special'], FILTER_SANITIZE_STRING),
409
                (string) filter_var($dataReceived['password'], FILTER_SANITIZE_STRING),
410
                (string) filter_var($dataReceived['self_change'], FILTER_SANITIZE_STRING),
0 ignored issues
show
Bug introduced by
(string)filter_var($data...FILTER_SANITIZE_STRING) of type string is incompatible with the type boolean expected by parameter $post_self_change of initializeUserPassword(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

410
                /** @scrutinizer ignore-type */ (string) filter_var($dataReceived['self_change'], FILTER_SANITIZE_STRING),
Loading history...
411
                $SETTINGS
412
            );
413
414
            echo $return;
415
416
            break;
417
418
        /*
419
        * CASE
420
        * Send email
421
        */
422
        case 'mail_me':
423
            $return = sendMailToUser(
424
                filter_var($dataReceived['receipt'], FILTER_SANITIZE_NUMBER_INT),
425
                $dataReceived['body'],
426
                (string) filter_var($dataReceived['subject'], FILTER_SANITIZE_STRING),
427
                (array) filter_var($dataReceived['pre_replace'], FILTER_SANITIZE_STRING),
428
                $SETTINGS
429
            );
430
431
            echo $return;
432
433
            break;
434
435
        /*
436
        * Generate a temporary encryption key for user
437
        */
438
        case 'generate_temporary_encryption_key':
439
            $return = generateOneTimeCode(
440
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
441
                $SETTINGS
442
            );
443
            
444
            echo $return;
445
446
            break;
447
448
        /*
449
        * user_sharekeys_reencryption_start
450
        */
451
        case 'user_sharekeys_reencryption_start':
452
            $return = startReEncryptingUserSharekeys(
453
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
454
                (bool) filter_var($dataReceived['self_change'], FILTER_SANITIZE_STRING),
455
                $SETTINGS
456
            );
457
            
458
            echo $return;
459
460
            break;
461
462
        /*
463
        * user_sharekeys_reencryption_next
464
        */
465
        case 'user_sharekeys_reencryption_next':
466
            $return = continueReEncryptingUserSharekeys(
467
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
468
                (bool) filter_var($dataReceived['self_change'], FILTER_SANITIZE_STRING),
469
                (string) filter_var($dataReceived['action'], FILTER_SANITIZE_STRING),
470
                (int) filter_var($dataReceived['start'], FILTER_SANITIZE_NUMBER_INT),
471
                (int) filter_var($dataReceived['length'], FILTER_SANITIZE_NUMBER_INT),
472
                $SETTINGS
473
            );
474
            
475
            echo $return;
476
477
            break;
478
479
        /*
480
        * user_psk_reencryption
481
        */
482
        case 'user_psk_reencryption':
483
            $return = migrateTo3_DoUserPersonalItemsEncryption(
484
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
485
                (int) filter_var($dataReceived['start'], FILTER_SANITIZE_NUMBER_INT),
486
                (int) filter_var($dataReceived['length'], FILTER_SANITIZE_NUMBER_INT),
487
                (string) filter_var($dataReceived['userPsk'], FILTER_SANITIZE_STRING),
488
                $SETTINGS
489
            );
490
            
491
            echo $return;
492
        
493
            break;
494
495
        /*
496
        * Get info 
497
        */
498
        case 'get_user_info':
499
            $return = getUserInfo(
500
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
501
                (string) filter_var($dataReceived['fields'], FILTER_SANITIZE_STRING),
502
                $SETTINGS
503
            );
504
            
505
            echo $return;
506
507
            break;
508
509
        /*
510
        * Change user's authenticataion password
511
        */
512
        case 'change_user_auth_password':
513
            $return = changeUserAuthenticationPassword(
514
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
515
                (string) filter_var($dataReceived['old_password'], FILTER_SANITIZE_STRING),
516
                (string) filter_var($dataReceived['new_password'], FILTER_SANITIZE_STRING),
517
                $SETTINGS
518
            );
519
            
520
            echo $return;
521
522
            break;
523
524
525
        /*
526
        * User's authenticataion password in LDAP has changed
527
        */
528
        case 'change_user_ldap_auth_password':
529
            $return = /** @scrutinizer ignore-call */ changeUserLDAPAuthenticationPassword(
530
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
531
                filter_var($dataReceived['previous_password'], FILTER_SANITIZE_STRING),
532
                filter_var($dataReceived['current_password'], FILTER_SANITIZE_STRING),
533
                $SETTINGS
534
            );
535
            
536
            echo $return;
537
538
            break;
539
    }
540
}
541
542
543
/**
544
 * 
545
 */
546
function changePassword(
547
    string $post_new_password,
548
    string $post_current_password,
549
    int $post_password_complexity,
550
    string $post_change_request,
551
    int $post_user_id,
552
    array $SETTINGS
553
): string
554
{
555
    // load passwordLib library
556
    $pwdlib = new SplClassLoader('PasswordLib', '../includes/libraries');
557
    $pwdlib->register();
558
    $pwdlib = new PasswordLib\PasswordLib();
559
560
    // Prepare variables
561
    $post_new_password_hashed = $pwdlib->createPasswordHash($post_new_password);
562
563
    // User has decided to change is PW
564
    if (
565
        $post_change_request === 'reset_user_password_expected'
566
        || $post_change_request === 'user_decides_to_change_password'
567
    ) {
568
        // Check that current user is correct
569
        if ((int) $post_user_id !== (int) $_SESSION['user_id']) {
570
            return prepareExchangedData(
571
                array(
572
                    'error' => true,
573
                    'message' => langHdl('error_not_allowed_to'),
574
                ),
575
                'encode'
576
            );
577
        }
578
579
        // check if expected security level is reached
580
        $dataUser = DB::queryfirstrow(
581
            'SELECT *
582
            FROM ' . prefixTable('users') . '
583
            WHERE id = %i',
584
            $post_user_id
585
        );
586
587
        // check if badly written
588
        $dataUser['fonction_id'] = array_filter(
589
            explode(',', str_replace(';', ',', $dataUser['fonction_id']))
590
        );
591
        $dataUser['fonction_id'] = implode(',', $dataUser['fonction_id']);
592
        DB::update(
593
            prefixTable('users'),
594
            array(
595
                'fonction_id' => $dataUser['fonction_id'],
596
            ),
597
            'id = %i',
598
            $post_user_id
599
        );
600
601
        if (empty($dataUser['fonction_id']) === false) {
602
            $data = DB::queryFirstRow(
603
                'SELECT complexity
604
                FROM ' . prefixTable('roles_title') . '
605
                WHERE id IN (' . $dataUser['fonction_id'] . ')
606
                ORDER BY complexity DESC'
607
            );
608
        } else {
609
            // In case user has no roles yet
610
            $data = array();
611
            $data['complexity'] = 0;
612
        }
613
614
        if ((int) $post_password_complexity < (int) $data['complexity']) {
615
            return prepareExchangedData(
616
                array(
617
                    'error' => true,
618
                    'message' => '<div style="margin:10px 0 10px 15px;">' . langHdl('complexity_level_not_reached') . '.<br>' .
619
                        langHdl('expected_complexity_level') . ': <b>' . TP_PW_COMPLEXITY[$data['complexity']][1] . '</b></div>',
620
                ),
621
                'encode'
622
            );
623
        }
624
625
        // Check that the 2 passwords are differents
626
        if ($post_current_password === $post_new_password) {
627
            return prepareExchangedData(
628
                array(
629
                    'error' => true,
630
                    'message' => langHdl('password_already_used'),
631
                ),
632
                'encode'
633
            );
634
        }
635
636
        // update sessions
637
        $_SESSION['last_pw_change'] = mktime(0, 0, 0, (int) date('m'), (int) date('d'), (int) date('y'));
638
        $_SESSION['validite_pw'] = true;
639
640
        // BEfore updating, check that the pwd is correct
641
        if ($pwdlib->verifyPasswordHash($post_new_password, $post_new_password_hashed) === true) {
642
            $special_action = 'none';
643
            if ($post_change_request === 'reset_user_password_expected') {
644
                $_SESSION['user']['private_key'] = decryptPrivateKey($post_current_password, $dataUser['private_key']);
645
            }
646
647
            // update DB
648
            DB::update(
649
                prefixTable('users'),
650
                array(
651
                    'pw' => $post_new_password_hashed,
652
                    'last_pw_change' => mktime(0, 0, 0, (int) date('m'), (int) date('d'), (int) date('y')),
653
                    'last_pw' => $post_current_password,
654
                    'special' => $special_action,
655
                    'private_key' => encryptPrivateKey($post_new_password, $_SESSION['user']['private_key']),
656
                ),
657
                'id = %i',
658
                $post_user_id
659
            );
660
            // update LOG
661
            logEvents($SETTINGS, 'user_mngt', 'at_user_pwd_changed', (string) $_SESSION['user_id'], $_SESSION['login'], $post_user_id);
662
663
            // Send back
664
            return prepareExchangedData(
665
                array(
666
                    'error' => false,
667
                    'message' => '',
668
                ),
669
                'encode'
670
            );
671
        }
672
        // Send back
673
        return prepareExchangedData(
674
            array(
675
                'error' => true,
676
                'message' => langHdl('pwd_hash_not_correct'),
677
            ),
678
            'encode'
679
        );
680
    }
681
    return prepareExchangedData(
682
        array(
683
            'error' => true,
684
            'message' => langHdl('error_not_allowed_to'),
685
        ),
686
        'encode'
687
    );
688
}
689
690
691
692
function generateQRCode(
693
    $post_id,
694
    $post_demand_origin,
695
    $post_send_mail,
696
    $post_login,
697
    $post_pwd,
698
    array $SETTINGS
699
): string
700
{
701
    // is this allowed by setting
702
    if ((isset($SETTINGS['ga_reset_by_user']) === false || (int) $SETTINGS['ga_reset_by_user'] !== 1)
703
        && (null === $post_demand_origin || $post_demand_origin !== 'users_management_list')
704
    ) {
705
        // User cannot ask for a new code
706
        return prepareExchangedData(
707
            array(
708
                'error' => true,
709
                'message' => langHdl('error_not_allowed_to'),
710
            ),
711
            'encode'
712
        );
713
    }
714
715
    // Check if user exists
716
    if (null === $post_id || empty($post_id) === true) {
717
        // Get data about user
718
        $data = DB::queryfirstrow(
719
            'SELECT id, email, pw
720
            FROM ' . prefixTable('users') . '
721
            WHERE login = %s',
722
            $post_login
723
        );
724
    } else {
725
        $data = DB::queryfirstrow(
726
            'SELECT id, login, email, pw
727
            FROM ' . prefixTable('users') . '
728
            WHERE id = %i',
729
            $post_id
730
        );
731
        $post_login = $data['login'];
732
    }
733
    // Get number of returned users
734
    $counter = DB::count();
735
736
    // load passwordLib library
737
    $pwdlib = new SplClassLoader('PasswordLib', $SETTINGS['cpassman_dir'] . '/includes/libraries');
738
    $pwdlib->register();
739
    $pwdlib = new PasswordLib\PasswordLib();
740
741
    // Do treatment
742
    if ($counter === 0) {
743
        // Not a registered user !
744
        logEvents($SETTINGS, 'failed_auth', 'user_not_exists', '', stripslashes($post_login), stripslashes($post_login));
745
        return prepareExchangedData(
746
            array(
747
                'error' => true,
748
                'message' => langHdl('no_user'),
749
                'tst' => 1,
750
            ),
751
            'encode'
752
        );
753
    }
754
755
    if (
756
        isset($post_pwd) === true
757
        && isset($data['pw']) === true
758
        && $pwdlib->verifyPasswordHash($post_pwd, $data['pw']) === false
759
        && $post_demand_origin !== 'users_management_list'
760
    ) {
761
        // checked the given password
762
        logEvents($SETTINGS, 'failed_auth', 'user_password_not_correct', '', stripslashes($post_login), stripslashes($post_login));
763
        return prepareExchangedData(
764
            array(
765
                'error' => true,
766
                'message' => langHdl('no_user'),
767
                'tst' => $post_demand_origin,
768
            ),
769
            'encode'
770
        );
771
    }
772
    
773
    if (empty($data['email']) === true) {
774
        return prepareExchangedData(
775
            array(
776
                'error' => true,
777
                'message' => langHdl('no_email'),
778
            ),
779
            'encode'
780
        );
781
    }
782
    
783
    // generate new GA user code
784
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Authentication/TwoFactorAuth/TwoFactorAuth.php';
785
    $tfa = new Authentication\TwoFactorAuth\TwoFactorAuth($SETTINGS['ga_website_name']);
786
    $gaSecretKey = $tfa->createSecret();
787
    $gaTemporaryCode = GenerateCryptKey(12, false, true, true, false, true, $SETTINGS);
788
789
    DB::update(
790
        prefixTable('users'),
791
        [
792
            'ga' => $gaSecretKey,
793
            'ga_temporary_code' => $gaTemporaryCode,
794
        ],
795
        'id = %i',
796
        $data['id']
797
    );
798
799
    // Log event
800
    logEvents($SETTINGS, 'user_connection', 'at_2fa_google_code_send_by_email', (string) $data['id'], stripslashes($post_login), stripslashes($post_login));
801
802
    // send mail?
803
    if ((int) $post_send_mail === 1) {
804
        json_decode(
805
            sendEmail(
806
                langHdl('email_ga_subject'),
807
                str_replace(
808
                    '#2FACode#',
809
                    $gaTemporaryCode,
810
                    langHdl('email_ga_text')
811
                ),
812
                $data['email'],
813
                $SETTINGS
814
            ),
815
            true
816
        );
817
818
        // send back
819
        return prepareExchangedData(
820
            array(
821
                'error' => false,
822
                'message' => $post_send_mail,
823
                'email' => $data['email'],
824
                'email_result' => str_replace(
825
                    '#email#',
826
                    '<b>' . obfuscateEmail($data['email']) . '</b>',
827
                    addslashes(langHdl('admin_email_result_ok'))
828
                ),
829
            ),
830
            'encode'
831
        );
832
    }
833
    
834
    // send back
835
    return prepareExchangedData(
836
        array(
837
            'error' => false,
838
            'message' => '',
839
            'email' => $data['email'],
840
            'email_result' => str_replace(
841
                '#email#',
842
                '<b>' . obfuscateEmail($data['email']) . '</b>',
843
                addslashes(langHdl('admin_email_result_ok'))
844
            ),
845
        ),
846
        'encode'
847
    );
848
}
849
850
function sendEmailsNotSent(
851
    array $SETTINGS
852
)
853
{
854
    if (isset($SETTINGS['enable_send_email_on_user_login'])
855
        && (int) $SETTINGS['enable_send_email_on_user_login'] === 1
856
    ) {
857
        $row = DB::queryFirstRow(
858
            'SELECT valeur FROM ' . prefixTable('misc') . ' WHERE type = %s AND intitule = %s',
859
            'cron',
860
            'sending_emails'
861
        );
862
863
        if ((int) (time() - $row['valeur']) >= 300 || (int) $row['valeur'] === 0) {
864
            $rows = DB::query(
865
                'SELECT *
866
                FROM ' . prefixTable('emails') .
867
                ' WHERE status != %s',
868
                'sent'
869
            );
870
            foreach ($rows as $record) {
871
                echo $record['increment_id'] . " >> ";
872
                // Send email
873
                $ret = json_decode(
874
                    sendEmail(
875
                        $record['subject'],
876
                        $record['body'],
877
                        $record['receivers'],
878
                        $SETTINGS
879
                    ),
880
                    true
881
                );
882
883
                // update item_id in files table
884
                DB::update(
885
                    prefixTable('emails'),
886
                    array(
887
                        'status' => $ret['error'] === 'error_mail_not_send' ? 'not_sent' : 'sent',
888
                    ),
889
                    'timestamp = %s',
890
                    $record['timestamp']
891
                );
892
            }
893
        }
894
        // update cron time
895
        DB::update(
896
            prefixTable('misc'),
897
            array(
898
                'valeur' => time(),
899
            ),
900
            'intitule = %s AND type = %s',
901
            'sending_emails',
902
            'cron'
903
        );
904
    }
905
}
906
907
function generateGenericPassword(
908
    int $size,
909
    bool $secure,
910
    bool $lowercase,
911
    bool $capitalize,
912
    bool $numerals,
913
    bool $symbols,
914
    array $SETTINGS
915
): string
916
{
917
    if ((int) $size > (int) $SETTINGS['pwd_maximum_length']) {
918
        return prepareExchangedData(
919
            array(
920
                'error_msg' => 'Password length is too long! ',
921
                'error' => 'true',
922
            ),
923
            'encode'
924
        );
925
    }
926
927
    $generator = new SplClassLoader('PasswordGenerator\Generator', '../includes/libraries');
928
    $generator->register();
929
    $generator = new PasswordGenerator\Generator\ComputerPasswordGenerator();
930
931
    // Is PHP7 being used?
932
    if (version_compare(PHP_VERSION, '7.0.0', '>=')) {
933
        $php7generator = new SplClassLoader('PasswordGenerator\RandomGenerator', '../includes/libraries');
934
        $php7generator->register();
935
        $generator->setRandomGenerator(new PasswordGenerator\RandomGenerator\Php7RandomGenerator());
936
    }
937
938
    // Manage size
939
    $generator->setLength(($size <= 0) ? 10 : $size);
940
941
    if ($secure === true) {
942
        $generator->setSymbols(true);
943
        $generator->setLowercase(true);
944
        $generator->setUppercase(true);
945
        $generator->setNumbers(true);
946
    } else {
947
        $generator->setLowercase($lowercase);
948
        $generator->setUppercase($capitalize);
949
        $generator->setNumbers($numerals);
950
        $generator->setSymbols($symbols);
951
    }
952
953
    return prepareExchangedData(
954
        array(
955
            'key' => $generator->generatePasswords(),
956
            'error' => '',
957
        ),
958
        'encode'
959
    );
960
}
961
962
function refreshUserItemsSeenList(
963
    array $SETTINGS
964
): string
965
{
966
    // get list of last items seen
967
    $arr_html = array();
968
    $rows = DB::query(
969
        '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
970
        FROM ' . prefixTable('log_items') . ' AS l
971
        RIGHT JOIN ' . prefixTable('items') . ' AS i ON (l.id_item = i.id)
972
        WHERE l.action = %s AND l.id_user = %i
973
        ORDER BY l.date DESC
974
        LIMIT 0, 100',
975
        'at_shown',
976
        $_SESSION['user_id']
977
    );
978
    if (DB::count() > 0) {
979
        foreach ($rows as $record) {
980
            if (in_array($record['id']->id, array_column($arr_html, 'id')) === false) {
981
                array_push(
982
                    $arr_html,
983
                    array(
984
                        'id' => $record['id'],
985
                        'label' => htmlspecialchars(stripslashes(htmlspecialchars_decode($record['label'], ENT_QUOTES)), ENT_QUOTES),
986
                        'tree_id' => $record['id_tree'],
987
                        'perso' => $record['perso'],
988
                        'restricted' => $record['restricted'],
989
                    )
990
                );
991
                if (count($arr_html) >= (int) $SETTINGS['max_latest_items']) {
992
                    break;
993
                }
994
            }
995
        }
996
    }
997
998
    // get wainting suggestions
999
    $nb_suggestions_waiting = 0;
1000
    if (
1001
        isset($SETTINGS['enable_suggestion']) === true && (int) $SETTINGS['enable_suggestion'] === 1
1002
        && ((int) $_SESSION['user_admin'] === 1 || (int) $_SESSION['user_manager'] === 1)
1003
    ) {
1004
        DB::query('SELECT * FROM ' . prefixTable('suggestion'));
1005
        $nb_suggestions_waiting = DB::count();
1006
    }
1007
1008
    return json_encode(
1009
        array(
1010
            'error' => '',
1011
            'existing_suggestions' => $nb_suggestions_waiting,
1012
            'html_json' => $arr_html,
1013
        ),
1014
        JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
1015
    );
1016
}
1017
1018
function sendingStatistics(
1019
    array $SETTINGS
1020
): void
1021
{
1022
    if (
1023
        isset($SETTINGS['send_statistics_items']) === true
1024
        && isset($SETTINGS['send_stats']) === true
1025
        && isset($SETTINGS['send_stats_time']) === true
1026
        && (int) $SETTINGS['send_stats'] === 1
1027
        && (int) ($SETTINGS['send_stats_time'] + TP_ONE_DAY_SECONDS) > time()
1028
    ) {
1029
        // get statistics data
1030
        $stats_data = getStatisticsData($SETTINGS);
1031
1032
        // get statistics items to share
1033
        $statsToSend = [];
1034
        $statsToSend['ip'] = $_SERVER['SERVER_ADDR'];
1035
        $statsToSend['timestamp'] = time();
1036
        foreach (array_filter(explode(';', $SETTINGS['send_statistics_items'])) as $data) {
1037
            if ($data === 'stat_languages') {
1038
                $tmp = '';
1039
                foreach ($stats_data[$data] as $key => $value) {
1040
                    $tmp .= $tmp === '' ? $key . '-' . $value : ',' . $key . '-' . $value;
1041
                }
1042
                $statsToSend[$data] = $tmp;
1043
            } elseif ($data === 'stat_country') {
1044
                $tmp = '';
1045
                foreach ($stats_data[$data] as $key => $value) {
1046
                    $tmp .= $tmp === '' ? $key . '-' . $value : ',' . $key . '-' . $value;
1047
                }
1048
                $statsToSend[$data] = $tmp;
1049
            } else {
1050
                $statsToSend[$data] = $stats_data[$data];
1051
            }
1052
        }
1053
1054
        // connect to Teampass Statistics database
1055
        $link2 = new MeekroDB(
1056
            'teampass.pw',
1057
            'teampass_user',
1058
            'ZMlEfRzKzFLZNzie',
1059
            'teampass_followup',
1060
            '3306',
1061
            'utf8'
1062
        );
1063
1064
        $link2->insert(
1065
            'statistics',
1066
            $statsToSend
1067
        );
1068
1069
        // update table misc with current timestamp
1070
        DB::update(
1071
            prefixTable('misc'),
1072
            array(
1073
                'valeur' => time(),
1074
            ),
1075
            'type = %s AND intitule = %s',
1076
            'admin',
1077
            'send_stats_time'
1078
        );
1079
1080
        //permits to test only once by session
1081
        $_SESSION['temporary']['send_stats_done'] = true;
1082
        $SETTINGS['send_stats_time'] = time();
1083
1084
        // save change in config file
1085
        handleConfigFile('update', $SETTINGS, 'send_stats_time', $SETTINGS['send_stats_time']);
1086
    }
1087
}
1088
1089
function generateBugReport(
1090
    string $data,
1091
    array $SETTINGS
1092
): string
1093
{
1094
    $config_exclude_vars = array(
1095
        'bck_script_passkey',
1096
        'email_smtp_server',
1097
        'email_auth_username',
1098
        'email_auth_pwd',
1099
        'email_from',
1100
    );
1101
1102
    // Get data
1103
    $post_data = json_decode($data, true);
1104
1105
    // Read config file
1106
    $list_of_options = '';
1107
    $url_found = '';
1108
    $anonym_url = '';
1109
    $tp_config_file = '../includes/config/tp.config.php';
1110
    $data = file($tp_config_file);
1111
    foreach ($data as $line) {
1112
        if (substr($line, 0, 4) === '    ') {
1113
            // Remove extra spaces
1114
            $line = str_replace('    ', '', $line);
1115
1116
            // Identify url to anonymize it
1117
            if (strpos($line, 'cpassman_url') > 0 && empty($url_found) === true) {
1118
                $url_found = substr($line, 19, strlen($line) - 22);
1119
                $tmp = parse_url($url_found);
1120
                $anonym_url = $tmp['scheme'] . '://<anonym_url>' . $tmp['path'];
1121
                $line = "'cpassman_url' => '" . $anonym_url . "\n";
1122
            }
1123
1124
            // Anonymize all urls
1125
            if (empty($anonym_url) === false) {
1126
                $line = str_replace($url_found, $anonym_url, $line);
1127
            }
1128
1129
            // Clear some vars
1130
            foreach ($config_exclude_vars as $var) {
1131
                if (strpos($line, $var) > 0) {
1132
                    $line = "'".$var."' => '<removed>'\n";
1133
                }
1134
            }
1135
1136
            // Complete line to display
1137
            $list_of_options .= $line;
1138
        }
1139
    }
1140
1141
    // Get error
1142
    $err = error_get_last();
1143
1144
    // Get 10 latest errors in Teampass
1145
    $teampass_errors = '';
1146
    $rows = DB::query(
1147
        'SELECT label, date AS error_date
1148
        FROM ' . prefixTable('log_system') . "
1149
        WHERE `type` LIKE 'error'
1150
        ORDER BY `date` DESC
1151
        LIMIT 0, 10"
1152
    );
1153
    if (DB::count() > 0) {
1154
        foreach ($rows as $record) {
1155
            if (empty($teampass_errors) === true) {
1156
                $teampass_errors = ' * ' . date($SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'], (int) $record['error_date']) . ' - ' . $record['label'];
1157
            } else {
1158
                $teampass_errors .= ' * ' . date($SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'], (int) $record['error_date']) . ' - ' . $record['label'];
1159
            }
1160
        }
1161
    }
1162
1163
    $link = mysqli_connect(DB_HOST, DB_USER, DB_PASSWD_CLEAR, DB_NAME, (int) DB_PORT, null);
1164
1165
    // Now prepare text
1166
    $txt = '### Page on which it happened
1167
' . $post_data['current_page'] . '
1168
1169
### Steps to reproduce
1170
1.
1171
2.
1172
3.
1173
1174
### Expected behaviour
1175
Tell us what should happen
1176
1177
1178
### Actual behaviour
1179
Tell us what happens instead
1180
1181
### Server configuration
1182
**Operating system**: ' . php_uname() . '
1183
1184
**Web server:** ' . $_SERVER['SERVER_SOFTWARE'] . '
1185
1186
**Database:** ' . ($link === false ? langHdl('undefined') : mysqli_get_server_info($link)) . '
1187
1188
**PHP version:** ' . PHP_VERSION . '
1189
1190
**Teampass version:** ' . TP_VERSION_FULL . '
1191
1192
**Teampass configuration file:**
1193
```
1194
' . $list_of_options . '
1195
```
1196
1197
**Updated from an older Teampass or fresh install:**
1198
1199
### Client configuration
1200
1201
**Browser:** ' . $post_data['browser_name'] . ' - ' . $post_data['browser_version'] . '
1202
1203
**Operating system:** ' . $post_data['os'] . ' - ' . $post_data['os_archi'] . 'bits
1204
1205
### Logs
1206
1207
#### Web server error log
1208
```
1209
' . $err['message'] . ' - ' . $err['file'] . ' (' . $err['line'] . ')
1210
```
1211
1212
#### Teampass 10 last system errors
1213
```
1214
' . $teampass_errors . '
1215
```
1216
1217
#### Log from the web-browser developer console (CTRL + SHIFT + i)
1218
```
1219
Insert the log here and especially the answer of the query that failed.
1220
```
1221
';
1222
1223
    return prepareExchangedData(
1224
        array(
1225
            'html' => $txt,
1226
            'error' => '',
1227
        ),
1228
        'encode'
1229
    );
1230
}
1231
1232
function isUserPasswordCorrect(
1233
    int $post_user_id,
1234
    string $post_user_password,
1235
    array $SETTINGS
1236
): string
1237
{
1238
    if (is_null($post_user_id) === false && isset($post_user_id) === true && empty($post_user_id) === false) {
1239
        // Check if user exists
1240
        $userInfo = DB::queryFirstRow(
1241
            'SELECT public_key, private_key, pw
1242
            FROM ' . prefixTable('users') . '
1243
            WHERE id = %i',
1244
            $post_user_id
1245
        );
1246
        if (DB::count() > 0) {
1247
            // Get one item
1248
            $record = DB::queryFirstRow(
1249
                'SELECT id, pw
1250
                FROM ' . prefixTable('items') . '
1251
                WHERE perso = 0'
1252
            );
1253
1254
            // Get itemKey from current user
1255
            $currentUserKey = DB::queryFirstRow(
1256
                'SELECT share_key, increment_id
1257
                FROM ' . prefixTable('sharekeys_items') . '
1258
                WHERE object_id = %i AND user_id = %i',
1259
                $record['id'],
1260
                $post_user_id
1261
            );
1262
1263
            if (count($currentUserKey) > 0) {
1264
                // Decrypt itemkey with user key
1265
                // use old password to decrypt private_key
1266
                $_SESSION['user']['private_key'] = decryptPrivateKey($post_user_password, $userInfo['private_key']);
1267
                $_SESSION['user']['public_key'] = decryptPrivateKey($post_user_password, $userInfo['public_key']);
1268
                $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $_SESSION['user']['private_key']);
1269
1270
                if (empty(base64_decode($itemKey)) === false) {
1271
                    // GOOD password
1272
                    return prepareExchangedData(
1273
                        array(
1274
                            'error' => false,
1275
                            'message' => '',
1276
                            'debug' => '',
1277
                        ),
1278
                        'encode'
1279
                    );
1280
                }
1281
            }
1282
            
1283
            // Use the password check
1284
            // load passwordLib library
1285
            $pwdlib = new SplClassLoader('PasswordLib', $SETTINGS['cpassman_dir'] . '/includes/libraries');
1286
            $pwdlib->register();
1287
            $pwdlib = new PasswordLib\PasswordLib();
1288
            
1289
            if ($pwdlib->verifyPasswordHash(htmlspecialchars_decode($post_user_password), $userInfo['pw']) === true) {
1290
                // GOOD password
1291
                return prepareExchangedData(
1292
                    array(
1293
                        'error' => false,
1294
                        'message' => '',
1295
                        'debug' => '',
1296
                    ),
1297
                    'encode'
1298
                );
1299
            }
1300
        }
1301
    }
1302
1303
    return prepareExchangedData(
1304
        array(
1305
            'error' => true,
1306
            'message' => langHdl('password_is_not_correct'),
1307
            'debug' => isset($itemKey) === true ? base64_decode($itemKey) : '',
1308
        ),
1309
        'encode'
1310
    );
1311
}
1312
1313
function changePrivateKeyEncryptionPassword(
1314
    int $post_user_id,
1315
    string $post_current_code,
1316
    string $post_new_code,
1317
    string $post_action_type,
1318
    array $SETTINGS
1319
): string
1320
{
1321
    if (empty($post_new_code) === true) {
1322
        $post_new_code = $_SESSION['user_pwd'];
1323
    }
1324
1325
    if (is_null($post_user_id) === false && isset($post_user_id) === true && empty($post_user_id) === false) {
1326
        // Get user info
1327
        $userData = DB::queryFirstRow(
1328
            'SELECT private_key
1329
            FROM ' . prefixTable('users') . '
1330
            WHERE id = %i',
1331
            $post_user_id
1332
        );
1333
        if (DB::count() > 0) {
1334
            if ($post_action_type === 'encrypt_privkey_with_user_password') {
1335
                // Here the user has his private key encrypted with an OTC.
1336
                // We need to encrypt it with his real password
1337
                $privateKey = decryptPrivateKey($post_new_code, $userData['private_key']);
1338
                $hashedPrivateKey = encryptPrivateKey($post_current_code, $privateKey);
1339
            } else {
1340
                $privateKey = decryptPrivateKey($post_current_code, $userData['private_key']);
1341
                $hashedPrivateKey = encryptPrivateKey($post_new_code, $privateKey);
1342
            }
1343
1344
            // Update user account
1345
            DB::update(
1346
                prefixTable('users'),
1347
                array(
1348
                    'private_key' => $hashedPrivateKey,
1349
                    'special' => 'none',
1350
                ),
1351
                'id = %i',
1352
                $post_user_id
1353
            );
1354
1355
            // Load superGlobals
1356
            include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
1357
            $superGlobal = new protect\SuperGlobal\SuperGlobal();
1358
1359
            $superGlobal->put('private_key', $privateKey, 'SESSION', 'user');
1360
        }
1361
1362
        // Return
1363
        return prepareExchangedData(
1364
            array(
1365
                'error' => false,
1366
                'message' => '',
1367
            ),
1368
            'encode'
1369
        );
1370
    }
1371
    
1372
    return prepareExchangedData(
1373
        array(
1374
            'error' => true,
1375
            'message' => langHdl('error_no_user'),
1376
            'debug' => '',
1377
        ),
1378
        'encode'
1379
    );
1380
}
1381
1382
function initializeUserPassword(
1383
    int $post_user_id,
1384
    string $post_special,
1385
    string $post_user_password,
1386
    bool $post_self_change,
1387
    array $SETTINGS
1388
): string
1389
{
1390
    if (is_null($post_user_id) === false && isset($post_user_id) === true && empty($post_user_id) === false) {
1391
        // Get user info
1392
        $userData = DB::queryFirstRow(
1393
            'SELECT email, auth_type, login
1394
            FROM ' . prefixTable('users') . '
1395
            WHERE id = %i',
1396
            $post_user_id
1397
        );
1398
        if (DB::count() > 0 && empty($userData['email']) === false) {
1399
            // If user pwd is empty then generate a new one and send it to user
1400
            if (isset($post_user_password) === false || empty($post_user_password) === true) {
1401
                // Generate new password
1402
                $post_user_password = generateQuickPassword();
1403
            }
1404
1405
            // If LDAP enabled, then
1406
            // check that this password is correct
1407
            $continue = false;
1408
            if ($userData['auth_type'] === 'ldap' && (int) $SETTINGS['ldap_mode'] === 1) {
1409
                $continue = ldapCheckUserPassword(
1410
                    $userData['login'],
1411
                    $post_user_password,
1412
                    $SETTINGS
1413
                );
1414
            }
1415
1416
            if ($continue === true) {
1417
                // Only change if email is successfull
1418
                // GEnerate new keys
1419
                $userKeys = generateUserKeys($post_user_password);
1420
1421
                // load passwordLib library
1422
                $pwdlib = new SplClassLoader('PasswordLib', '../includes/libraries');
1423
                $pwdlib->register();
1424
                $pwdlib = new PasswordLib\PasswordLib();
1425
1426
                // Update user account
1427
                DB::update(
1428
                    prefixTable('users'),
1429
                    array(
1430
                        'special' => $post_special,
1431
                        'pw' => $pwdlib->createPasswordHash($post_user_password),
1432
                        'public_key' => $userKeys['public_key'],
1433
                        'private_key' => $userKeys['private_key'],
1434
                        'last_pw_change' => time(),
1435
                    ),
1436
                    'id = %i',
1437
                    $post_user_id
1438
                );
1439
1440
                // Return
1441
                return prepareExchangedData(
1442
                    array(
1443
                        'error' => false,
1444
                        'message' => '',
1445
                        'user_pwd' => $post_user_password,
1446
                        'user_email' => $userData['email'],
1447
                    ),
1448
                    'encode'
1449
                );
1450
            }
1451
            // Return error
1452
            return prepareExchangedData(
1453
                array(
1454
                    'error' => true,
1455
                    'message' => langHdl('no_email_set'),
1456
                    'debug' => '',
1457
                    'self_change' => $post_self_change,
1458
                ),
1459
                'encode'
1460
            );
1461
        }
1462
1463
        // Error
1464
        return prepareExchangedData(
1465
            array(
1466
                'error' => true,
1467
                'message' => langHdl('no_email_set'),
1468
                'debug' => '',
1469
            ),
1470
            'encode'
1471
        );
1472
    }
1473
    
1474
    return prepareExchangedData(
1475
        array(
1476
            'error' => true,
1477
            'message' => langHdl('error_no_user'),
1478
            'debug' => '',
1479
        ),
1480
        'encode'
1481
    );
1482
}
1483
1484
function sendMailToUser(
1485
    string $post_receipt,
1486
    string $post_body,
1487
    string $post_subject,
1488
    array $post_replace,
1489
    array $SETTINGS
1490
): string
1491
{
1492
    if (count($post_replace) > 0 && is_null($post_replace) === false) {
1493
        $post_body = str_replace(
1494
            array_keys($post_replace),
1495
            array_values($post_replace),
1496
            $post_body
1497
        );
1498
    }
1499
    $ret = sendEmail(
1500
        $post_subject,
1501
        $post_body,
1502
        $post_receipt,
1503
        $SETTINGS,
1504
        '',
1505
        false
1506
    );
1507
1508
    $ret = json_decode($ret, true);
1509
1510
    return prepareExchangedData(
1511
        array(
1512
            'error' => empty($ret['error']) === true ? false : true,
1513
            'message' => $ret['message'],
1514
        ),
1515
        'encode'
1516
    );
1517
}
1518
1519
function generateOneTimeCode(
1520
    int $post_user_id,
1521
    array $SETTINGS
1522
): string
1523
{
1524
    if (is_null($post_user_id) === false && isset($post_user_id) === true && empty($post_user_id) === false) {
1525
        // Get user info
1526
        $userData = DB::queryFirstRow(
1527
            'SELECT email, auth_type, login
1528
            FROM ' . prefixTable('users') . '
1529
            WHERE id = %i',
1530
            $post_user_id
1531
        );
1532
        if (DB::count() > 0 && empty($userData['email']) === false) {
1533
            // Generate pwd
1534
            $password = generateQuickPassword();
1535
1536
            // GEnerate new keys
1537
            $userKeys = generateUserKeys($password);
1538
1539
            // Save in DB
1540
            DB::update(
1541
                prefixTable('users'),
1542
                array(
1543
                    'public_key' => $userKeys['public_key'],
1544
                    'private_key' => $userKeys['private_key'],
1545
                    'special' => 'generate-keys',
1546
                ),
1547
                'id=%i',
1548
                $post_user_id
1549
            );
1550
1551
            return prepareExchangedData(
1552
                array(
1553
                    'error' => false,
1554
                    'message' => '',
1555
                    'userTemporaryCode' => $password,
1556
                ),
1557
                'encode'
1558
            );
1559
        }
1560
        
1561
        return prepareExchangedData(
1562
            array(
1563
                'error' => true,
1564
                'message' => langHdl('no_email_set'),
1565
            ),
1566
            'encode'
1567
        );
1568
    }
1569
        
1570
    return prepareExchangedData(
1571
        array(
1572
            'error' => true,
1573
            'message' => langHdl('error_no_user'),
1574
        ),
1575
        'encode'
1576
    );
1577
}
1578
1579
function startReEncryptingUserSharekeys(
1580
    int $post_user_id,
1581
    bool $post_self_change,
1582
    array $SETTINGS
1583
): string
1584
{
1585
    //$post_user_id = is_null($post_user_id) === true ? $_SESSION['user_id'] : $post_user_id;
1586
1587
    if (is_null($post_user_id) === false && isset($post_user_id) === true && empty($post_user_id) === false) {
1588
        // Check if user exists
1589
        DB::queryFirstRow(
1590
            'SELECT *
1591
            FROM ' . prefixTable('users') . '
1592
            WHERE id = %i',
1593
            $post_user_id
1594
        );
1595
        if (DB::count() > 0) {
1596
            // Include libraries
1597
            include_once $SETTINGS['cpassman_dir'] . '/sources/aes.functions.php';
1598
1599
            // CLear old sharekeys
1600
            if ($post_self_change === false) {
1601
                deleteUserObjetsKeys($post_user_id, $SETTINGS);
1602
            }
1603
1604
            // Continu with next step
1605
            return prepareExchangedData(
1606
                array(
1607
                    'error' => false,
1608
                    'message' => '',
1609
                    'step' => 'step1',
1610
                    'userId' => $post_user_id,
1611
                    'start' => 0,
1612
                    'self_change' => $post_self_change,
1613
                ),
1614
                'encode'
1615
            );
1616
        }
1617
        // Nothing to do
1618
        return prepareExchangedData(
1619
            array(
1620
                'error' => true,
1621
                'message' => langHdl('error_no_user'),
1622
            ),
1623
            'encode'
1624
        );
1625
    }
1626
1627
    return prepareExchangedData(
1628
        array(
1629
            'error' => true,
1630
            'message' => langHdl('error_no_user'),
1631
        ),
1632
        'encode'
1633
    );
1634
}
1635
1636
function continueReEncryptingUserSharekeys(
1637
    int $post_user_id,
1638
    bool $post_self_change,
1639
    string $post_action,
1640
    int $post_start,
1641
    int $post_length,
1642
    array $SETTINGS
1643
): string
1644
{
1645
    if (is_null($post_user_id) === false && isset($post_user_id) === true && empty($post_user_id) === false) {
1646
        // Check if user exists
1647
        $userInfo = DB::queryFirstRow(
1648
            'SELECT public_key
1649
            FROM ' . prefixTable('users') . '
1650
            WHERE id = %i',
1651
            $post_user_id
1652
        );
1653
        if (DB::count() > 0) {
1654
            // Include libraries
1655
            include_once $SETTINGS['cpassman_dir'] . '/sources/aes.functions.php';
1656
1657
            // WHAT STEP TO PERFORM?
1658
            if ($post_action === 'step0') {
1659
                // CLear old sharekeys
1660
                if ($post_self_change === false) {
1661
                    deleteUserObjetsKeys($post_user_id, $SETTINGS);
1662
                }
1663
1664
                $post_action = 'step1';
1665
            }
1666
1667
            // STEP 1 - ITEMS
1668
            if ($post_action === 'step1') {
1669
                $return = continueReEncryptingUserSharekeysStep1(
1670
                    $post_user_id,
1671
                    $post_self_change,
1672
                    $post_action,
1673
                    $post_start,
1674
                    $post_length,
1675
                    $userInfo['public_key'],
1676
                    $SETTINGS
1677
                );
1678
1679
                $next_start = $return['next_start'];
1680
                $post_action = $return['post_action'];
1681
            }
1682
1683
            // STEP 2 - LOGS
1684
            if ($post_action === 'step2') {
1685
                $return = continueReEncryptingUserSharekeysStep2(
1686
                    $post_user_id,
1687
                    $post_self_change,
1688
                    $post_action,
1689
                    $post_start,
1690
                    $post_length,
1691
                    $userInfo['public_key'],
1692
                    $SETTINGS
1693
                );
1694
1695
                $next_start = $return['next_start'];
1696
                $post_action = $return['post_action'];
1697
            }
1698
1699
            // STEP 3 - FIELDS
1700
            if ($post_action === 'step3') {
1701
                $return = continueReEncryptingUserSharekeysStep3(
1702
                    $post_user_id,
1703
                    $post_self_change,
1704
                    $post_action,
1705
                    $post_start,
1706
                    $post_length,
1707
                    $userInfo['public_key'],
1708
                    $SETTINGS
1709
                );
1710
1711
                $next_start = $return['next_start'];
1712
                $post_action = $return['post_action'];
1713
            }
1714
            
1715
            // STEP 4 - SUGGESTIONS
1716
            if ($post_action === 'step4') {
1717
                $return = continueReEncryptingUserSharekeysStep4(
1718
                    $post_user_id,
1719
                    $post_self_change,
1720
                    $post_action,
1721
                    $post_start,
1722
                    $post_length,
1723
                    $userInfo['public_key'],
1724
                    $SETTINGS
1725
                );
1726
1727
                $next_start = $return['next_start'];
1728
                $post_action = $return['post_action'];
1729
            }
1730
            
1731
            // STEP 5 - FILES
1732
            if ($post_action === 'step5') {
1733
                $return = continueReEncryptingUserSharekeysStep5(
1734
                    $post_user_id,
1735
                    $post_self_change,
1736
                    $post_action,
1737
                    $post_start,
1738
                    $post_length,
1739
                    $userInfo['public_key'],
1740
                    $SETTINGS
1741
                );
1742
1743
                $next_start = $return['next_start'];
1744
                $post_action = $return['post_action'];
1745
            }
1746
            
1747
            // STEP 6 - PERSONAL ITEMS
1748
            if ($post_action === 'step6') {
1749
                $return = continueReEncryptingUserSharekeysStep6(
1750
                    $post_user_id,
1751
                    $post_self_change,
1752
                    $post_action,
1753
                    $post_start,
1754
                    $post_length,
1755
                    $userInfo['public_key'],
1756
                    $SETTINGS
1757
                );
1758
1759
                $next_start = $return['next_start'];
1760
                $post_action = $return['post_action'];
1761
            }
1762
1763
            // Continu with next step
1764
            return prepareExchangedData(
1765
                array(
1766
                    'error' => false,
1767
                    'message' => '',
1768
                    'step' => $post_action,
1769
                    'start' => isset($next_start) === true ? $next_start : 0,
1770
                    'userId' => $post_user_id,
1771
                    'self_change' => $post_self_change,
1772
                ),
1773
                'encode'
1774
            );
1775
        }
1776
        
1777
        // Nothing to do
1778
        return prepareExchangedData(
1779
            array(
1780
                'error' => false,
1781
                'message' => '',
1782
                'step' => 'finished',
1783
                'start' => 0,
1784
                'userId' => $post_user_id,
1785
                'self_change' => $post_self_change,
1786
            ),
1787
            'encode'
1788
        );
1789
    }
1790
    
1791
    // Nothing to do
1792
    return prepareExchangedData(
1793
        array(
1794
            'error' => true,
1795
            'message' => langHdl('error_no_user'),
1796
        ),
1797
        'encode'
1798
    );
1799
}
1800
1801
function continueReEncryptingUserSharekeysStep1(
1802
    int $post_user_id,
1803
    bool $post_self_change,
1804
    string $post_action,
1805
    int $post_start,
1806
    int $post_length,
1807
    string $user_public_key,
1808
    array $SETTINGS
1809
): string
1810
{
1811
    // Loop on items
1812
    $rows = DB::query(
1813
        'SELECT id, pw
1814
        FROM ' . prefixTable('items') . '
1815
        WHERE perso = 0
1816
        LIMIT ' . $post_start . ', ' . $post_length
1817
    );
1818
    foreach ($rows as $record) {
1819
        // Get itemKey from current user
1820
        $currentUserKey = DB::queryFirstRow(
1821
            'SELECT share_key, increment_id
1822
            FROM ' . prefixTable('sharekeys_items') . '
1823
            WHERE object_id = %i AND user_id = %i',
1824
            $record['id'],
1825
            $_SESSION['user_id']
1826
        );
1827
        if (count($currentUserKey) === 0) continue;
1828
1829
        // Decrypt itemkey with admin key
1830
        $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $_SESSION['user']['private_key']);
1831
        
1832
        // Encrypt Item key
1833
        $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
1834
        
1835
        // Save the key in DB
1836
        if ($post_self_change === false) {
1837
            DB::insert(
1838
                prefixTable('sharekeys_items'),
1839
                array(
1840
                    'object_id' => (int) $record['id'],
1841
                    'user_id' => (int) $post_user_id,
1842
                    'share_key' => $share_key_for_item,
1843
                )
1844
            );
1845
        } else {
1846
            // Get itemIncrement from selected user
1847
            if ((int) $post_user_id !== (int) $_SESSION['user_id']) {
1848
                $currentUserKey = DB::queryFirstRow(
1849
                    'SELECT increment_id
1850
                    FROM ' . prefixTable('sharekeys_items') . '
1851
                    WHERE object_id = %i AND user_id = %i',
1852
                    $record['id'],
1853
                    $post_user_id
1854
                );
1855
1856
                if (DB::count() > 0) {
1857
                    // NOw update
1858
                    DB::update(
1859
                        prefixTable('sharekeys_items'),
1860
                        array(
1861
                            'share_key' => $share_key_for_item,
1862
                        ),
1863
                        'increment_id = %i',
1864
                        $currentUserKey['increment_id']
1865
                    );
1866
                } else {
1867
                    DB::insert(
1868
                        prefixTable('sharekeys_items'),
1869
                        array(
1870
                            'object_id' => (int) $record['id'],
1871
                            'user_id' => (int) $post_user_id,
1872
                            'share_key' => $share_key_for_item,
1873
                        )
1874
                    );
1875
                }
1876
            }
1877
        }
1878
    }
1879
1880
    // SHould we change step?
1881
    DB::query(
1882
        'SELECT *
1883
        FROM ' . prefixTable('items') . '
1884
        WHERE perso = 0'
1885
    );
1886
1887
    $next_start = (int) $post_start + (int) $post_length;
1888
    return [
0 ignored issues
show
Bug Best Practice introduced by
The expression return array('next_start...() ? 'step2' : 'step1') returns the type array<string,integer|string> which is incompatible with the type-hinted return string.
Loading history...
1889
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
1890
        'post_action' => $next_start > DB::count() ? 'step2' : 'step1',
1891
    ];
1892
}
1893
1894
function continueReEncryptingUserSharekeysStep2(
1895
    int $post_user_id,
1896
    bool $post_self_change,
1897
    string $post_action,
1898
    int $post_start,
1899
    int $post_length,
1900
    string $user_public_key,
1901
    array $SETTINGS
1902
): array
1903
{
1904
    // Loop on logs
1905
    $rows = DB::query(
1906
        'SELECT increment_id
1907
        FROM ' . prefixTable('log_items') . '
1908
        WHERE raison LIKE "at_pw :%" AND encryption_type = "teampass_aes"
1909
        LIMIT ' . $post_start . ', ' . $post_length
1910
    );
1911
    foreach ($rows as $record) {
1912
        // Get itemKey from current user
1913
        $currentUserKey = DB::queryFirstRow(
1914
            'SELECT share_key
1915
            FROM ' . prefixTable('sharekeys_logs') . '
1916
            WHERE object_id = %i AND user_id = %i',
1917
            $record['increment_id'],
1918
            $_SESSION['user_id']
1919
        );
1920
1921
        // Decrypt itemkey with admin key
1922
        $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $_SESSION['user']['private_key']);
1923
1924
        // Encrypt Item key
1925
        $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
1926
1927
        // Save the key in DB
1928
        if ($post_self_change === false) {
1929
            DB::insert(
1930
                prefixTable('sharekeys_logs'),
1931
                array(
1932
                    'object_id' => (int) $record['increment_id'],
1933
                    'user_id' => (int) $post_user_id,
1934
                    'share_key' => $share_key_for_item,
1935
                )
1936
            );
1937
        } else {
1938
            // Get itemIncrement from selected user
1939
            if ((int) $post_user_id !== (int) $_SESSION['user_id']) {
1940
                $currentUserKey = DB::queryFirstRow(
1941
                    'SELECT increment_id
1942
                    FROM ' . prefixTable('sharekeys_items') . '
1943
                    WHERE object_id = %i AND user_id = %i',
1944
                    $record['id'],
1945
                    $post_user_id
1946
                );
1947
            }
1948
1949
            // NOw update
1950
            DB::update(
1951
                prefixTable('sharekeys_logs'),
1952
                array(
1953
                    'share_key' => $share_key_for_item,
1954
                ),
1955
                'increment_id = %i',
1956
                $currentUserKey['increment_id']
1957
            );
1958
        }
1959
    }
1960
1961
    // SHould we change step?
1962
    DB::query(
1963
        'SELECT increment_id
1964
        FROM ' . prefixTable('log_items') . '
1965
        WHERE raison LIKE "at_pw :%" AND encryption_type = "teampass_aes"'
1966
    );
1967
1968
    $next_start = (int) $post_start + (int) $post_length;
1969
    return [
1970
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
1971
        'post_action' => $next_start > DB::count() ? 'step3' : 'step2',
1972
    ];
1973
}
1974
1975
function continueReEncryptingUserSharekeysStep3(
1976
    int $post_user_id,
1977
    bool $post_self_change,
1978
    string $post_action,
1979
    int $post_start,
1980
    int $post_length,
1981
    string $user_public_key,
1982
    array $SETTINGS
1983
): array
1984
{
1985
    // Loop on fields
1986
    $rows = DB::query(
1987
        'SELECT id
1988
        FROM ' . prefixTable('categories_items') . '
1989
        WHERE encryption_type = "teampass_aes"
1990
        LIMIT ' . $post_start . ', ' . $post_length
1991
    );
1992
    foreach ($rows as $record) {
1993
        // Get itemKey from current user
1994
        $currentUserKey = DB::queryFirstRow(
1995
            'SELECT share_key
1996
            FROM ' . prefixTable('sharekeys_fields') . '
1997
            WHERE object_id = %i AND user_id = %i',
1998
            $record['id'],
1999
            $_SESSION['user_id']
2000
        );
2001
2002
        // Decrypt itemkey with admin key
2003
        $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $_SESSION['user']['private_key']);
2004
2005
        // Encrypt Item key
2006
        $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
2007
2008
        // Save the key in DB
2009
        if ($post_self_change === false) {
2010
            DB::insert(
2011
                prefixTable('sharekeys_fields'),
2012
                array(
2013
                    'object_id' => (int) $record['id'],
2014
                    'user_id' => (int) $post_user_id,
2015
                    'share_key' => $share_key_for_item,
2016
                )
2017
            );
2018
        } else {
2019
            // Get itemIncrement from selected user
2020
            if ((int) $post_user_id !== (int) $_SESSION['user_id']) {
2021
                $currentUserKey = DB::queryFirstRow(
2022
                    'SELECT increment_id
2023
                    FROM ' . prefixTable('sharekeys_items') . '
2024
                    WHERE object_id = %i AND user_id = %i',
2025
                    $record['id'],
2026
                    $post_user_id
2027
                );
2028
            }
2029
2030
            // NOw update
2031
            DB::update(
2032
                prefixTable('sharekeys_fields'),
2033
                array(
2034
                    'share_key' => $share_key_for_item,
2035
                ),
2036
                'increment_id = %i',
2037
                $currentUserKey['increment_id']
2038
            );
2039
        }
2040
    }
2041
2042
    // SHould we change step?
2043
    DB::query(
2044
        'SELECT *
2045
        FROM ' . prefixTable('categories_items') . '
2046
        WHERE encryption_type = "teampass_aes"'
2047
    );
2048
2049
    $next_start = (int) $post_start + (int) $post_length;
2050
    return [
2051
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2052
        'post_action' => $next_start > DB::count() ? 'step4' : 'step3',
2053
    ];
2054
}
2055
2056
function continueReEncryptingUserSharekeysStep4(
2057
    int $post_user_id,
2058
    bool $post_self_change,
2059
    string $post_action,
2060
    int $post_start,
2061
    int $post_length,
2062
    string $user_public_key,
2063
    array $SETTINGS
2064
): array
2065
{
2066
    // Loop on suggestions
2067
    $rows = DB::query(
2068
        'SELECT id
2069
        FROM ' . prefixTable('suggestion') . '
2070
        LIMIT ' . $post_start . ', ' . $post_length
2071
    );
2072
    foreach ($rows as $record) {
2073
        // Get itemKey from current user
2074
        $currentUserKey = DB::queryFirstRow(
2075
            'SELECT share_key
2076
            FROM ' . prefixTable('sharekeys_suggestions') . '
2077
            WHERE object_id = %i AND user_id = %i',
2078
            $record['id'],
2079
            $_SESSION['user_id']
2080
        );
2081
2082
        // Decrypt itemkey with admin key
2083
        $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $_SESSION['user']['private_key']);
2084
2085
        // Encrypt Item key
2086
        $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
2087
2088
        // Save the key in DB
2089
        if ($post_self_change === false) {
2090
            DB::insert(
2091
                prefixTable('sharekeys_suggestions'),
2092
                array(
2093
                    'object_id' => (int) $record['id'],
2094
                    'user_id' => (int) $post_user_id,
2095
                    'share_key' => $share_key_for_item,
2096
                )
2097
            );
2098
        } else {
2099
            // Get itemIncrement from selected user
2100
            if ((int) $post_user_id !== (int) $_SESSION['user_id']) {
2101
                $currentUserKey = DB::queryFirstRow(
2102
                    'SELECT increment_id
2103
                    FROM ' . prefixTable('sharekeys_items') . '
2104
                    WHERE object_id = %i AND user_id = %i',
2105
                    $record['id'],
2106
                    $post_user_id
2107
                );
2108
            }
2109
2110
            // NOw update
2111
            DB::update(
2112
                prefixTable('sharekeys_suggestions'),
2113
                array(
2114
                    'share_key' => $share_key_for_item,
2115
                ),
2116
                'increment_id = %i',
2117
                $currentUserKey['increment_id']
2118
            );
2119
        }
2120
    }
2121
2122
    // SHould we change step?
2123
    DB::query(
2124
        'SELECT *
2125
        FROM ' . prefixTable('suggestion')
2126
    );
2127
2128
    $next_start = (int) $post_start + (int) $post_length;
2129
    return [
2130
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2131
        'post_action' => $next_start > DB::count() ? 'step5' : 'step4',
2132
    ];
2133
}
2134
2135
function continueReEncryptingUserSharekeysStep5(
2136
    int $post_user_id,
2137
    bool $post_self_change,
2138
    string $post_action,
2139
    int $post_start,
2140
    int $post_length,
2141
    string $user_public_key,
2142
    array $SETTINGS
2143
): array
2144
{
2145
    // Loop on files
2146
    $rows = DB::query(
2147
        'SELECT id
2148
        FROM ' . prefixTable('files') . '
2149
        WHERE status = "' . TP_ENCRYPTION_NAME . '"
2150
        LIMIT ' . $post_start . ', ' . $post_length
2151
    ); //aes_encryption
2152
    foreach ($rows as $record) {
2153
        // Get itemKey from current user
2154
        $currentUserKey = DB::queryFirstRow(
2155
            'SELECT share_key
2156
            FROM ' . prefixTable('sharekeys_files') . '
2157
            WHERE object_id = %i AND user_id = %i',
2158
            $record['id'],
2159
            $_SESSION['user_id']
2160
        );
2161
2162
        // Decrypt itemkey with admin key
2163
        $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $_SESSION['user']['private_key']);
2164
2165
        // Encrypt Item key
2166
        $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
2167
2168
        // Save the key in DB
2169
        if ($post_self_change === false) {
2170
            DB::insert(
2171
                prefixTable('sharekeys_files'),
2172
                array(
2173
                    'object_id' => (int) $record['id'],
2174
                    'user_id' => (int) $post_user_id,
2175
                    'share_key' => $share_key_for_item,
2176
                )
2177
            );
2178
        } else {
2179
            // Get itemIncrement from selected user
2180
            if ((int) $post_user_id !== (int) $_SESSION['user_id']) {
2181
                $currentUserKey = DB::queryFirstRow(
2182
                    'SELECT increment_id
2183
                    FROM ' . prefixTable('sharekeys_items') . '
2184
                    WHERE object_id = %i AND user_id = %i',
2185
                    $record['id'],
2186
                    $post_user_id
2187
                );
2188
            }
2189
2190
            // NOw update
2191
            DB::update(
2192
                prefixTable('sharekeys_files'),
2193
                array(
2194
                    'share_key' => $share_key_for_item,
2195
                ),
2196
                'increment_id = %i',
2197
                $currentUserKey['increment_id']
2198
            );
2199
        }
2200
    }
2201
2202
    // SHould we change step?
2203
    DB::query(
2204
        'SELECT *
2205
        FROM ' . prefixTable('files') . '
2206
        WHERE status = "' . TP_ENCRYPTION_NAME . '"'
2207
    );
2208
2209
    $next_start = (int) $post_start + (int) $post_length;
2210
    return [
2211
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2212
        'post_action' => $next_start > DB::count() ? 'step6' : 'step5',
2213
    ];
2214
}
2215
2216
function continueReEncryptingUserSharekeysStep6(
2217
    int $post_user_id,
2218
    bool $post_self_change,
2219
    string $post_action,
2220
    int $post_start,
2221
    int $post_length,
2222
    string $user_public_key,
2223
    array $SETTINGS
2224
): array
2225
{
2226
    // IF USER IS NOT THE SAME
2227
    if ((int) $post_user_id === (int) $_SESSION['user_id']) {
2228
        return [
2229
            'next_start' => 0,
2230
            'post_action' => 'finished',
2231
        ];
2232
    }
2233
    
2234
    // Loop on persoanl items
2235
    if (count($_SESSION['personal_folders']) > 0) {
2236
        $rows = DB::query(
2237
            'SELECT id, pw
2238
            FROM ' . prefixTable('items') . '
2239
            WHERE perso = 1 AND id_tree IN %ls
2240
            LIMIT ' . $post_start . ', ' . $post_length,
2241
            $_SESSION['personal_folders']
2242
        );
2243
        foreach ($rows as $record) {
2244
            // Get itemKey from current user
2245
            $currentUserKey = DB::queryFirstRow(
2246
                'SELECT share_key, increment_id
2247
                FROM ' . prefixTable('sharekeys_items') . '
2248
                WHERE object_id = %i AND user_id = %i',
2249
                $record['id'],
2250
                $_SESSION['user_id']
2251
            );
2252
2253
            // Decrypt itemkey with admin key
2254
            $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $_SESSION['user']['private_key']);
2255
2256
            // Encrypt Item key
2257
            $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
2258
2259
            // Save the key in DB
2260
            if ($post_self_change === false) {
2261
                DB::insert(
2262
                    prefixTable('sharekeys_items'),
2263
                    array(
2264
                        'object_id' => (int) $record['id'],
2265
                        'user_id' => (int) $post_user_id,
2266
                        'share_key' => $share_key_for_item,
2267
                    )
2268
                );
2269
            } else {
2270
                // Get itemIncrement from selected user
2271
                if ((int) $post_user_id !== (int) $_SESSION['user_id']) {
2272
                    $currentUserKey = DB::queryFirstRow(
2273
                        'SELECT increment_id
2274
                        FROM ' . prefixTable('sharekeys_items') . '
2275
                        WHERE object_id = %i AND user_id = %i',
2276
                        $record['id'],
2277
                        $post_user_id
2278
                    );
2279
                }
2280
2281
                // NOw update
2282
                DB::update(
2283
                    prefixTable('sharekeys_items'),
2284
                    array(
2285
                        'share_key' => $share_key_for_item,
2286
                    ),
2287
                    'increment_id = %i',
2288
                    $currentUserKey['increment_id']
2289
                );
2290
            }
2291
        }
2292
    }
2293
2294
    // SHould we change step?
2295
    DB::query(
2296
        'SELECT *
2297
        FROM ' . prefixTable('items') . '
2298
        WHERE perso = 0'
2299
    );
2300
2301
    $next_start = (int) $post_start + (int) $post_length;
2302
    return [
2303
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2304
        'post_action' => $next_start > DB::count() ? 'finished' : 'step6',
2305
    ];
2306
}
2307
2308
function migrateTo3_DoUserPersonalItemsEncryption(
2309
    int $post_user_id,
2310
    int $post_start,
2311
    int $post_length,
2312
    string $post_user_psk,
2313
    array $SETTINGS
2314
) {
2315
    $next_step = '';
2316
2317
    if (is_null($post_user_id) === false && isset($post_user_id) === true && empty($post_user_id) === false) {
2318
        // Check if user exists
2319
        $userInfo = DB::queryFirstRow(
2320
            'SELECT public_key, encrypted_psk
2321
            FROM ' . prefixTable('users') . '
2322
            WHERE id = %i',
2323
            $post_user_id
2324
        );
2325
        if (DB::count() > 0) {
2326
            // check if psk is correct.
2327
            if (empty($userInfo['encrypted_psk']) === false) {
2328
                $user_key_encoded = defuse_validate_personal_key(
2329
                    $post_user_psk,
2330
                    $userInfo['encrypted_psk']
2331
                );
2332
2333
                if (strpos($user_key_encoded, "Error ") !== false) {
2334
                    return prepareExchangedData(
2335
                        array(
2336
                            'error' => true,
2337
                            'message' => langHdl('bad_psk'),
2338
                        ),
2339
                        'encode'
2340
                    );
2341
                }
2342
2343
                // Loop on persoanl items
2344
                $rows = DB::query(
2345
                    'SELECT id, pw
2346
                    FROM ' . prefixTable('items') . '
2347
                    WHERE perso = 1 AND id_tree IN %ls
2348
                    LIMIT ' . $post_start . ', ' . $post_length,
2349
                    $_SESSION['personal_folders']
2350
                );
2351
                $countUserPersonalItems = DB::count();
2352
                foreach ($rows as $record) {
2353
                    if ($record['encryption_type'] !== 'teampass_aes') {
2354
                        // Decrypt with Defuse
2355
                        $passwd = cryption(
2356
                            $record['pw'],
2357
                            $user_key_encoded,
2358
                            'decrypt',
2359
                            $SETTINGS
2360
                        );
2361
2362
                        // Encrypt with Object Key
2363
                        $cryptedStuff = doDataEncryption($passwd['string']);
2364
2365
                        // Store new password in DB
2366
                        DB::update(
2367
                            prefixTable('items'),
2368
                            array(
2369
                                'pw' => $cryptedStuff['encrypted'],
2370
                                'encryption_type' => 'teampass_aes',
2371
                            ),
2372
                            'id = %i',
2373
                            $record['id']
2374
                        );
2375
2376
                        // Insert in DB the new object key for this item by user
2377
                        DB::insert(
2378
                            prefixTable('sharekeys_items'),
2379
                            array(
2380
                                'object_id' => (int) $record['id'],
2381
                                'user_id' => (int) $post_user_id,
2382
                                'share_key' => encryptUserObjectKey($cryptedStuff['objectKey'], $userInfo['public_key']),
2383
                            )
2384
                        );
2385
2386
2387
                        // Does this item has Files?
2388
                        // Loop on files
2389
                        $rows = DB::query(
2390
                            'SELECT id, file
2391
                            FROM ' . prefixTable('files') . '
2392
                            WHERE status != %s
2393
                            AND id_item = %i',
2394
                            TP_ENCRYPTION_NAME,
2395
                            $record['id']
2396
                        );
2397
                        //aes_encryption
2398
                        foreach ($rows as $record2) {
2399
                            // Now decrypt the file
2400
                            prepareFileWithDefuse(
2401
                                'decrypt',
2402
                                $SETTINGS['path_to_upload_folder'] . '/' . $record2['file'],
2403
                                $SETTINGS['path_to_upload_folder'] . '/' . $record2['file'] . '.delete',
2404
                                $SETTINGS,
2405
                                $post_user_psk
2406
                            );
2407
2408
                            // Encrypt the file
2409
                            $encryptedFile = encryptFile($record2['file'] . '.delete', $SETTINGS['path_to_upload_folder']);
2410
2411
                            DB::update(
2412
                                prefixTable('files'),
2413
                                array(
2414
                                    'file' => $encryptedFile['fileHash'],
2415
                                    'status' => TP_ENCRYPTION_NAME,
2416
                                ),
2417
                                'id = %i',
2418
                                $record2['id']
2419
                            );
2420
2421
                            // Save key
2422
                            DB::insert(
2423
                                prefixTable('sharekeys_files'),
2424
                                array(
2425
                                    'object_id' => (int) $record2['id'],
2426
                                    'user_id' => (int) $_SESSION['user_id'],
2427
                                    'share_key' => encryptUserObjectKey($encryptedFile['objectKey'], $_SESSION['user']['public_key']),
2428
                                )
2429
                            );
2430
2431
                            // Unlink original file
2432
                            unlink($SETTINGS['path_to_upload_folder'] . '/' . $record2['file']);
2433
                        }
2434
                    }
2435
                }
2436
2437
                // SHould we change step?
2438
                $next_start = (int) $post_start + (int) $post_length;
2439
                if ($next_start > $countUserPersonalItems) {
2440
                    // Now update user
2441
                    DB::update(
2442
                        prefixTable('users'),
2443
                        array(
2444
                            'special' => 'none',
2445
                            'upgrade_needed' => 0,
2446
                            'encrypted_psk' => '',
2447
                        ),
2448
                        'id = %i',
2449
                        $post_user_id
2450
                    );
2451
2452
                    $next_step = 'finished';
2453
                    $next_start = 0;
2454
                }
2455
2456
                // Continu with next step
2457
                return prepareExchangedData(
2458
                    array(
2459
                        'error' => false,
2460
                        'message' => '',
2461
                        'step' => $next_step,
2462
                        'start' => $next_start,
2463
                        'userId' => $post_user_id
2464
                    ),
2465
                    'encode'
2466
                );
2467
            }
2468
        }
2469
        
2470
        // Nothing to do
2471
        return prepareExchangedData(
2472
            array(
2473
                'error' => true,
2474
                'message' => langHdl('error_no_user'),
2475
            ),
2476
            'encode'
2477
        );
2478
    }
2479
    
2480
    // Nothing to do
2481
    return prepareExchangedData(
2482
        array(
2483
            'error' => true,
2484
            'message' => langHdl('error_no_user'),
2485
        ),
2486
        'encode'
2487
    );
2488
}
2489
2490
2491
function getUserInfo(
2492
    int $post_user_id,
2493
    string $post_fields,
2494
    array $SETTINGS
2495
)
2496
{
2497
    if (is_null($post_user_id) === false && isset($post_user_id) === true && empty($post_user_id) === false) {
2498
        // Get user info
2499
        $userData = DB::queryFirstRow(
2500
            'SELECT '.$post_fields.'
2501
            FROM ' . prefixTable('users') . '
2502
            WHERE id = %i',
2503
            $post_user_id
2504
        );
2505
        if (DB::count() > 0) {
2506
            return prepareExchangedData(
2507
                array(
2508
                    'error' => false,
2509
                    'message' => '',
2510
                    'queryResults' => $userData,
2511
                ),
2512
                'encode'
2513
            );
2514
        }
2515
    }
2516
    return prepareExchangedData(
2517
        array(
2518
            'error' => true,
2519
            'message' => langHdl('error_no_user'),
2520
        ),
2521
        'encode'
2522
    );
2523
}
2524
2525
function changeUserAuthenticationPassword(
2526
    int $post_user_id,
2527
    string $post_current_pwd,
2528
    string $post_new_pwd,
2529
    array $SETTINGS
2530
)
2531
{
2532
    if (is_null($post_user_id) === false && isset($post_user_id) === true && empty($post_user_id) === false) {
2533
        // Get user info
2534
        $userData = DB::queryFirstRow(
2535
            'SELECT auth_type, login, private_key
2536
            FROM ' . prefixTable('users') . '
2537
            WHERE id = %i',
2538
            $post_user_id
2539
        );
2540
        if (DB::count() > 0) {
2541
            // Now check if current password is correct
2542
            // For this, just check if it is possible to decrypt the privatekey
2543
            // And compare it to the one in session
2544
            $privateKey = decryptPrivateKey($post_current_pwd, $userData['private_key']);
2545
2546
            // Load superGlobals
2547
            include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
2548
            $superGlobal = new protect\SuperGlobal\SuperGlobal();
2549
2550
            if ($superGlobal->get('private_key', 'SESSION', 'user') === $privateKey) {
2551
                // Encrypt it with new password
2552
                $hashedPrivateKey = encryptPrivateKey($post_new_pwd, $privateKey);
2553
2554
                // Generate new hash for auth password
2555
                // load passwordLib library
2556
                $pwdlib = new SplClassLoader('PasswordLib', '../includes/libraries');
2557
                $pwdlib->register();
2558
                $pwdlib = new PasswordLib\PasswordLib();
2559
2560
                // Prepare variables
2561
                $newPw = $pwdlib->createPasswordHash($post_new_pwd);
2562
2563
                // Update user account
2564
                DB::update(
2565
                    prefixTable('users'),
2566
                    array(
2567
                        'private_key' => $hashedPrivateKey,
2568
                        'pw' => $newPw,
2569
                        'special' => 'none',
2570
                    ),
2571
                    'id = %i',
2572
                    $post_user_id
2573
                );
2574
2575
                $superGlobal->put('private_key', $privateKey, 'SESSION', 'user');
2576
2577
                return prepareExchangedData(
2578
                    array(
2579
                        'error' => false,
2580
                        'message' => langHdl('done'),'',
2581
                    ),
2582
                    'encode'
2583
                );
2584
            }
2585
            
2586
            // ERROR
2587
            return prepareExchangedData(
2588
                array(
2589
                    'error' => true,
2590
                    'message' => langHdl('bad_password'),
2591
                ),
2592
                'encode'
2593
            );
2594
        }
2595
    }
2596
        
2597
    return prepareExchangedData(
2598
        array(
2599
            'error' => true,
2600
            'message' => langHdl('error_no_user'),
2601
        ),
2602
        'encode'
2603
    );
2604
}
2605
2606
            
2607
function changeUserLDAPAuthenticationPassword(
2608
    int $post_user_id,
2609
    string $post_previous_pwd,
2610
    string $post_current_pwd,
2611
    array $SETTINGS
2612
)
2613
{
2614
    if (is_null($post_user_id) === false && isset($post_user_id) === true && empty($post_user_id) === false) {
2615
        // Get user info
2616
        $userData = DB::queryFirstRow(
2617
            'SELECT auth_type, login, private_key, special
2618
            FROM ' . prefixTable('users') . '
2619
            WHERE id = %i',
2620
            $post_user_id
2621
        );
2622
        
2623
        if (DB::count() > 0) {
2624
            // Now check if current password is correct (only if not ldap)
2625
            if ($userData['auth_type'] === 'ldap' && $userData['special'] === 'auth-pwd-change') {
2626
                // As it is a change for an LDAP user
2627
                
2628
                // Now check if current password is correct
2629
                // For this, just check if it is possible to decrypt the privatekey
2630
                // And compare it to the one in session
2631
                $privateKey = decryptPrivateKey($post_previous_pwd, $userData['private_key']);
2632
2633
                // Encrypt it with new password
2634
                $hashedPrivateKey = encryptPrivateKey($post_current_pwd, $privateKey);
2635
2636
                // Update user account
2637
                DB::update(
2638
                    prefixTable('users'),
2639
                    array(
2640
                        'private_key' => $hashedPrivateKey,
2641
                        'special' => 'none',
2642
                    ),
2643
                    'id = %i',
2644
                    $post_user_id
2645
                );
2646
2647
                // Load superGlobals
2648
                include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
2649
                $superGlobal = new protect\SuperGlobal\SuperGlobal();
2650
                $superGlobal->put('private_key', $privateKey, 'SESSION', 'user');
2651
2652
                return prepareExchangedData(
2653
                    array(
2654
                        'error' => false,
2655
                        'message' => langHdl('done'),'',
2656
                    ),
2657
                    'encode'
2658
                );
2659
            }
2660
2661
            // For this, just check if it is possible to decrypt the privatekey
2662
            // And try to decrypt one existing key
2663
            $privateKey = decryptPrivateKey($post_previous_pwd, $userData['private_key']);
2664
2665
            if (empty($privateKey) === true) {
2666
                return prepareExchangedData(
2667
                    array(
2668
                        'error' => true,
2669
                        'message' => langHdl('password_is_not_correct'),
2670
                    ),
2671
                    'encode'
2672
                );
2673
            }
2674
2675
            // Test if possible to decvrypt one key
2676
            // Get one item
2677
            $record = DB::queryFirstRow(
2678
                'SELECT id, pw
2679
                FROM ' . prefixTable('items') . '
2680
                WHERE perso = 0'
2681
            );
2682
2683
            // Get itemKey from current user
2684
            $currentUserKey = DB::queryFirstRow(
2685
                'SELECT share_key, increment_id
2686
                FROM ' . prefixTable('sharekeys_items') . '
2687
                WHERE object_id = %i AND user_id = %i',
2688
                $record['id'],
2689
                $post_user_id
2690
            );
2691
2692
            if (count($currentUserKey) > 0) {
2693
                // Decrypt itemkey with user key
2694
                // use old password to decrypt private_key
2695
                $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $privateKey);
2696
                
2697
                if (empty(base64_decode($itemKey)) === false) {
2698
                    // GOOD password
2699
                    // Encrypt it with current password
2700
                    $hashedPrivateKey = encryptPrivateKey($post_current_pwd, $privateKey);
2701
                    
2702
                    // Update user account
2703
                    DB::update(
2704
                        prefixTable('users'),
2705
                        array(
2706
                            'private_key' => $hashedPrivateKey,
2707
                            'special' => 'none',
2708
                        ),
2709
                        'id = %i',
2710
                        $post_user_id
2711
                    );
2712
                    
2713
                    // Load superGlobals
2714
                    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
2715
                    $superGlobal = new protect\SuperGlobal\SuperGlobal();
2716
                    $superGlobal->put('private_key', $privateKey, 'SESSION', 'user');
2717
2718
                    return prepareExchangedData(
2719
                        array(
2720
                            'error' => false,
2721
                            'message' => langHdl('done'),
2722
                        ),
2723
                        'encode'
2724
                    );
2725
                }
2726
            }
2727
            
2728
            // ERROR
2729
            return prepareExchangedData(
2730
                array(
2731
                    'error' => true,
2732
                    'message' => langHdl('bad_password'),
2733
                ),
2734
                'encode'
2735
            );
2736
        }
2737
    }
2738
2739
    // ERROR
2740
    return prepareExchangedData(
2741
        array(
2742
            'error' => true,
2743
            'message' => langHdl('error_no_user'),
2744
        ),
2745
        'encode'
2746
    );
2747
}
2748
2749
2750
function increaseSessionDuration(
2751
    int $duration
2752
): string
2753
{
2754
    // check if session is not already expired.
2755
    if ($_SESSION['sessionDuration'] > time()) {
2756
        // Calculate end of session
2757
        $_SESSION['sessionDuration'] = (int) ($_SESSION['sessionDuration'] + $duration);
2758
        // Update table
2759
        DB::update(
2760
            prefixTable('users'),
2761
            array(
2762
                'session_end' => $_SESSION['sessionDuration'],
2763
            ),
2764
            'id = %i',
2765
            $_SESSION['user_id']
2766
        );
2767
        // Return data
2768
        return '[{"new_value":"' . $_SESSION['sessionDuration'] . '"}]';
2769
    }
2770
    
2771
    return '[{"new_value":"expired"}]';
2772
}