Passed
Push — wip_sessions ( b55e55...384f5b )
by Nils
05:05
created

buildEmail()   C

Complexity

Conditions 9
Paths 436

Size

Total Lines 75
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 9
eloc 37
c 2
b 0
f 0
nc 436
nop 7
dl 0
loc 75
rs 5.0657

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
 *
13
 * @project   Teampass
14
 * @file      main.functions.php
15
 * ---
16
 *
17
 * @author    Nils Laumaillé ([email protected])
18
 *
19
 * @copyright 2009-2023 Teampass.net
20
 *
21
 * @license   https://spdx.org/licenses/GPL-3.0-only.html#licenseText GPL-3.0
22
 * ---
23
 *
24
 * @see       https://www.teampass.net
25
 */
26
27
use LdapRecord\Connection;
28
use ForceUTF8\Encoding;
29
use Elegant\Sanitizer\Sanitizer;
30
use voku\helper\AntiXSS;
31
use Hackzilla\PasswordGenerator\Generator\ComputerPasswordGenerator;
32
use Hackzilla\PasswordGenerator\RandomGenerator\Php7RandomGenerator;
33
use TeampassClasses\SessionManager\SessionManager;
34
use Symfony\Component\HttpFoundation\Request;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Request. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
35
use TeampassClasses\Language\Language;
36
use TeampassClasses\NestedTree\NestedTree;
37
use Defuse\Crypto\Key;
38
use Defuse\Crypto\Crypto;
39
use Defuse\Crypto\KeyProtectedByPassword;
40
use Defuse\Crypto\File as CryptoFile;
41
use Defuse\Crypto\Exception as CryptoException;
42
use Elegant\Sanitizer\Filters\Uppercase;
43
use PHPMailer\PHPMailer\PHPMailer;
44
use PasswordLib\PasswordLib;
45
use Symfony\Component\Process\Exception\ProcessFailedException;
46
use Symfony\Component\Process\Process;
47
use Symfony\Component\Process\PhpExecutableFinder;
48
use TeampassClasses\Encryption\Encryption;
49
50
51
// Load config if $SETTINGS not defined
52
if (isset($SETTINGS['cpassman_dir']) === false || empty($SETTINGS['cpassman_dir']) === true) {
53
    include_once __DIR__ . '/../includes/config/tp.config.php';
54
}
55
56
header('Content-type: text/html; charset=utf-8');
57
header('Cache-Control: no-cache, must-revalidate');
58
59
loadClasses('DB');
60
$session = SessionManager::getSession();
61
62
63
/**
64
 * genHash().
65
 *
66
 * Generate a hash for user login
67
 *
68
 * @param string $password What password
69
 * @param string $cost     What cost
70
 *
71
 * @return string|void
72
 */
73
function bCrypt(
74
    string $password,
75
    string $cost
76
): ?string
77
{
78
    $salt = sprintf('$2y$%02d$', $cost);
79
    if (function_exists('openssl_random_pseudo_bytes')) {
80
        $salt .= bin2hex(openssl_random_pseudo_bytes(11));
81
    } else {
82
        $chars = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
83
        for ($i = 0; $i < 22; ++$i) {
84
            $salt .= $chars[mt_rand(0, 63)];
85
        }
86
    }
87
88
    return crypt($password, $salt);
89
}
90
91
/**
92
 * Checks if a string is hex encoded
93
 *
94
 * @param string $str
95
 * @return boolean
96
 */
97
function isHex(string $str): bool
98
{
99
    if (str_starts_with(strtolower($str), '0x')) {
100
        $str = substr($str, 2);
101
    }
102
103
    return ctype_xdigit($str);
104
}
105
106
/**
107
 * Defuse cryption function.
108
 *
109
 * @param string $message   what to de/crypt
110
 * @param string $ascii_key key to use
111
 * @param string $type      operation to perform
112
 * @param array  $SETTINGS  Teampass settings
113
 *
114
 * @return array
115
 */
116
function cryption(string $message, string $ascii_key, string $type, ?array $SETTINGS = []): array
117
{
118
    $ascii_key = empty($ascii_key) === true ? file_get_contents(SECUREPATH.'/'.SECUREFILE) : $ascii_key;
119
    $err = false;
120
    
121
    // convert KEY
122
    $key = Key::loadFromAsciiSafeString($ascii_key);
123
    try {
124
        if ($type === 'encrypt') {
125
            $text = Crypto::encrypt($message, $key);
126
        } elseif ($type === 'decrypt') {
127
            $text = Crypto::decrypt($message, $key);
128
        }
129
    } catch (CryptoException\WrongKeyOrModifiedCiphertextException $ex) {
130
        $err = 'an attack! either the wrong key was loaded, or the ciphertext has changed since it was created either corrupted in the database or intentionally modified by someone trying to carry out an attack.';
131
    } catch (CryptoException\BadFormatException $ex) {
132
        $err = $ex;
133
    } catch (CryptoException\EnvironmentIsBrokenException $ex) {
134
        $err = $ex;
135
    } catch (CryptoException\CryptoException $ex) {
136
        $err = $ex;
137
    } catch (CryptoException\IOException $ex) {
138
        $err = $ex;
139
    }
140
141
    return [
142
        'string' => $text ?? '',
143
        'error' => $err,
144
    ];
145
}
146
147
/**
148
 * Generating a defuse key.
149
 *
150
 * @return string
151
 */
152
function defuse_generate_key()
153
{
154
    $key = Key::createNewRandomKey();
155
    $key = $key->saveToAsciiSafeString();
156
    return $key;
157
}
158
159
/**
160
 * Generate a Defuse personal key.
161
 *
162
 * @param string $psk psk used
163
 *
164
 * @return string
165
 */
166
function defuse_generate_personal_key(string $psk): string
167
{
168
    $protected_key = KeyProtectedByPassword::createRandomPasswordProtectedKey($psk);
169
    return $protected_key->saveToAsciiSafeString(); // save this in user table
170
}
171
172
/**
173
 * Validate persoanl key with defuse.
174
 *
175
 * @param string $psk                   the user's psk
176
 * @param string $protected_key_encoded special key
177
 *
178
 * @return string
179
 */
180
function defuse_validate_personal_key(string $psk, string $protected_key_encoded): string
181
{
182
    try {
183
        $protected_key_encoded = KeyProtectedByPassword::loadFromAsciiSafeString($protected_key_encoded);
184
        $user_key = $protected_key_encoded->unlockKey($psk);
185
        $user_key_encoded = $user_key->saveToAsciiSafeString();
186
    } catch (CryptoException\EnvironmentIsBrokenException $ex) {
187
        return 'Error - Major issue as the encryption is broken.';
188
    } catch (CryptoException\WrongKeyOrModifiedCiphertextException $ex) {
189
        return 'Error - The saltkey is not the correct one.';
190
    }
191
192
    return $user_key_encoded;
193
    // store it in session once user has entered his psk
194
}
195
196
/**
197
 * Decrypt a defuse string if encrypted.
198
 *
199
 * @param string $value Encrypted string
200
 *
201
 * @return string Decrypted string
202
 */
203
function defuseReturnDecrypted(string $value, $SETTINGS): string
204
{
205
    if (substr($value, 0, 3) === 'def') {
206
        $value = cryption($value, '', 'decrypt', $SETTINGS)['string'];
207
    }
208
209
    return $value;
210
}
211
212
/**
213
 * Trims a string depending on a specific string.
214
 *
215
 * @param string|array $chaine  what to trim
216
 * @param string       $element trim on what
217
 *
218
 * @return string
219
 */
220
function trimElement($chaine, string $element): string
221
{
222
    if (! empty($chaine)) {
223
        if (is_array($chaine) === true) {
224
            $chaine = implode(';', $chaine);
225
        }
226
        $chaine = trim($chaine);
227
        if (substr($chaine, 0, 1) === $element) {
228
            $chaine = substr($chaine, 1);
229
        }
230
        if (substr($chaine, strlen($chaine) - 1, 1) === $element) {
231
            $chaine = substr($chaine, 0, strlen($chaine) - 1);
232
        }
233
    }
234
235
    return $chaine;
236
}
237
238
/**
239
 * Permits to suppress all "special" characters from string.
240
 *
241
 * @param string $string  what to clean
242
 * @param bool   $special use of special chars?
243
 *
244
 * @return string
245
 */
246
function cleanString(string $string, bool $special = false): string
247
{
248
    // Create temporary table for special characters escape
249
    $tabSpecialChar = [];
250
    for ($i = 0; $i <= 31; ++$i) {
251
        $tabSpecialChar[] = chr($i);
252
    }
253
    array_push($tabSpecialChar, '<br />');
254
    if ((int) $special === 1) {
255
        $tabSpecialChar = array_merge($tabSpecialChar, ['</li>', '<ul>', '<ol>']);
256
    }
257
258
    return str_replace($tabSpecialChar, "\n", $string);
259
}
260
261
/**
262
 * Erro manager for DB.
263
 *
264
 * @param array $params output from query
265
 *
266
 * @return void
267
 */
268
function db_error_handler(array $params): void
269
{
270
    echo 'Error: ' . $params['error'] . "<br>\n";
271
    echo 'Query: ' . $params['query'] . "<br>\n";
272
    throw new Exception('Error - Query', 1);
273
}
274
275
/**
276
 * Identify user's rights
277
 *
278
 * @param string|array $groupesVisiblesUser  [description]
279
 * @param string|array $groupesInterditsUser [description]
280
 * @param string       $isAdmin              [description]
281
 * @param string       $idFonctions          [description]
282
 *
283
 * @return bool
284
 */
285
function identifyUserRights(
286
    $groupesVisiblesUser,
287
    $groupesInterditsUser,
288
    $isAdmin,
289
    $idFonctions,
290
    $SETTINGS
291
) {
292
    $session = SessionManager::getSession();
293
    $tree = new NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
294
295
    // Check if user is ADMINISTRATOR    
296
    (int) $isAdmin === 1 ?
297
        identAdmin(
298
            $idFonctions,
299
            $SETTINGS, /** @scrutinizer ignore-type */
300
            $tree
301
        )
302
        :
303
        identUser(
304
            $groupesVisiblesUser,
305
            $groupesInterditsUser,
306
            $idFonctions,
307
            $SETTINGS, /** @scrutinizer ignore-type */
308
            $tree
309
        );
310
311
    // update user's timestamp
312
    DB::update(
313
        prefixTable('users'),
314
        [
315
            'timestamp' => time(),
316
        ],
317
        'id=%i',
318
        $session->get('user-id')
319
    );
320
321
    return true;
322
}
323
324
/**
325
 * Identify administrator.
326
 *
327
 * @param string $idFonctions Roles of user
328
 * @param array  $SETTINGS    Teampass settings
329
 * @param object $tree        Tree of folders
330
 *
331
 * @return bool
332
 */
333
function identAdmin($idFonctions, $SETTINGS, $tree)
334
{
335
    
336
    $session = SessionManager::getSession();
337
    $groupesVisibles = [];
338
    $session->set('user-personal_folders', []);
339
    $session->set('user-accessible_folders', []);
340
    $session->set('user-no_access_folders', []);
341
    $session->set('user-personal_visible_folders', []);
342
    $session->set('user-read_only_folders', []);
343
    $session->set('system-list_restricted_folders_for_items', []);
344
    $session->set('system-list_folders_editable_by_role', []);
345
    $session->set('user-list_folders_limited', []);
346
    $session->set('user-forbiden_personal_folders', []);
347
    $globalsUserId = $session->get('user-id');
348
    $globalsVisibleFolders = $session->get('user-accessible_folders');
349
    $globalsPersonalVisibleFolders = $session->get('user-personal_visible_folders');
350
    // Get list of Folders
351
    $rows = DB::query('SELECT id FROM ' . prefixTable('nested_tree') . ' WHERE personal_folder = %i', 0);
352
    foreach ($rows as $record) {
353
        array_push($groupesVisibles, $record['id']);
354
    }
355
    $session->set('user-accessible_folders', $groupesVisibles);
356
    $session->set('user-all_non_personal_folders', $groupesVisibles);
357
    // Exclude all PF
358
    $where = new WhereClause('and');
359
    // create a WHERE statement of pieces joined by ANDs
360
    $where->add('personal_folder=%i', 1);
361
    if (
362
        isset($SETTINGS['enable_pf_feature']) === true
363
        && (int) $SETTINGS['enable_pf_feature'] === 1
364
    ) {
365
        $where->add('title=%s', $globalsUserId);
366
        $where->negateLast();
367
    }
368
    // Get ID of personal folder
369
    $persfld = DB::queryfirstrow(
370
        'SELECT id FROM ' . prefixTable('nested_tree') . ' WHERE title = %s',
371
        $globalsUserId
372
    );
373
    if (empty($persfld['id']) === false) {
374
        if (in_array($persfld['id'], $globalsVisibleFolders) === false) {
375
            array_push($globalsVisibleFolders, $persfld['id']);
376
            array_push($globalsPersonalVisibleFolders, $persfld['id']);
377
            // get all descendants
378
            $tree->rebuild();
379
            $tst = $tree->getDescendants($persfld['id']);
380
            foreach ($tst as $t) {
381
                array_push($globalsVisibleFolders, $t->id);
382
                array_push($globalsPersonalVisibleFolders, $t->id);
383
            }
384
        }
385
    }
386
387
    // get complete list of ROLES
388
    $tmp = explode(';', $idFonctions);
389
    $rows = DB::query(
390
        'SELECT * FROM ' . prefixTable('roles_title') . '
391
        ORDER BY title ASC'
392
    );
393
    foreach ($rows as $record) {
394
        if (! empty($record['id']) && ! in_array($record['id'], $tmp)) {
395
            array_push($tmp, $record['id']);
396
        }
397
    }
398
    $session->set('user-roles', implode(';', $tmp));
399
    $session->set('user-admin', 1);
400
    // Check if admin has created Folders and Roles
401
    DB::query('SELECT * FROM ' . prefixTable('nested_tree') . '');
402
    $session->set('user-nb_folders', DB::count());
403
    DB::query('SELECT * FROM ' . prefixTable('roles_title'));
404
    $session->set('user-nb_roles', DB::count());
405
406
    return true;
407
}
408
409
/**
410
 * Permits to convert an element to array.
411
 *
412
 * @param string|array $element Any value to be returned as array
413
 *
414
 * @return array
415
 */
416
function convertToArray($element): array
417
{
418
    if (is_string($element) === true) {
419
        if (empty($element) === true) {
420
            return [];
421
        }
422
        return explode(
423
            ';',
424
            trimElement($element, ';')
425
        );
426
    }
427
    return $element;
428
}
429
430
/**
431
 * Defines the rights the user has.
432
 *
433
 * @param string|array $allowedFolders  Allowed folders
434
 * @param string|array $noAccessFolders Not allowed folders
435
 * @param string|array $userRoles       Roles of user
436
 * @param array        $SETTINGS        Teampass settings
437
 * @param object       $tree            Tree of folders
438
 * 
439
 * @return bool
440
 */
441
function identUser(
442
    $allowedFolders,
443
    $noAccessFolders,
444
    $userRoles,
445
    array $SETTINGS,
446
    object $tree
447
) {
448
    
449
    $session = SessionManager::getSession();
450
    // Init
451
    $session->set('user-accessible_folders', []);
452
    $session->set('user-personal_folders', []);
453
    $session->set('user-no_access_folders', []);
454
    $session->set('user-personal_visible_folders', []);
455
    $session->set('user-read_only_folders', []);
456
    $session->set('user-user-roles', $userRoles);
457
    $session->set('user-admin', 0);
458
    // init
459
    $personalFolders = [];
460
    $readOnlyFolders = [];
461
    $noAccessPersonalFolders = [];
462
    $restrictedFoldersForItems = [];
463
    $foldersLimited = [];
464
    $foldersLimitedFull = [];
465
    $allowedFoldersByRoles = [];
466
    $globalsUserId = $session->get('user-id');
467
    $globalsPersonalFolders = $session->get('user-personal_folder_enabled');
468
    // Ensure consistency in array format
469
    $noAccessFolders = convertToArray($noAccessFolders);
470
    $userRoles = convertToArray($userRoles);
471
    $allowedFolders = convertToArray($allowedFolders);
472
    
473
    // Get list of folders depending on Roles
474
    $arrays = identUserGetFoldersFromRoles(
475
        $userRoles,
476
        $allowedFoldersByRoles,
477
        $readOnlyFolders,
478
        $allowedFolders
479
    );
480
    $allowedFoldersByRoles = $arrays['allowedFoldersByRoles'];
481
    $readOnlyFolders = $arrays['readOnlyFolders'];
482
483
    // Does this user is allowed to see other items
484
    $inc = 0;
485
    $rows = DB::query(
486
        'SELECT id, id_tree FROM ' . prefixTable('items') . '
487
            WHERE restricted_to LIKE %ss AND inactif = %s'.
488
            (count($allowedFolders) > 0 ? ' AND id_tree NOT IN ('.implode(',', $allowedFolders).')' : ''),
489
        $globalsUserId,
490
        '0'
491
    );
492
    foreach ($rows as $record) {
493
        // Exclude restriction on item if folder is fully accessible
494
        //if (in_array($record['id_tree'], $allowedFolders) === false) {
495
            $restrictedFoldersForItems[$record['id_tree']][$inc] = $record['id'];
496
            ++$inc;
497
        //}
498
    }
499
500
    // Check for the users roles if some specific rights exist on items
501
    $rows = DB::query(
502
        'SELECT i.id_tree, r.item_id
503
        FROM ' . prefixTable('items') . ' as i
504
        INNER JOIN ' . prefixTable('restriction_to_roles') . ' as r ON (r.item_id=i.id)
505
        WHERE i.id_tree <> "" '.
506
        (count($userRoles) > 0 ? 'AND r.role_id IN %li ' : '').
507
        'ORDER BY i.id_tree ASC',
508
        $userRoles
509
    );
510
    $inc = 0;
511
    foreach ($rows as $record) {
512
        //if (isset($record['id_tree'])) {
513
            $foldersLimited[$record['id_tree']][$inc] = $record['item_id'];
514
            array_push($foldersLimitedFull, $record['id_tree']);
515
            ++$inc;
516
        //}
517
    }
518
519
    // Get list of Personal Folders
520
    $arrays = identUserGetPFList(
521
        $globalsPersonalFolders,
522
        $allowedFolders,
523
        $globalsUserId,
524
        $personalFolders,
525
        $noAccessPersonalFolders,
526
        $foldersLimitedFull,
527
        $allowedFoldersByRoles,
528
        array_keys($restrictedFoldersForItems),
529
        $readOnlyFolders,
530
        $noAccessFolders,
531
        isset($SETTINGS['enable_pf_feature']) === true ? $SETTINGS['enable_pf_feature'] : 0,
532
        $tree
533
    );
534
    $allowedFolders = $arrays['allowedFolders'];
535
    $personalFolders = $arrays['personalFolders'];
536
    $noAccessPersonalFolders = $arrays['noAccessPersonalFolders'];
537
538
    // Return data
539
    $session->set('user-all_non_personal_folders', $allowedFolders);
540
    $session->set('user-accessible_folders', array_unique(array_merge($allowedFolders, $personalFolders), SORT_NUMERIC));
541
    $session->set('user-read_only_folders', $readOnlyFolders);
542
    $session->set('user-no_access_folders', $noAccessFolders);
543
    $session->set('user-personal_folders', $personalFolders);
544
    $session->set('user-list_folders_limited', $foldersLimited);
545
    $session->set('system-list_folders_editable_by_role', $allowedFoldersByRoles, 'SESSION');
546
    $session->set('system-list_restricted_folders_for_items', $restrictedFoldersForItems);
547
    $session->set('user-forbiden_personal_folders', $noAccessPersonalFolders);
548
    $session->set(
549
        'all_folders_including_no_access',
550
        array_unique(array_merge(
551
            $allowedFolders,
552
            $personalFolders,
553
            $noAccessFolders,
554
            $readOnlyFolders
555
        ), SORT_NUMERIC)
556
    );
557
    // Folders and Roles numbers
558
    DB::queryfirstrow('SELECT id FROM ' . prefixTable('nested_tree') . '');
559
    $session->set('user-nb_folders', DB::count());
560
    DB::queryfirstrow('SELECT id FROM ' . prefixTable('roles_title'));
561
    $session->set('user-nb_roles', DB::count());
562
    // check if change proposals on User's items
563
    if (isset($SETTINGS['enable_suggestion']) === true && (int) $SETTINGS['enable_suggestion'] === 1) {
564
        $countNewItems = DB::query(
565
            'SELECT COUNT(*)
566
            FROM ' . prefixTable('items_change') . ' AS c
567
            LEFT JOIN ' . prefixTable('log_items') . ' AS i ON (c.item_id = i.id_item)
568
            WHERE i.action = %s AND i.id_user = %i',
569
            'at_creation',
570
            $globalsUserId
571
        );
572
        $session->set('user-nb_item_change_proposals', $countNewItems);
573
    } else {
574
        $session->set('user-nb_item_change_proposals', 0);
575
    }
576
577
    return true;
578
}
579
580
/**
581
 * Get list of folders depending on Roles
582
 * 
583
 * @param array $userRoles
584
 * @param array $allowedFoldersByRoles
585
 * @param array $readOnlyFolders
586
 * @param array $allowedFolders
587
 * 
588
 * @return array
589
 */
590
function identUserGetFoldersFromRoles($userRoles, $allowedFoldersByRoles, $readOnlyFolders, $allowedFolders) : array
591
{
592
    $rows = DB::query(
593
        'SELECT *
594
        FROM ' . prefixTable('roles_values') . '
595
        WHERE type IN %ls'.(count($userRoles) > 0 ? ' AND role_id IN %li' : ''),
596
        ['W', 'ND', 'NE', 'NDNE', 'R'],
597
        $userRoles,
598
    );
599
    foreach ($rows as $record) {
600
        if ($record['type'] === 'R') {
601
            array_push($readOnlyFolders, $record['folder_id']);
602
        } elseif (in_array($record['folder_id'], $allowedFolders) === false) {
603
            array_push($allowedFoldersByRoles, $record['folder_id']);
604
        }
605
    }
606
    $allowedFoldersByRoles = array_unique($allowedFoldersByRoles);
607
    $readOnlyFolders = array_unique($readOnlyFolders);
608
    
609
    // Clean arrays
610
    foreach ($allowedFoldersByRoles as $value) {
611
        $key = array_search($value, $readOnlyFolders);
612
        if ($key !== false) {
613
            unset($readOnlyFolders[$key]);
614
        }
615
    }
616
    return [
617
        'readOnlyFolders' => $readOnlyFolders,
618
        'allowedFoldersByRoles' => $allowedFoldersByRoles
619
    ];
620
}
621
622
/**
623
 * Get list of Personal Folders
624
 * 
625
 * @param int $globalsPersonalFolders
626
 * @param array $allowedFolders
627
 * @param int $globalsUserId
628
 * @param array $personalFolders
629
 * @param array $noAccessPersonalFolders
630
 * @param array $foldersLimitedFull
631
 * @param array $allowedFoldersByRoles
632
 * @param array $restrictedFoldersForItems
633
 * @param array $readOnlyFolders
634
 * @param array $noAccessFolders
635
 * @param int $enablePfFeature
636
 * @param object $tree
637
 * 
638
 * @return array
639
 */
640
function identUserGetPFList(
641
    $globalsPersonalFolders,
642
    $allowedFolders,
643
    $globalsUserId,
644
    $personalFolders,
645
    $noAccessPersonalFolders,
646
    $foldersLimitedFull,
647
    $allowedFoldersByRoles,
648
    $restrictedFoldersForItems,
649
    $readOnlyFolders,
650
    $noAccessFolders,
651
    $enablePfFeature,
652
    $tree
653
)
654
{
655
    if (
656
        (int) $enablePfFeature === 1
657
        && (int) $globalsPersonalFolders === 1
658
    ) {
659
        $persoFld = DB::queryfirstrow(
660
            'SELECT id
661
            FROM ' . prefixTable('nested_tree') . '
662
            WHERE title = %s AND personal_folder = %i'.
663
            (count($allowedFolders) > 0 ? ' AND id NOT IN ('.implode(',', $allowedFolders).')' : ''),
664
            $globalsUserId,
665
            1
666
        );
667
        if (empty($persoFld['id']) === false) {
668
            array_push($personalFolders, $persoFld['id']);
669
            array_push($allowedFolders, $persoFld['id']);
670
            // get all descendants
671
            $ids = $tree->getDescendants($persoFld['id'], false, false, true);
672
            foreach ($ids as $id) {
673
                //array_push($allowedFolders, $id);
674
                array_push($personalFolders, $id);
675
            }
676
        }
677
    }
678
    
679
    // Exclude all other PF
680
    $where = new WhereClause('and');
681
    $where->add('personal_folder=%i', 1);
682
    if (count($personalFolders) > 0) {
683
        $where->add('id NOT IN ('.implode(',', $personalFolders).')');
684
    }
685
    if (
686
        (int) $enablePfFeature === 1
687
        && (int) $globalsPersonalFolders === 1
688
    ) {
689
        $where->add('title=%s', $globalsUserId);
690
        $where->negateLast();
691
    }
692
    $persoFlds = DB::query(
693
        'SELECT id
694
        FROM ' . prefixTable('nested_tree') . '
695
        WHERE %l',
696
        $where
697
    );
698
    foreach ($persoFlds as $persoFldId) {
699
        array_push($noAccessPersonalFolders, $persoFldId['id']);
700
    }
701
702
    // All folders visibles
703
    $allowedFolders = array_unique(array_merge(
704
        $allowedFolders,
705
        $foldersLimitedFull,
706
        $allowedFoldersByRoles,
707
        $restrictedFoldersForItems,
708
        $readOnlyFolders
709
    ), SORT_NUMERIC);
710
    // Exclude from allowed folders all the specific user forbidden folders
711
    if (count($noAccessFolders) > 0) {
712
        $allowedFolders = array_diff($allowedFolders, $noAccessFolders);
713
    }
714
715
    return [
716
        'allowedFolders' => array_diff(array_diff($allowedFolders, $noAccessPersonalFolders), $personalFolders),
717
        'personalFolders' => $personalFolders,
718
        'noAccessPersonalFolders' => $noAccessPersonalFolders
719
    ];
720
}
721
722
723
/**
724
 * Update the CACHE table.
725
 *
726
 * @param string $action   What to do
727
 * @param array  $SETTINGS Teampass settings
728
 * @param int    $ident    Ident format
729
 * 
730
 * @return void
731
 */
732
function updateCacheTable(string $action, ?int $ident = null): void
733
{
734
    if ($action === 'reload') {
735
        // Rebuild full cache table
736
        cacheTableRefresh();
737
    } elseif ($action === 'update_value' && is_null($ident) === false) {
738
        // UPDATE an item
739
        cacheTableUpdate($ident);
740
    } elseif ($action === 'add_value' && is_null($ident) === false) {
741
        // ADD an item
742
        cacheTableAdd($ident);
743
    } elseif ($action === 'delete_value' && is_null($ident) === false) {
744
        // DELETE an item
745
        DB::delete(prefixTable('cache'), 'id = %i', $ident);
746
    }
747
}
748
749
/**
750
 * Cache table - refresh.
751
 *
752
 * @return void
753
 */
754
function cacheTableRefresh(): void
755
{
756
    // Load class DB
757
    loadClasses('DB');
758
759
    //Load Tree
760
    $tree = new NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
761
    // truncate table
762
    DB::query('TRUNCATE TABLE ' . prefixTable('cache'));
763
    // reload date
764
    $rows = DB::query(
765
        'SELECT *
766
        FROM ' . prefixTable('items') . ' as i
767
        INNER JOIN ' . prefixTable('log_items') . ' as l ON (l.id_item = i.id)
768
        AND l.action = %s
769
        AND i.inactif = %i',
770
        'at_creation',
771
        0
772
    );
773
    foreach ($rows as $record) {
774
        if (empty($record['id_tree']) === false) {
775
            // Get all TAGS
776
            $tags = '';
777
            $itemTags = DB::query(
778
                'SELECT tag
779
                FROM ' . prefixTable('tags') . '
780
                WHERE item_id = %i AND tag != ""',
781
                $record['id']
782
            );
783
            foreach ($itemTags as $itemTag) {
784
                $tags .= $itemTag['tag'] . ' ';
785
            }
786
787
            // Get renewal period
788
            $resNT = DB::queryfirstrow(
789
                'SELECT renewal_period
790
                FROM ' . prefixTable('nested_tree') . '
791
                WHERE id = %i',
792
                $record['id_tree']
793
            );
794
            // form id_tree to full foldername
795
            $folder = [];
796
            $arbo = $tree->getPath($record['id_tree'], true);
797
            foreach ($arbo as $elem) {
798
                // Check if title is the ID of a user
799
                if (is_numeric($elem->title) === true) {
800
                    // Is this a User id?
801
                    $user = DB::queryfirstrow(
802
                        'SELECT id, login
803
                        FROM ' . prefixTable('users') . '
804
                        WHERE id = %i',
805
                        $elem->title
806
                    );
807
                    if (count($user) > 0) {
808
                        $elem->title = $user['login'];
809
                    }
810
                }
811
                // Build path
812
                array_push($folder, stripslashes($elem->title));
813
            }
814
            // store data
815
            DB::insert(
816
                prefixTable('cache'),
817
                [
818
                    'id' => $record['id'],
819
                    'label' => $record['label'],
820
                    'description' => $record['description'] ?? '',
821
                    'url' => isset($record['url']) && ! empty($record['url']) ? $record['url'] : '0',
822
                    'tags' => $tags,
823
                    'id_tree' => $record['id_tree'],
824
                    'perso' => $record['perso'],
825
                    'restricted_to' => isset($record['restricted_to']) && ! empty($record['restricted_to']) ? $record['restricted_to'] : '0',
826
                    'login' => $record['login'] ?? '',
827
                    'folder' => implode(' > ', $folder),
828
                    'author' => $record['id_user'],
829
                    'renewal_period' => $resNT['renewal_period'] ?? '0',
830
                    'timestamp' => $record['date'],
831
                ]
832
            );
833
        }
834
    }
835
}
836
837
/**
838
 * Cache table - update existing value.
839
 *
840
 * @param int    $ident    Ident format
841
 * 
842
 * @return void
843
 */
844
function cacheTableUpdate(?int $ident = null): void
845
{
846
    $session = SessionManager::getSession();
847
    loadClasses('DB');
848
849
    //Load Tree
850
    $tree = new NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
851
    // get new value from db
852
    $data = DB::queryfirstrow(
853
        'SELECT label, description, id_tree, perso, restricted_to, login, url
854
        FROM ' . prefixTable('items') . '
855
        WHERE id=%i',
856
        $ident
857
    );
858
    // Get all TAGS
859
    $tags = '';
860
    $itemTags = DB::query(
861
        'SELECT tag
862
            FROM ' . prefixTable('tags') . '
863
            WHERE item_id = %i AND tag != ""',
864
        $ident
865
    );
866
    foreach ($itemTags as $itemTag) {
867
        $tags .= $itemTag['tag'] . ' ';
868
    }
869
    // form id_tree to full foldername
870
    $folder = [];
871
    $arbo = $tree->getPath($data['id_tree'], true);
872
    foreach ($arbo as $elem) {
873
        // Check if title is the ID of a user
874
        if (is_numeric($elem->title) === true) {
875
            // Is this a User id?
876
            $user = DB::queryfirstrow(
877
                'SELECT id, login
878
                FROM ' . prefixTable('users') . '
879
                WHERE id = %i',
880
                $elem->title
881
            );
882
            if (count($user) > 0) {
883
                $elem->title = $user['login'];
884
            }
885
        }
886
        // Build path
887
        array_push($folder, stripslashes($elem->title));
888
    }
889
    // finaly update
890
    DB::update(
891
        prefixTable('cache'),
892
        [
893
            'label' => $data['label'],
894
            'description' => $data['description'],
895
            'tags' => $tags,
896
            'url' => isset($data['url']) && ! empty($data['url']) ? $data['url'] : '0',
897
            'id_tree' => $data['id_tree'],
898
            'perso' => $data['perso'],
899
            'restricted_to' => isset($data['restricted_to']) && ! empty($data['restricted_to']) ? $data['restricted_to'] : '0',
900
            'login' => $data['login'] ?? '',
901
            'folder' => implode(' » ', $folder),
902
            'author' => $session->get('user-id'),
903
        ],
904
        'id = %i',
905
        $ident
906
    );
907
}
908
909
/**
910
 * Cache table - add new value.
911
 *
912
 * @param int    $ident    Ident format
913
 * 
914
 * @return void
915
 */
916
function cacheTableAdd(?int $ident = null): void
917
{
918
    $session = SessionManager::getSession();
919
    $globalsUserId = $session->get('user-id');
920
921
    // Load class DB
922
    loadClasses('DB');
923
924
    //Load Tree
925
    $tree = new NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
926
    // get new value from db
927
    $data = DB::queryFirstRow(
928
        'SELECT i.label, i.description, i.id_tree as id_tree, i.perso, i.restricted_to, i.id, i.login, i.url, l.date
929
        FROM ' . prefixTable('items') . ' as i
930
        INNER JOIN ' . prefixTable('log_items') . ' as l ON (l.id_item = i.id)
931
        WHERE i.id = %i
932
        AND l.action = %s',
933
        $ident,
934
        'at_creation'
935
    );
936
    // Get all TAGS
937
    $tags = '';
938
    $itemTags = DB::query(
939
        'SELECT tag
940
            FROM ' . prefixTable('tags') . '
941
            WHERE item_id = %i AND tag != ""',
942
        $ident
943
    );
944
    foreach ($itemTags as $itemTag) {
945
        $tags .= $itemTag['tag'] . ' ';
946
    }
947
    // form id_tree to full foldername
948
    $folder = [];
949
    $arbo = $tree->getPath($data['id_tree'], true);
950
    foreach ($arbo as $elem) {
951
        // Check if title is the ID of a user
952
        if (is_numeric($elem->title) === true) {
953
            // Is this a User id?
954
            $user = DB::queryfirstrow(
955
                'SELECT id, login
956
                FROM ' . prefixTable('users') . '
957
                WHERE id = %i',
958
                $elem->title
959
            );
960
            if (count($user) > 0) {
961
                $elem->title = $user['login'];
962
            }
963
        }
964
        // Build path
965
        array_push($folder, stripslashes($elem->title));
966
    }
967
    // finaly update
968
    DB::insert(
969
        prefixTable('cache'),
970
        [
971
            'id' => $data['id'],
972
            'label' => $data['label'],
973
            'description' => $data['description'],
974
            'tags' => isset($tags) && empty($tags) === false ? $tags : 'None',
975
            'url' => isset($data['url']) && ! empty($data['url']) ? $data['url'] : '0',
976
            'id_tree' => $data['id_tree'],
977
            'perso' => isset($data['perso']) && empty($data['perso']) === false && $data['perso'] !== 'None' ? $data['perso'] : '0',
978
            'restricted_to' => isset($data['restricted_to']) && empty($data['restricted_to']) === false ? $data['restricted_to'] : '0',
979
            'login' => $data['login'] ?? '',
980
            'folder' => implode(' » ', $folder),
981
            'author' => $globalsUserId,
982
            'timestamp' => $data['date'],
983
        ]
984
    );
985
}
986
987
/**
988
 * Do statistics.
989
 *
990
 * @param array $SETTINGS Teampass settings
991
 *
992
 * @return array
993
 */
994
function getStatisticsData(array $SETTINGS): array
995
{
996
    DB::query(
997
        'SELECT id FROM ' . prefixTable('nested_tree') . ' WHERE personal_folder = %i',
998
        0
999
    );
1000
    $counter_folders = DB::count();
1001
    DB::query(
1002
        'SELECT id FROM ' . prefixTable('nested_tree') . ' WHERE personal_folder = %i',
1003
        1
1004
    );
1005
    $counter_folders_perso = DB::count();
1006
    DB::query(
1007
        'SELECT id FROM ' . prefixTable('items') . ' WHERE perso = %i',
1008
        0
1009
    );
1010
    $counter_items = DB::count();
1011
        DB::query(
1012
        'SELECT id FROM ' . prefixTable('items') . ' WHERE perso = %i',
1013
        1
1014
    );
1015
    $counter_items_perso = DB::count();
1016
        DB::query(
1017
        'SELECT id FROM ' . prefixTable('users') . ''
1018
    );
1019
    $counter_users = DB::count();
1020
        DB::query(
1021
        'SELECT id FROM ' . prefixTable('users') . ' WHERE admin = %i',
1022
        1
1023
    );
1024
    $admins = DB::count();
1025
    DB::query(
1026
        'SELECT id FROM ' . prefixTable('users') . ' WHERE gestionnaire = %i',
1027
        1
1028
    );
1029
    $managers = DB::count();
1030
    DB::query(
1031
        'SELECT id FROM ' . prefixTable('users') . ' WHERE read_only = %i',
1032
        1
1033
    );
1034
    $readOnly = DB::count();
1035
    // list the languages
1036
    $usedLang = [];
1037
    $tp_languages = DB::query(
1038
        'SELECT name FROM ' . prefixTable('languages')
1039
    );
1040
    foreach ($tp_languages as $tp_language) {
1041
        DB::query(
1042
            'SELECT * FROM ' . prefixTable('users') . ' WHERE user_language = %s',
1043
            $tp_language['name']
1044
        );
1045
        $usedLang[$tp_language['name']] = round((DB::count() * 100 / $counter_users), 0);
1046
    }
1047
1048
    // get list of ips
1049
    $usedIp = [];
1050
    $tp_ips = DB::query(
1051
        'SELECT user_ip FROM ' . prefixTable('users')
1052
    );
1053
    foreach ($tp_ips as $ip) {
1054
        if (array_key_exists($ip['user_ip'], $usedIp)) {
1055
            $usedIp[$ip['user_ip']] += $usedIp[$ip['user_ip']];
1056
        } elseif (! empty($ip['user_ip']) && $ip['user_ip'] !== 'none') {
1057
            $usedIp[$ip['user_ip']] = 1;
1058
        }
1059
    }
1060
1061
    return [
1062
        'error' => '',
1063
        'stat_phpversion' => phpversion(),
1064
        'stat_folders' => $counter_folders,
1065
        'stat_folders_shared' => intval($counter_folders) - intval($counter_folders_perso),
1066
        'stat_items' => $counter_items,
1067
        'stat_items_shared' => intval($counter_items) - intval($counter_items_perso),
1068
        'stat_users' => $counter_users,
1069
        'stat_admins' => $admins,
1070
        'stat_managers' => $managers,
1071
        'stat_ro' => $readOnly,
1072
        'stat_kb' => $SETTINGS['enable_kb'],
1073
        'stat_pf' => $SETTINGS['enable_pf_feature'],
1074
        'stat_fav' => $SETTINGS['enable_favourites'],
1075
        'stat_teampassversion' => TP_VERSION,
1076
        'stat_ldap' => $SETTINGS['ldap_mode'],
1077
        'stat_agses' => $SETTINGS['agses_authentication_enabled'],
1078
        'stat_duo' => $SETTINGS['duo'],
1079
        'stat_suggestion' => $SETTINGS['enable_suggestion'],
1080
        'stat_api' => $SETTINGS['api'],
1081
        'stat_customfields' => $SETTINGS['item_extra_fields'],
1082
        'stat_syslog' => $SETTINGS['syslog_enable'],
1083
        'stat_2fa' => $SETTINGS['google_authentication'],
1084
        'stat_stricthttps' => $SETTINGS['enable_sts'],
1085
        'stat_mysqlversion' => DB::serverVersion(),
1086
        'stat_languages' => $usedLang,
1087
        'stat_country' => $usedIp,
1088
    ];
1089
}
1090
1091
/**
1092
 * Permits to prepare the way to send the email
1093
 * 
1094
 * @param string $subject       email subject
1095
 * @param string $body          email message
1096
 * @param string $email         email
1097
 * @param string $receiverName  Receiver name
1098
 * @param array  $SETTINGS      settings
1099
 *
1100
 * @return void
1101
 */
1102
function prepareSendingEmail(
1103
    $subject,
1104
    $body,
1105
    $email,
1106
    $receiverName = ''
1107
): void 
1108
{
1109
    DB::insert(
1110
        prefixTable('processes'),
1111
        array(
1112
            'created_at' => time(),
1113
            'process_type' => 'send_email',
1114
            'arguments' => json_encode([
1115
                'subject' => $subject,
1116
                'receivers' => $email,
1117
                'body' => $body,
1118
                'receiver_name' => $receiverName,
1119
            ], JSON_HEX_QUOT | JSON_HEX_TAG),
1120
            'updated_at' => '',
1121
            'finished_at' => '',
1122
            'output' => '',
1123
        )
1124
    );
1125
}
1126
1127
/**
1128
 * Permits to send an email.
1129
 *
1130
 * @param string $subject     email subject
1131
 * @param string $textMail    email message
1132
 * @param string $email       email
1133
 * @param array  $SETTINGS    settings
1134
 * @param string $textMailAlt email message alt
1135
 * @param bool   $silent      no errors
1136
 *
1137
 * @return string some json info
1138
 */
1139
function sendEmail(
1140
    $subject,
1141
    $textMail,
1142
    $email,
1143
    $SETTINGS,
1144
    $textMailAlt = null,
1145
    $silent = true,
1146
    $cron = false
1147
) {
1148
    $lang = new Language(); 
1149
1150
    // CAse where email not defined
1151
    if ($email === 'none' || empty($email) === true) {
1152
        return json_encode(
1153
            [
1154
                'error' => true,
1155
                'message' => $lang->get('forgot_my_pw_email_sent'),
1156
            ]
1157
        );
1158
    }
1159
1160
    // Build and send email
1161
    $email = buildEmail(
1162
        $subject,
1163
        $textMail,
1164
        $email,
1165
        $SETTINGS,
1166
        $textMailAlt = null,
1167
        $silent = true,
1168
        $cron
1169
    );
1170
1171
    if ($silent === false) {
0 ignored issues
show
introduced by
The condition $silent === false is always false.
Loading history...
1172
        return json_encode(
1173
            [
1174
                'error' => false,
1175
                'message' => $lang->get('forgot_my_pw_email_sent'),
1176
            ]
1177
        );
1178
    }
1179
    // Debug purpose
1180
    if ((int) $SETTINGS['email_debug_level'] !== 0 && $cron === false) {
1181
        return json_encode(
1182
            [
1183
                'error' => true,
1184
                'message' => isset($email['ErrorInfo']) === true ? $email['ErrorInfo'] : '',
1185
            ]
1186
        );
1187
    }
1188
    return json_encode(
1189
        [
1190
            'error' => false,
1191
            'message' => $lang->get('share_sent_ok'),
1192
        ]
1193
    );
1194
}
1195
1196
1197
function buildEmail(
1198
    $subject,
1199
    $textMail,
1200
    $email,
1201
    $SETTINGS,
1202
    $textMailAlt = null,
1203
    $silent = true,
1204
    $cron = false
1205
)
1206
{
1207
    // Load PHPMailer
1208
    $mail = new PHPMailer(true);
1209
    $languageDir = $SETTINGS['cpassman_dir'] . '/vendor/phpmailer/phpmailer/language/';
1210
1211
    try {
1212
        // Set language and SMTPDebug
1213
        $mail->setLanguage('en', $languageDir);
1214
        $mail->SMTPDebug = ($cron || $silent) ? 0 : $SETTINGS['email_debug_level'];
1215
        
1216
        /*
1217
        // Define custom Debug output function
1218
        $mail->Debugoutput = function($str, $level) {
1219
            // Path to your log file
1220
            $logFilePath = '/var/log/phpmailer.log';
1221
            file_put_contents($logFilePath, gmdate('Y-m-d H:i:s'). "\t$level\t$str\n", FILE_APPEND | LOCK_EX);
1222
        };
1223
        */
1224
1225
        // Configure SMTP
1226
        $mail->isSMTP();
1227
        $mail->Host = $SETTINGS['email_smtp_server'];
1228
        $mail->SMTPAuth = (int) $SETTINGS['email_smtp_auth'] === 1;
1229
        $mail->Username = $SETTINGS['email_auth_username'];
1230
        $mail->Password = $SETTINGS['email_auth_pwd'];
1231
        $mail->Port = (int) $SETTINGS['email_port'];
1232
        $mail->SMTPSecure = $SETTINGS['email_security'] !== 'none' ? $SETTINGS['email_security'] : '';
1233
        $mail->SMTPAutoTLS = $SETTINGS['email_security'] !== 'none';
1234
        $mail->SMTPOptions = [
1235
            'ssl' => [
1236
                'verify_peer' => false,
1237
                'verify_peer_name' => false,
1238
                'allow_self_signed' => true,
1239
            ],
1240
        ];
1241
1242
        // Set From and FromName
1243
        $mail->From = $SETTINGS['email_from'];
1244
        $mail->FromName = $SETTINGS['email_from_name'];
1245
1246
        // Prepare recipients
1247
        foreach (array_filter(explode(',', $email)) as $dest) {
1248
            $mail->addAddress($dest);
1249
        }
1250
        
1251
        // Prepare HTML and AltBody
1252
        $text_html = emailBody($textMail);
1253
        $mail->WordWrap = 80;
1254
        $mail->isHtml(true);
1255
        $mail->Subject = $subject;
1256
        $mail->Body = $text_html;
1257
        $mail->AltBody = is_null($textMailAlt) ? '' : $textMailAlt;
1258
        
1259
        // Send email
1260
        $mail->send();
1261
        $mail->smtpClose();
1262
        
1263
        return '';
1264
    } catch (Exception $e) {
1265
        if (!$silent || (int) $SETTINGS['email_debug_level'] !== 0) {
1266
            return json_encode([
1267
                'error' => true,
1268
                'errorInfo' => str_replace(["\n", "\t", "\r"], '', $mail->ErrorInfo),
1269
            ]);
1270
        }
1271
        return '';
1272
    }
1273
}
1274
1275
/**
1276
 * Returns the email body.
1277
 *
1278
 * @param string $textMail Text for the email
1279
 */
1280
function emailBody(string $textMail): string
1281
{
1282
    return '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.=
1283
    w3.org/TR/html4/loose.dtd"><html>
1284
    <head><title>Email Template</title>
1285
    <style type="text/css">
1286
    body { background-color: #f0f0f0; padding: 10px 0; margin:0 0 10px =0; }
1287
    </style></head>
1288
    <body style="-ms-text-size-adjust: none; size-adjust: none; margin: 0; padding: 10px 0; background-color: #f0f0f0;" bgcolor="#f0f0f0" leftmargin="0" topmargin="0" marginwidth="0" marginheight="0">
1289
    <table border="0" width="100%" height="100%" cellpadding="0" cellspacing="0" bgcolor="#f0f0f0" style="border-spacing: 0;">
1290
    <tr><td style="border-collapse: collapse;"><br>
1291
        <table border="0" width="100%" cellpadding="0" cellspacing="0" bgcolor="#17357c" style="border-spacing: 0; margin-bottom: 25px;">
1292
        <tr><td style="border-collapse: collapse; padding: 11px 20px;">
1293
            <div style="max-width:150px; max-height:34px; color:#f0f0f0; font-weight:bold;">Teampass</div>
1294
        </td></tr></table></td>
1295
    </tr>
1296
    <tr><td align="center" valign="top" bgcolor="#f0f0f0" style="border-collapse: collapse; background-color: #f0f0f0;">
1297
        <table width="600" cellpadding="0" cellspacing="0" border="0" class="container" bgcolor="#ffffff" style="border-spacing: 0; border-bottom: 1px solid #e0e0e0; box-shadow: 0 0 3px #ddd; color: #434343; font-family: Helvetica, Verdana, sans-serif;">
1298
        <tr><td class="container-padding" bgcolor="#ffffff" style="border-collapse: collapse; border-left: 1px solid #e0e0e0; background-color: #ffffff; padding-left: 30px; padding-right: 30px;">
1299
        <br><div style="float:right;">' .
1300
        $textMail .
1301
        '<br><br></td></tr></table>
1302
    </td></tr></table>
1303
    <br></body></html>';
1304
}
1305
1306
/**
1307
 * Generate a Key.
1308
 * 
1309
 * @return string
1310
 */
1311
function generateKey(): string
1312
{
1313
    return substr(md5(rand() . rand()), 0, 15);
1314
}
1315
1316
/**
1317
 * Convert date to timestamp.
1318
 *
1319
 * @param string $date        The date
1320
 * @param string $date_format Date format
1321
 *
1322
 * @return int
1323
 */
1324
function dateToStamp(string $date, string $date_format): int
1325
{
1326
    $date = date_parse_from_format($date_format, $date);
1327
    if ((int) $date['warning_count'] === 0 && (int) $date['error_count'] === 0) {
1328
        return mktime(
1329
            empty($date['hour']) === false ? $date['hour'] : 23,
1330
            empty($date['minute']) === false ? $date['minute'] : 59,
1331
            empty($date['second']) === false ? $date['second'] : 59,
1332
            $date['month'],
1333
            $date['day'],
1334
            $date['year']
1335
        );
1336
    }
1337
    return 0;
1338
}
1339
1340
/**
1341
 * Is this a date.
1342
 *
1343
 * @param string $date Date
1344
 *
1345
 * @return bool
1346
 */
1347
function isDate(string $date): bool
1348
{
1349
    return strtotime($date) !== false;
1350
}
1351
1352
/**
1353
 * Check if isUTF8().
1354
 *
1355
 * @param string|array $string Is the string
1356
 *
1357
 * @return int is the string in UTF8 format
1358
 */
1359
function isUTF8($string): int
1360
{
1361
    if (is_array($string) === true) {
1362
        $string = $string['string'];
1363
    }
1364
1365
    return preg_match(
1366
        '%^(?:
1367
        [\x09\x0A\x0D\x20-\x7E] # ASCII
1368
        | [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
1369
        | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
1370
        | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
1371
        | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
1372
        | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
1373
        | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
1374
        | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
1375
        )*$%xs',
1376
        $string
1377
    );
1378
}
1379
1380
/**
1381
 * Prepare an array to UTF8 format before JSON_encode.
1382
 *
1383
 * @param array $array Array of values
1384
 *
1385
 * @return array
1386
 */
1387
function utf8Converter(array $array): array
1388
{
1389
    array_walk_recursive(
1390
        $array,
1391
        static function (&$item): void {
1392
            if (mb_detect_encoding((string) $item, 'utf-8', true) === false) {
1393
                $item = mb_convert_encoding($item, 'ISO-8859-1', 'UTF-8');
1394
            }
1395
        }
1396
    );
1397
    return $array;
1398
}
1399
1400
/**
1401
 * Permits to prepare data to be exchanged.
1402
 *
1403
 * @param array|string $data Text
1404
 * @param string       $type Parameter
1405
 * @param string       $key  Optional key
1406
 *
1407
 * @return string|array
1408
 */
1409
function prepareExchangedData($data, string $type, ?string $key = null)
1410
{
1411
    $session = SessionManager::getSession();
1412
    
1413
    // Perform
1414
    if ($type === 'encode' && is_array($data) === true) {
1415
        // Now encode
1416
        return Encryption::encrypt(
1417
            json_encode(
1418
                $data,
1419
                JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
1420
            ),
1421
            $session->get('key')
1422
        );
1423
    }
1424
    if ($type === 'decode' && is_array($data) === false) {
1425
        // check if key exists
1426
        return json_decode(
1427
            (string) Encryption::decrypt(
1428
                (string) $data,
1429
                $session->get('key')
1430
            ),
1431
            true
1432
        );
1433
    }
1434
    return '';
1435
}
1436
1437
1438
/**
1439
 * Create a thumbnail.
1440
 *
1441
 * @param string  $src           Source
1442
 * @param string  $dest          Destination
1443
 * @param int $desired_width Size of width
1444
 * 
1445
 * @return void|string|bool
1446
 */
1447
function makeThumbnail(string $src, string $dest, int $desired_width)
1448
{
1449
    /* read the source image */
1450
    if (is_file($src) === true && mime_content_type($src) === 'image/png') {
1451
        $source_image = imagecreatefrompng($src);
1452
        if ($source_image === false) {
1453
            return "Error: Not a valid PNG file! It's type is ".mime_content_type($src);
1454
        }
1455
    } else {
1456
        return "Error: Not a valid PNG file! It's type is ".mime_content_type($src);
1457
    }
1458
1459
    // Get height and width
1460
    $width = imagesx($source_image);
1461
    $height = imagesy($source_image);
1462
    /* find the "desired height" of this thumbnail, relative to the desired width  */
1463
    $desired_height = (int) floor($height * $desired_width / $width);
1464
    /* create a new, "virtual" image */
1465
    $virtual_image = imagecreatetruecolor($desired_width, $desired_height);
1466
    if ($virtual_image === false) {
1467
        return false;
1468
    }
1469
    /* copy source image at a resized size */
1470
    imagecopyresampled($virtual_image, $source_image, 0, 0, 0, 0, $desired_width, $desired_height, $width, $height);
1471
    /* create the physical thumbnail image to its destination */
1472
    imagejpeg($virtual_image, $dest);
1473
}
1474
1475
/**
1476
 * Check table prefix in SQL query.
1477
 *
1478
 * @param string $table Table name
1479
 * 
1480
 * @return string
1481
 */
1482
function prefixTable(string $table): string
1483
{
1484
    $safeTable = htmlspecialchars(DB_PREFIX . $table);
1485
    if (empty($safeTable) === false) {
1486
        // sanitize string
1487
        return $safeTable;
1488
    }
1489
    // stop error no table
1490
    return 'table_not_exists';
1491
}
1492
1493
/**
1494
 * GenerateCryptKey
1495
 *
1496
 * @param int     $size      Length
1497
 * @param bool $secure Secure
1498
 * @param bool $numerals Numerics
1499
 * @param bool $uppercase Uppercase letters
1500
 * @param bool $symbols Symbols
1501
 * @param bool $lowercase Lowercase
1502
 * @param array   $SETTINGS  SETTINGS
1503
 * 
1504
 * @return string
1505
 */
1506
function GenerateCryptKey(
1507
    int $size = 20,
1508
    bool $secure = false,
1509
    bool $numerals = false,
1510
    bool $uppercase = false,
1511
    bool $symbols = false,
1512
    bool $lowercase = false,
1513
    array $SETTINGS = []
1514
): string {
1515
    $generator = new ComputerPasswordGenerator();
1516
    $generator->setRandomGenerator(new Php7RandomGenerator());
1517
    
1518
    // Manage size
1519
    $generator->setLength((int) $size);
1520
    if ($secure === true) {
1521
        $generator->setSymbols(true);
1522
        $generator->setLowercase(true);
1523
        $generator->setUppercase(true);
1524
        $generator->setNumbers(true);
1525
    } else {
1526
        $generator->setLowercase($lowercase);
1527
        $generator->setUppercase($uppercase);
1528
        $generator->setNumbers($numerals);
1529
        $generator->setSymbols($symbols);
1530
    }
1531
1532
    return $generator->generatePasswords()[0];
1533
}
1534
1535
/**
1536
 * Send sysLOG message
1537
 *
1538
 * @param string    $message
1539
 * @param string    $host
1540
 * @param int       $port
1541
 * @param string    $component
1542
 * 
1543
 * @return void
1544
*/
1545
function send_syslog($message, $host, $port, $component = 'teampass'): void
1546
{
1547
    $sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
1548
    $syslog_message = '<123>' . date('M d H:i:s ') . $component . ': ' . $message;
1549
    socket_sendto($sock, (string) $syslog_message, strlen($syslog_message), 0, (string) $host, (int) $port);
1550
    socket_close($sock);
1551
}
1552
1553
/**
1554
 * Permits to log events into DB
1555
 *
1556
 * @param array  $SETTINGS Teampass settings
1557
 * @param string $type     Type
1558
 * @param string $label    Label
1559
 * @param string $who      Who
1560
 * @param string $login    Login
1561
 * @param string|int $field_1  Field
1562
 * 
1563
 * @return void
1564
 */
1565
function logEvents(
1566
    array $SETTINGS, 
1567
    string $type, 
1568
    string $label, 
1569
    string $who, 
1570
    ?string $login = null, 
1571
    $field_1 = null
1572
): void
1573
{
1574
    if (empty($who)) {
1575
        $who = getClientIpServer();
1576
    }
1577
1578
    // Load class DB
1579
    loadClasses('DB');
1580
1581
    DB::insert(
1582
        prefixTable('log_system'),
1583
        [
1584
            'type' => $type,
1585
            'date' => time(),
1586
            'label' => $label,
1587
            'qui' => $who,
1588
            'field_1' => $field_1 === null ? '' : $field_1,
1589
        ]
1590
    );
1591
    // If SYSLOG
1592
    if (isset($SETTINGS['syslog_enable']) === true && (int) $SETTINGS['syslog_enable'] === 1) {
1593
        if ($type === 'user_mngt') {
1594
            send_syslog(
1595
                'action=' . str_replace('at_', '', $label) . ' attribute=user user=' . $who . ' userid="' . $login . '" change="' . $field_1 . '" ',
1596
                $SETTINGS['syslog_host'],
1597
                $SETTINGS['syslog_port'],
1598
                'teampass'
1599
            );
1600
        } else {
1601
            send_syslog(
1602
                'action=' . $type . ' attribute=' . $label . ' user=' . $who . ' userid="' . $login . '" ',
1603
                $SETTINGS['syslog_host'],
1604
                $SETTINGS['syslog_port'],
1605
                'teampass'
1606
            );
1607
        }
1608
    }
1609
}
1610
1611
/**
1612
 * Log events.
1613
 *
1614
 * @param array  $SETTINGS        Teampass settings
1615
 * @param int    $item_id         Item id
1616
 * @param string $item_label      Item label
1617
 * @param int    $id_user         User id
1618
 * @param string $action          Code for reason
1619
 * @param string $login           User login
1620
 * @param string $raison          Code for reason
1621
 * @param string $encryption_type Encryption on
1622
 * @param string $time Encryption Time
1623
 * @param string $old_value       Old value
1624
 * 
1625
 * @return void
1626
 */
1627
function logItems(
1628
    array $SETTINGS,
1629
    int $item_id,
1630
    string $item_label,
1631
    int $id_user,
1632
    string $action,
1633
    ?string $login = null,
1634
    ?string $raison = null,
1635
    ?string $encryption_type = null,
1636
    ?string $time = null,
1637
    ?string $old_value = null
1638
): void {
1639
    // Load class DB
1640
    loadClasses('DB');
1641
1642
    // Insert log in DB
1643
    DB::insert(
1644
        prefixTable('log_items'),
1645
        [
1646
            'id_item' => $item_id,
1647
            'date' => is_null($time) === true ? time() : $time,
1648
            'id_user' => $id_user,
1649
            'action' => $action,
1650
            'raison' => $raison,
1651
            'old_value' => $old_value,
1652
            'encryption_type' => is_null($encryption_type) === true ? TP_ENCRYPTION_NAME : $encryption_type,
1653
        ]
1654
    );
1655
    // Timestamp the last change
1656
    if ($action === 'at_creation' || $action === 'at_modifiation' || $action === 'at_delete' || $action === 'at_import') {
1657
        DB::update(
1658
            prefixTable('misc'),
1659
            [
1660
                'valeur' => time(),
1661
            ],
1662
            'type = %s AND intitule = %s',
1663
            'timestamp',
1664
            'last_item_change'
1665
        );
1666
    }
1667
1668
    // SYSLOG
1669
    if (isset($SETTINGS['syslog_enable']) === true && $SETTINGS['syslog_enable'] === '1') {
1670
        // Extract reason
1671
        $attribute = is_null($raison) === true ? Array('') : explode(' : ', $raison);
1672
        // Get item info if not known
1673
        if (empty($item_label) === true) {
1674
            $dataItem = DB::queryfirstrow(
1675
                'SELECT id, id_tree, label
1676
                FROM ' . prefixTable('items') . '
1677
                WHERE id = %i',
1678
                $item_id
1679
            );
1680
            $item_label = $dataItem['label'];
1681
        }
1682
1683
        send_syslog(
1684
            'action=' . str_replace('at_', '', $action) .
1685
                ' attribute=' . str_replace('at_', '', $attribute[0]) .
1686
                ' itemno=' . $item_id .
1687
                ' user=' . is_null($login) === true ? '' : addslashes((string) $login) .
1688
                ' itemname="' . addslashes($item_label) . '"',
1689
            $SETTINGS['syslog_host'],
1690
            $SETTINGS['syslog_port'],
1691
            'teampass'
1692
        );
1693
    }
1694
1695
    // send notification if enabled
1696
    //notifyOnChange($item_id, $action, $SETTINGS);
1697
}
1698
1699
/**
1700
 * Prepare notification email to subscribers.
1701
 *
1702
 * @param int    $item_id  Item id
1703
 * @param string $label    Item label
1704
 * @param array  $changes  List of changes
1705
 * @param array  $SETTINGS Teampass settings
1706
 * 
1707
 * @return void
1708
 */
1709
function notifyChangesToSubscribers(int $item_id, string $label, array $changes, array $SETTINGS): void
1710
{
1711
    $session = SessionManager::getSession();
1712
    $lang = new Language(); 
1713
    $globalsUserId = $session->get('user-id');
1714
    $globalsLastname = $session->get('user-lastname');
1715
    $globalsName = $session->get('user-name');
1716
    // send email to user that what to be notified
1717
    $notification = DB::queryOneColumn(
1718
        'email',
1719
        'SELECT *
1720
        FROM ' . prefixTable('notification') . ' AS n
1721
        INNER JOIN ' . prefixTable('users') . ' AS u ON (n.user_id = u.id)
1722
        WHERE n.item_id = %i AND n.user_id != %i',
1723
        $item_id,
1724
        $globalsUserId
1725
    );
1726
    if (DB::count() > 0) {
1727
        // Prepare path
1728
        $path = geItemReadablePath($item_id, '', $SETTINGS);
1729
        // Get list of changes
1730
        $htmlChanges = '<ul>';
1731
        foreach ($changes as $change) {
1732
            $htmlChanges .= '<li>' . $change . '</li>';
1733
        }
1734
        $htmlChanges .= '</ul>';
1735
        // send email
1736
        DB::insert(
1737
            prefixTable('emails'),
1738
            [
1739
                'timestamp' => time(),
1740
                'subject' => $lang->get('email_subject_item_updated'),
1741
                'body' => str_replace(
1742
                    ['#item_label#', '#folder_name#', '#item_id#', '#url#', '#name#', '#lastname#', '#changes#'],
1743
                    [$label, $path, $item_id, $SETTINGS['cpassman_url'], $globalsName, $globalsLastname, $htmlChanges],
1744
                    $lang->get('email_body_item_updated')
1745
                ),
1746
                'receivers' => implode(',', $notification),
1747
                'status' => '',
1748
            ]
1749
        );
1750
    }
1751
}
1752
1753
/**
1754
 * Returns the Item + path.
1755
 *
1756
 * @param int    $id_tree  Node id
1757
 * @param string $label    Label
1758
 * @param array  $SETTINGS TP settings
1759
 * 
1760
 * @return string
1761
 */
1762
function geItemReadablePath(int $id_tree, string $label, array $SETTINGS): string
1763
{
1764
    $tree = new NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
1765
    $arbo = $tree->getPath($id_tree, true);
1766
    $path = '';
1767
    foreach ($arbo as $elem) {
1768
        if (empty($path) === true) {
1769
            $path = htmlspecialchars(stripslashes(htmlspecialchars_decode($elem->title, ENT_QUOTES)), ENT_QUOTES) . ' ';
1770
        } else {
1771
            $path .= '&#8594; ' . htmlspecialchars(stripslashes(htmlspecialchars_decode($elem->title, ENT_QUOTES)), ENT_QUOTES);
1772
        }
1773
    }
1774
1775
    // Build text to show user
1776
    if (empty($label) === false) {
1777
        return empty($path) === true ? addslashes($label) : addslashes($label) . ' (' . $path . ')';
1778
    }
1779
    return empty($path) === true ? '' : $path;
1780
}
1781
1782
/**
1783
 * Get the client ip address.
1784
 *
1785
 * @return string IP address
1786
 */
1787
function getClientIpServer(): string
1788
{
1789
    if (getenv('HTTP_CLIENT_IP')) {
1790
        $ipaddress = getenv('HTTP_CLIENT_IP');
1791
    } elseif (getenv('HTTP_X_FORWARDED_FOR')) {
1792
        $ipaddress = getenv('HTTP_X_FORWARDED_FOR');
1793
    } elseif (getenv('HTTP_X_FORWARDED')) {
1794
        $ipaddress = getenv('HTTP_X_FORWARDED');
1795
    } elseif (getenv('HTTP_FORWARDED_FOR')) {
1796
        $ipaddress = getenv('HTTP_FORWARDED_FOR');
1797
    } elseif (getenv('HTTP_FORWARDED')) {
1798
        $ipaddress = getenv('HTTP_FORWARDED');
1799
    } elseif (getenv('REMOTE_ADDR')) {
1800
        $ipaddress = getenv('REMOTE_ADDR');
1801
    } else {
1802
        $ipaddress = 'UNKNOWN';
1803
    }
1804
1805
    return $ipaddress;
1806
}
1807
1808
/**
1809
 * Escape all HTML, JavaScript, and CSS.
1810
 *
1811
 * @param string $input    The input string
1812
 * @param string $encoding Which character encoding are we using?
1813
 * 
1814
 * @return string
1815
 */
1816
function noHTML(string $input, string $encoding = 'UTF-8'): string
1817
{
1818
    return htmlspecialchars($input, ENT_QUOTES | ENT_XHTML, $encoding, false);
1819
}
1820
1821
/**
1822
 * Permits to handle the Teampass config file
1823
 * $action accepts "rebuild" and "update"
1824
 *
1825
 * @param string $action   Action to perform
1826
 * @param array  $SETTINGS Teampass settings
1827
 * @param string $field    Field to refresh
1828
 * @param string $value    Value to set
1829
 *
1830
 * @return string|bool
1831
 */
1832
function handleConfigFile($action, $SETTINGS, $field = null, $value = null)
1833
{
1834
    $tp_config_file = $SETTINGS['cpassman_dir'] . '/includes/config/tp.config.php';
1835
1836
    // Load class DB
1837
    loadClasses('DB');
1838
1839
    if (file_exists($tp_config_file) === false || $action === 'rebuild') {
1840
        // perform a copy
1841
        if (file_exists($tp_config_file)) {
1842
            if (! copy($tp_config_file, $tp_config_file . '.' . date('Y_m_d_His', time()))) {
1843
                return "ERROR: Could not copy file '" . $tp_config_file . "'";
1844
            }
1845
        }
1846
1847
        // regenerate
1848
        $data = [];
1849
        $data[0] = "<?php\n";
1850
        $data[1] = "global \$SETTINGS;\n";
1851
        $data[2] = "\$SETTINGS = array (\n";
1852
        $rows = DB::query(
1853
            'SELECT * FROM ' . prefixTable('misc') . ' WHERE type=%s',
1854
            'admin'
1855
        );
1856
        foreach ($rows as $record) {
1857
            array_push($data, "    '" . $record['intitule'] . "' => '" . htmlspecialchars_decode($record['valeur'], ENT_COMPAT) . "',\n");
1858
        }
1859
        array_push($data, ");\n");
1860
        $data = array_unique($data);
1861
    // ---
1862
    } elseif ($action === 'update' && empty($field) === false) {
1863
        $data = file($tp_config_file);
1864
        $inc = 0;
1865
        $bFound = false;
1866
        foreach ($data as $line) {
1867
            if (stristr($line, ');')) {
1868
                break;
1869
            }
1870
1871
            if (stristr($line, "'" . $field . "' => '")) {
1872
                $data[$inc] = "    '" . $field . "' => '" . htmlspecialchars_decode($value ?? '', ENT_COMPAT) . "',\n";
1873
                $bFound = true;
1874
                break;
1875
            }
1876
            ++$inc;
1877
        }
1878
        if ($bFound === false) {
1879
            $data[$inc] = "    '" . $field . "' => '" . htmlspecialchars_decode($value ?? '', ENT_COMPAT). "',\n);\n";
1880
        }
1881
    }
1882
1883
    // update file
1884
    file_put_contents($tp_config_file, implode('', $data ?? []));
1885
    return true;
1886
}
1887
1888
/**
1889
 * Permits to replace &#92; to permit correct display
1890
 *
1891
 * @param string $input Some text
1892
 * 
1893
 * @return string
1894
 */
1895
function handleBackslash(string $input): string
1896
{
1897
    return str_replace('&amp;#92;', '&#92;', $input);
1898
}
1899
1900
/**
1901
 * Permits to load settings
1902
 * 
1903
 * @return void
1904
*/
1905
function loadSettings(): void
1906
{
1907
    global $SETTINGS;
1908
    /* LOAD CPASSMAN SETTINGS */
1909
    if (! isset($SETTINGS['loaded']) || $SETTINGS['loaded'] !== 1) {
1910
        $SETTINGS = [];
1911
        $SETTINGS['duplicate_folder'] = 0;
1912
        //by default, this is set to 0;
1913
        $SETTINGS['duplicate_item'] = 0;
1914
        //by default, this is set to 0;
1915
        $SETTINGS['number_of_used_pw'] = 5;
1916
        //by default, this value is set to 5;
1917
        $settings = [];
1918
        $rows = DB::query(
1919
            'SELECT * FROM ' . prefixTable('misc') . ' WHERE type=%s_type OR type=%s_type2',
1920
            [
1921
                'type' => 'admin',
1922
                'type2' => 'settings',
1923
            ]
1924
        );
1925
        foreach ($rows as $record) {
1926
            if ($record['type'] === 'admin') {
1927
                $SETTINGS[$record['intitule']] = $record['valeur'];
1928
            } else {
1929
                $settings[$record['intitule']] = $record['valeur'];
1930
            }
1931
        }
1932
        $SETTINGS['loaded'] = 1;
1933
        $SETTINGS['default_session_expiration_time'] = 5;
1934
    }
1935
}
1936
1937
/**
1938
 * check if folder has custom fields.
1939
 * Ensure that target one also has same custom fields
1940
 * 
1941
 * @param int $source_id
1942
 * @param int $target_id 
1943
 * 
1944
 * @return bool
1945
*/
1946
function checkCFconsistency(int $source_id, int $target_id): bool
1947
{
1948
    $source_cf = [];
1949
    $rows = DB::QUERY(
1950
        'SELECT id_category
1951
            FROM ' . prefixTable('categories_folders') . '
1952
            WHERE id_folder = %i',
1953
        $source_id
1954
    );
1955
    foreach ($rows as $record) {
1956
        array_push($source_cf, $record['id_category']);
1957
    }
1958
1959
    $target_cf = [];
1960
    $rows = DB::QUERY(
1961
        'SELECT id_category
1962
            FROM ' . prefixTable('categories_folders') . '
1963
            WHERE id_folder = %i',
1964
        $target_id
1965
    );
1966
    foreach ($rows as $record) {
1967
        array_push($target_cf, $record['id_category']);
1968
    }
1969
1970
    $cf_diff = array_diff($source_cf, $target_cf);
1971
    if (count($cf_diff) > 0) {
1972
        return false;
1973
    }
1974
1975
    return true;
1976
}
1977
1978
/**
1979
 * Will encrypte/decrypt a fil eusing Defuse.
1980
 *
1981
 * @param string $type        can be either encrypt or decrypt
1982
 * @param string $source_file path to source file
1983
 * @param string $target_file path to target file
1984
 * @param array  $SETTINGS    Settings
1985
 * @param string $password    A password
1986
 *
1987
 * @return string|bool
1988
 */
1989
function prepareFileWithDefuse(
1990
    string $type,
1991
    string $source_file,
1992
    string $target_file,
1993
    array $SETTINGS,
1994
    string $password = null
1995
) {
1996
    // Load AntiXSS
1997
    $antiXss = new AntiXSS();
1998
    // Protect against bad inputs
1999
    if (is_array($source_file) === true || is_array($target_file) === true) {
2000
        return 'error_cannot_be_array';
2001
    }
2002
2003
    // Sanitize
2004
    $source_file = $antiXss->xss_clean($source_file);
2005
    $target_file = $antiXss->xss_clean($target_file);
2006
    if (empty($password) === true || is_null($password) === true) {
2007
        // get KEY to define password
2008
        $ascii_key = file_get_contents(SECUREPATH.'/'.SECUREFILE);
2009
        $password = Key::loadFromAsciiSafeString($ascii_key);
2010
    }
2011
2012
    $err = '';
2013
    if ($type === 'decrypt') {
2014
        // Decrypt file
2015
        $err = defuseFileDecrypt(
2016
            $source_file,
2017
            $target_file,
2018
            $SETTINGS, /** @scrutinizer ignore-type */
2019
            $password
2020
        );
2021
    } elseif ($type === 'encrypt') {
2022
        // Encrypt file
2023
        $err = defuseFileEncrypt(
2024
            $source_file,
2025
            $target_file,
2026
            $SETTINGS, /** @scrutinizer ignore-type */
2027
            $password
2028
        );
2029
    }
2030
2031
    // return error
2032
    return $err === true ? '' : $err;
2033
}
2034
2035
/**
2036
 * Encrypt a file with Defuse.
2037
 *
2038
 * @param string $source_file path to source file
2039
 * @param string $target_file path to target file
2040
 * @param array  $SETTINGS    Settings
2041
 * @param string $password    A password
2042
 *
2043
 * @return string|bool
2044
 */
2045
function defuseFileEncrypt(
2046
    string $source_file,
2047
    string $target_file,
2048
    array $SETTINGS,
2049
    string $password = null
2050
) {
2051
    try {
2052
        CryptoFile::encryptFileWithPassword(
2053
            $source_file,
2054
            $target_file,
2055
            $password
2056
        );
2057
    } catch (CryptoException\WrongKeyOrModifiedCiphertextException $ex) {
2058
        $err = 'wrong_key';
2059
    } catch (CryptoException\EnvironmentIsBrokenException $ex) {
2060
        $err = $ex;
2061
    } catch (CryptoException\IOException $ex) {
2062
        $err = $ex;
2063
    }
2064
2065
    // return error
2066
    return empty($err) === false ? $err : true;
2067
}
2068
2069
/**
2070
 * Decrypt a file with Defuse.
2071
 *
2072
 * @param string $source_file path to source file
2073
 * @param string $target_file path to target file
2074
 * @param array  $SETTINGS    Settings
2075
 * @param string $password    A password
2076
 *
2077
 * @return string|bool
2078
 */
2079
function defuseFileDecrypt(
2080
    string $source_file,
2081
    string $target_file,
2082
    array $SETTINGS,
2083
    string $password = null
2084
) {
2085
    try {
2086
        CryptoFile::decryptFileWithPassword(
2087
            $source_file,
2088
            $target_file,
2089
            $password
2090
        );
2091
    } catch (CryptoException\WrongKeyOrModifiedCiphertextException $ex) {
2092
        $err = 'wrong_key';
2093
    } catch (CryptoException\EnvironmentIsBrokenException $ex) {
2094
        $err = $ex;
2095
    } catch (CryptoException\IOException $ex) {
2096
        $err = $ex;
2097
    }
2098
2099
    // return error
2100
    return empty($err) === false ? $err : true;
2101
}
2102
2103
/*
2104
* NOT TO BE USED
2105
*/
2106
/**
2107
 * Undocumented function.
2108
 *
2109
 * @param string $text Text to debug
2110
 */
2111
function debugTeampass(string $text): void
2112
{
2113
    $debugFile = fopen('D:/wamp64/www/TeamPass/debug.txt', 'r+');
2114
    if ($debugFile !== false) {
2115
        fputs($debugFile, $text);
2116
        fclose($debugFile);
2117
    }
2118
}
2119
2120
/**
2121
 * DELETE the file with expected command depending on server type.
2122
 *
2123
 * @param string $file     Path to file
2124
 * @param array  $SETTINGS Teampass settings
2125
 *
2126
 * @return void
2127
 */
2128
function fileDelete(string $file, array $SETTINGS): void
2129
{
2130
    // Load AntiXSS
2131
    $antiXss = new AntiXSS();
2132
    $file = $antiXss->xss_clean($file);
2133
    if (is_file($file)) {
2134
        unlink($file);
2135
    }
2136
}
2137
2138
/**
2139
 * Permits to extract the file extension.
2140
 *
2141
 * @param string $file File name
2142
 *
2143
 * @return string
2144
 */
2145
function getFileExtension(string $file): string
2146
{
2147
    if (strpos($file, '.') === false) {
2148
        return $file;
2149
    }
2150
2151
    return substr($file, strrpos($file, '.') + 1);
2152
}
2153
2154
/**
2155
 * Chmods files and folders with different permissions.
2156
 *
2157
 * This is an all-PHP alternative to using: \n
2158
 * <tt>exec("find ".$path." -type f -exec chmod 644 {} \;");</tt> \n
2159
 * <tt>exec("find ".$path." -type d -exec chmod 755 {} \;");</tt>
2160
 *
2161
 * @author Jeppe Toustrup (tenzer at tenzer dot dk)
2162
  *
2163
 * @param string $path      An either relative or absolute path to a file or directory which should be processed.
2164
 * @param int    $filePerm The permissions any found files should get.
2165
 * @param int    $dirPerm  The permissions any found folder should get.
2166
 *
2167
 * @return bool Returns TRUE if the path if found and FALSE if not.
2168
 *
2169
 * @warning The permission levels has to be entered in octal format, which
2170
 * normally means adding a zero ("0") in front of the permission level. \n
2171
 * More info at: http://php.net/chmod.
2172
*/
2173
2174
function recursiveChmod(
2175
    string $path,
2176
    int $filePerm = 0644,
2177
    int  $dirPerm = 0755
2178
) {
2179
    // Check if the path exists
2180
    if (! file_exists($path)) {
2181
        return false;
2182
    }
2183
2184
    // See whether this is a file
2185
    if (is_file($path)) {
2186
        // Chmod the file with our given filepermissions
2187
        try {
2188
            chmod($path, $filePerm);
2189
        } catch (Exception $e) {
2190
            return false;
2191
        }
2192
    // If this is a directory...
2193
    } elseif (is_dir($path)) {
2194
        // Then get an array of the contents
2195
        $foldersAndFiles = scandir($path);
2196
        // Remove "." and ".." from the list
2197
        $entries = array_slice($foldersAndFiles, 2);
2198
        // Parse every result...
2199
        foreach ($entries as $entry) {
2200
            // And call this function again recursively, with the same permissions
2201
            recursiveChmod($path.'/'.$entry, $filePerm, $dirPerm);
2202
        }
2203
2204
        // When we are done with the contents of the directory, we chmod the directory itself
2205
        try {
2206
            chmod($path, $filePerm);
2207
        } catch (Exception $e) {
2208
            return false;
2209
        }
2210
    }
2211
2212
    // Everything seemed to work out well, return true
2213
    return true;
2214
}
2215
2216
/**
2217
 * Check if user can access to this item.
2218
 *
2219
 * @param int   $item_id ID of item
2220
 * @param array $SETTINGS
2221
 *
2222
 * @return bool|string
2223
 */
2224
function accessToItemIsGranted(int $item_id, array $SETTINGS)
2225
{
2226
    
2227
    $session = SessionManager::getSession();
2228
    $session_groupes_visibles = $session->get('user-accessible_folders');
2229
    $session_list_restricted_folders_for_items = $session->get('system-list_restricted_folders_for_items');
2230
    // Load item data
2231
    $data = DB::queryFirstRow(
2232
        'SELECT id_tree
2233
        FROM ' . prefixTable('items') . '
2234
        WHERE id = %i',
2235
        $item_id
2236
    );
2237
    // Check if user can access this folder
2238
    if (in_array($data['id_tree'], $session_groupes_visibles) === false) {
2239
        // Now check if this folder is restricted to user
2240
        if (isset($session_list_restricted_folders_for_items[$data['id_tree']]) === true
2241
            && in_array($item_id, $session_list_restricted_folders_for_items[$data['id_tree']]) === false
2242
        ) {
2243
            return 'ERR_FOLDER_NOT_ALLOWED';
2244
        }
2245
    }
2246
2247
    return true;
2248
}
2249
2250
/**
2251
 * Creates a unique key.
2252
 *
2253
 * @param int $lenght Key lenght
2254
 *
2255
 * @return string
2256
 */
2257
function uniqidReal(int $lenght = 13): string
2258
{
2259
    if (function_exists('random_bytes')) {
2260
        $bytes = random_bytes(intval(ceil($lenght / 2)));
2261
    } elseif (function_exists('openssl_random_pseudo_bytes')) {
2262
        $bytes = openssl_random_pseudo_bytes(intval(ceil($lenght / 2)));
2263
    } else {
2264
        throw new Exception('no cryptographically secure random function available');
2265
    }
2266
2267
    return substr(bin2hex($bytes), 0, $lenght);
2268
}
2269
2270
/**
2271
 * Obfuscate an email.
2272
 *
2273
 * @param string $email Email address
2274
 *
2275
 * @return string
2276
 */
2277
function obfuscateEmail(string $email): string
2278
{
2279
    $email = explode("@", $email);
2280
    $name = $email[0];
2281
    if (strlen($name) > 3) {
2282
        $name = substr($name, 0, 2);
2283
        for ($i = 0; $i < strlen($email[0]) - 3; $i++) {
2284
            $name .= "*";
2285
        }
2286
        $name .= substr($email[0], -1, 1);
2287
    }
2288
    $host = explode(".", $email[1])[0];
2289
    if (strlen($host) > 3) {
2290
        $host = substr($host, 0, 1);
2291
        for ($i = 0; $i < strlen(explode(".", $email[1])[0]) - 2; $i++) {
2292
            $host .= "*";
2293
        }
2294
        $host .= substr(explode(".", $email[1])[0], -1, 1);
2295
    }
2296
    $email = $name . "@" . $host . "." . explode(".", $email[1])[1];
2297
    return $email;
2298
}
2299
2300
/**
2301
 * Perform a Query.
2302
 *
2303
 * @param array  $SETTINGS Teamapss settings
2304
 * @param string $fields   Fields to use
2305
 * @param string $table    Table to use
2306
 *
2307
 * @return array
2308
 */
2309
function performDBQuery(array $SETTINGS, string $fields, string $table): array
2310
{
2311
    // include librairies & connect to DB
2312
    //include_once $SETTINGS['cpassman_dir'] . '/includes/config/settings.php';
2313
2314
    // Load class DB
2315
    loadClasses('DB');
2316
    
2317
    // Insert log in DB
2318
    return DB::query(
2319
        'SELECT ' . $fields . '
2320
        FROM ' . prefixTable($table)
2321
    );
2322
}
2323
2324
/**
2325
 * Undocumented function.
2326
 *
2327
 * @param int $bytes Size of file
2328
 *
2329
 * @return string
2330
 */
2331
function formatSizeUnits(int $bytes): string
2332
{
2333
    if ($bytes >= 1073741824) {
2334
        $bytes = number_format($bytes / 1073741824, 2) . ' GB';
2335
    } elseif ($bytes >= 1048576) {
2336
        $bytes = number_format($bytes / 1048576, 2) . ' MB';
2337
    } elseif ($bytes >= 1024) {
2338
        $bytes = number_format($bytes / 1024, 2) . ' KB';
2339
    } elseif ($bytes > 1) {
2340
        $bytes .= ' bytes';
2341
    } elseif ($bytes === 1) {
2342
        $bytes .= ' byte';
2343
    } else {
2344
        $bytes = '0 bytes';
2345
    }
2346
2347
    return $bytes;
2348
}
2349
2350
/**
2351
 * Generate user pair of keys.
2352
 *
2353
 * @param string $userPwd User password
2354
 *
2355
 * @return array
2356
 */
2357
function generateUserKeys(string $userPwd): array
2358
{
2359
    //if (WIP === false) {
2360
        // Load classes
2361
        $rsa = new Crypt_RSA();
2362
        $cipher = new Crypt_AES();
2363
        // Create the private and public key
2364
        $res = $rsa->createKey(4096);
2365
        // Encrypt the privatekey
2366
        $cipher->setPassword($userPwd);
2367
        $privatekey = $cipher->encrypt($res['privatekey']);
2368
        return [
2369
            'private_key' => base64_encode($privatekey),
2370
            'public_key' => base64_encode($res['publickey']),
2371
            'private_key_clear' => base64_encode($res['privatekey']),
2372
        ];
2373
    /*} else {
2374
        // Create the keys
2375
        $keys = RSA::createKey();
2376
2377
        return [
2378
            'private_key' => base64_encode($keys->withPassword($userPwd)->toString('PKCS8')),
2379
            'public_key' => base64_encode($keys->getPublicKey()),
2380
            'private_key_clear' => base64_encode($keys->toString('PKCS8')),
2381
        ];
2382
    }*/
2383
}
2384
2385
/**
2386
 * Permits to decrypt the user's privatekey.
2387
 *
2388
 * @param string $userPwd        User password
2389
 * @param string $userPrivateKey User private key
2390
 *
2391
 * @return string|object
2392
 */
2393
function decryptPrivateKey(string $userPwd, string $userPrivateKey)
2394
{
2395
    if (empty($userPwd) === false) {
2396
        //if (WIP === false) {
2397
            // Load classes
2398
            $cipher = new Crypt_AES();
2399
            // Encrypt the privatekey
2400
            $cipher->setPassword($userPwd);
2401
            try {
2402
                return base64_encode((string) $cipher->decrypt(base64_decode($userPrivateKey)));
2403
            } catch (Exception $e) {
2404
                return $e;
2405
            }
2406
        /*} else {
2407
            //echo $userPrivateKey." ;; ".($userPwd)." ;;";
2408
            // Load and decrypt the private key
2409
            try {
2410
                $privateKey = PublicKeyLoader::loadPrivateKey(base64_decode($userPrivateKey), $userPwd)->withHash('sha1')->withMGFHash('sha1');
2411
                print_r($privateKey);
2412
                return base64_encode((string) $$privateKey);
2413
            } catch (NoKeyLoadedException $e) {
2414
                print_r($e);
2415
                return $e;
2416
            }
2417
        }*/
2418
    }
2419
    return '';
2420
}
2421
2422
/**
2423
 * Permits to encrypt the user's privatekey.
2424
 *
2425
 * @param string $userPwd        User password
2426
 * @param string $userPrivateKey User private key
2427
 *
2428
 * @return string
2429
 */
2430
function encryptPrivateKey(string $userPwd, string $userPrivateKey): string
2431
{
2432
    if (empty($userPwd) === false) {
2433
        //if (WIP === false) {
2434
            // Load classes
2435
            $cipher = new Crypt_AES();
2436
            // Encrypt the privatekey
2437
            $cipher->setPassword($userPwd);        
2438
            try {
2439
                return base64_encode($cipher->encrypt(base64_decode($userPrivateKey)));
2440
            } catch (Exception $e) {
2441
                return $e;
2442
            }
2443
        /*} else {
2444
            // Load the private key
2445
            $privateKey = PublicKeyLoader::load(base64_decode($userPrivateKey));
2446
2447
            try {
2448
                return base64_encode($privateKey->withPassword($userPwd));
2449
            } catch (Exception $e) {
2450
                return $e;
2451
            }
2452
        }*/
2453
    }
2454
    return '';
2455
}
2456
2457
/**
2458
 * Encrypts a string using AES.
2459
 *
2460
 * @param string $data String to encrypt
2461
 * @param string $key
2462
 *
2463
 * @return array
2464
 */
2465
function doDataEncryption(string $data, string $key = NULL): array
2466
{
2467
    //if (WIP === false) {
2468
        // Load classes
2469
        $cipher = new Crypt_AES(CRYPT_AES_MODE_CBC);
2470
        // Generate an object key
2471
        $objectKey = is_null($key) === true ? uniqidReal(KEY_LENGTH) : $key;
2472
        // Set it as password
2473
        $cipher->setPassword($objectKey);
2474
        return [
2475
            'encrypted' => base64_encode($cipher->encrypt($data)),
2476
            'objectKey' => base64_encode($objectKey),
2477
        ];
2478
    /*} else {
2479
2480
    }*/
2481
}
2482
2483
/**
2484
 * Decrypts a string using AES.
2485
 *
2486
 * @param string $data Encrypted data
2487
 * @param string $key  Key to uncrypt
2488
 *
2489
 * @return string
2490
 */
2491
function doDataDecryption(string $data, string $key): string
2492
{
2493
    //if (WIP === false) {
2494
        // Load classes
2495
        $cipher = new Crypt_AES();
2496
        // Set the object key
2497
        $cipher->setPassword(base64_decode($key));
2498
        return base64_encode((string) $cipher->decrypt(base64_decode($data)));
2499
    /*} else {
2500
2501
    }*/
2502
}
2503
2504
/**
2505
 * Encrypts using RSA a string using a public key.
2506
 *
2507
 * @param string $key       Key to be encrypted
2508
 * @param string $publicKey User public key
2509
 *
2510
 * @return string
2511
 */
2512
function encryptUserObjectKey(string $key, string $publicKey): string
2513
{
2514
    //if (WIP === false) {
2515
        // Load classes
2516
        $rsa = new Crypt_RSA();
2517
        $rsa->loadKey(base64_decode($publicKey));
0 ignored issues
show
Security Variable Injection introduced by
base64_decode($publicKey) can contain request data and is used in variable name context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Crypt_RSA::loadKey() is called
    in sources/main.functions.php on line 2517
  2. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1816
  3. Data is passed through _parseKey(), and $this->_parseKey($key, $type) is assigned to $components
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1875
  4. $components['comment'] is assigned to property Crypt_RSA::$comment
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1898
  5. Read from property Crypt_RSA::$comment, and 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . $this->comment is assigned to $RSAPublicKey
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1096
  6. $RSAPublicKey is returned
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1098
  7. array('privatekey' => $this->_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients), 'publickey' => $this->_convertPublicKey($n, $e), 'partialkey' => false) is returned
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 760
  8. $rsa->createKey(4096) is assigned to $res
    in sources/main.functions.php on line 2364
  9. Data is passed through base64_encode()
    in sources/main.functions.php on line 2370
  10. array('private_key' => base64_encode($privatekey), 'public_key' => base64_encode($res['publickey']), 'private_key_clear' => base64_encode($res['privatekey'])) is returned
    in sources/main.functions.php on line 2368
  11. generateUserKeys($passwordClear) is assigned to $userKeys
    in sources/identify.php on line 1506
  12. encryptUserObjectKey() is called
    in sources/identify.php on line 1547
  13. Enters via parameter $publicKey
    in sources/main.functions.php on line 2512
  14. Data is passed through base64_decode()
    in sources/main.functions.php on line 2517

Used in variable context

  1. Crypt_RSA::loadKey() is called
    in sources/main.functions.php on line 2517
  2. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1816
  3. Crypt_RSA::_parseKey() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1875
  4. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1154
  5. Data is passed through _extractBER()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1610
  6. $this->_extractBER($key) is assigned to $decoded
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1610
  7. Data is passed through _string_shift()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1670
  8. $this->_string_shift($decoded, $length) is assigned to $paddedKey
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1670
  9. Data is passed through _string_shift()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1695
  10. Data is passed through unpack()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1695
  11. extract() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1695

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
Security Variable Injection introduced by
base64_decode($publicKey) can contain request data and is used in variable name context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Crypt_RSA::loadKey() is called
    in sources/main.functions.php on line 2517
  2. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1816
  3. Data is passed through _parseKey(), and $this->_parseKey($key, $type) is assigned to $components
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1875
  4. $components['comment'] is assigned to property Crypt_RSA::$comment
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1898
  5. Read from property Crypt_RSA::$comment, and 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . $this->comment is assigned to $RSAPublicKey
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1096
  6. $RSAPublicKey is returned
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1098
  7. array('privatekey' => $this->_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients), 'publickey' => $this->_convertPublicKey($n, $e), 'partialkey' => false) is returned
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 760
  8. $rsa->createKey(4096) is assigned to $res
    in sources/main.functions.php on line 2364
  9. Data is passed through base64_encode()
    in sources/main.functions.php on line 2370
  10. array('private_key' => base64_encode($privatekey), 'public_key' => base64_encode($res['publickey']), 'private_key_clear' => base64_encode($res['privatekey'])) is returned
    in sources/main.functions.php on line 2368
  11. generateUserKeys($passwordClear) is assigned to $userKeys
    in sources/identify.php on line 1506
  12. encryptUserObjectKey() is called
    in sources/identify.php on line 1547
  13. Enters via parameter $publicKey
    in sources/main.functions.php on line 2512
  14. Data is passed through base64_decode()
    in sources/main.functions.php on line 2517

Used in variable context

  1. Crypt_RSA::loadKey() is called
    in sources/main.functions.php on line 2517
  2. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1816
  3. Crypt_RSA::_parseKey() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1875
  4. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1154
  5. Data is passed through preg_split()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1498
  6. preg_split('#\r\n|\r|\n#', $key) is assigned to $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1498
  7. Data is passed through array_slice()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1514
  8. Data is passed through array_map()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1514
  9. Data is passed through implode()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1514
  10. Data is passed through base64_decode()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1514
  11. base64_decode(implode('', array_map('trim', array_slice($key, 4, $publicLength)))) is assigned to $public
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1514
  12. Data is passed through substr()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1515
  13. substr($public, 11) is assigned to $public
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1515
  14. Data is passed through _string_shift()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1516
  15. Data is passed through unpack()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1516
  16. extract() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1516

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
Security Variable Injection introduced by
base64_decode($publicKey) can contain request data and is used in variable name context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Crypt_RSA::loadKey() is called
    in sources/main.functions.php on line 2517
  2. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1816
  3. Data is passed through _parseKey(), and $this->_parseKey($key, $type) is assigned to $components
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1875
  4. $components['comment'] is assigned to property Crypt_RSA::$comment
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1898
  5. Read from property Crypt_RSA::$comment, and 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . $this->comment is assigned to $RSAPublicKey
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1096
  6. $RSAPublicKey is returned
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1098
  7. array('privatekey' => $this->_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients), 'publickey' => $this->_convertPublicKey($n, $e), 'partialkey' => false) is returned
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 760
  8. $rsa->createKey(4096) is assigned to $res
    in sources/main.functions.php on line 2364
  9. Data is passed through base64_encode()
    in sources/main.functions.php on line 2370
  10. array('private_key' => base64_encode($privatekey), 'public_key' => base64_encode($res['publickey']), 'private_key_clear' => base64_encode($res['privatekey'])) is returned
    in sources/main.functions.php on line 2368
  11. generateUserKeys($passwordClear) is assigned to $userKeys
    in sources/identify.php on line 1506
  12. encryptUserObjectKey() is called
    in sources/identify.php on line 1547
  13. Enters via parameter $publicKey
    in sources/main.functions.php on line 2512
  14. Data is passed through base64_decode()
    in sources/main.functions.php on line 2517

Used in variable context

  1. Crypt_RSA::loadKey() is called
    in sources/main.functions.php on line 2517
  2. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1816
  3. Crypt_RSA::_parseKey() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1875
  4. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1154
  5. Data is passed through explode()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1430
  6. explode(' ', $key, 3) is assigned to $parts
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1430
  7. Data is passed through base64_decode()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1432
  8. IssetNode ? base64_decode($parts[1]) : false is assigned to $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1432
  9. Data is passed through _string_shift()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1456
  10. Data is passed through unpack()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1456
  11. extract() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1456

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
Security Variable Injection introduced by
base64_decode($publicKey) can contain request data and is used in variable name context(s) leading to a potential security vulnerability.

2 paths for user data to reach this point

  1. Path: ParameterBag::get() returns request data in vendor/symfony/http-foundation/ParameterBag.php on line 82
  1. ParameterBag::get() returns request data
    in vendor/symfony/http-foundation/ParameterBag.php on line 82
  2. Data is passed through identifyGetUserCredentials(), and identifyGetUserCredentials($SETTINGS, (string)$server['PHP_AUTH_USER'], (string)$server['PHP_AUTH_PW'], (string)filter_var($dataReceived['pw'], FILTER_SANITIZE_FULL_SPECIAL_CHARS), (string)filter_var($dataReceived['login'], FILTER_SANITIZE_FULL_SPECIAL_CHARS)) is assigned to $userCredentials
    in sources/identify.php on line 293
  3. $userCredentials['passwordClear'] is assigned to $passwordClear
    in sources/identify.php on line 301
  4. prepareUserEncryptionKeys() is called
    in sources/identify.php on line 499
  5. Enters via parameter $passwordClear
    in sources/identify.php on line 1042
  6. generateUserKeys() is called
    in sources/identify.php on line 1047
  7. Enters via parameter $userPwd
    in sources/main.functions.php on line 2357
  8. Crypt_Base::setPassword() is called
    in sources/main.functions.php on line 2366
  9. Enters via parameter $password
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 685
  10. Data is passed through hash_pbkdf2(), and hash_pbkdf2($hash, $password, $salt, $count, $dkLen, true) is assigned to $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 779
  11. Crypt_Base::setKey() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 783
  12. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 653
  13. $key is assigned to property Crypt_AES::$key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 660
  14. Read from property Crypt_AES::$key, and Data is passed through openssl_encrypt(), and openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options) is returned
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 821
  15. $cipher->encrypt($res['privatekey']) is assigned to $privatekey
    in sources/main.functions.php on line 2367
  16. Data is passed through base64_encode()
    in sources/main.functions.php on line 2369
  17. array('private_key' => base64_encode($privatekey), 'public_key' => base64_encode($res['publickey']), 'private_key_clear' => base64_encode($res['privatekey'])) is returned
    in sources/main.functions.php on line 2368
  18. generateUserKeys($passwordClear) is assigned to $userKeys
    in sources/identify.php on line 1506
  19. encryptUserObjectKey() is called
    in sources/identify.php on line 1547
  20. Enters via parameter $publicKey
    in sources/main.functions.php on line 2512
  21. Data is passed through base64_decode()
    in sources/main.functions.php on line 2517
  2. Path: Crypt_RSA::loadKey() is called in sources/main.functions.php on line 2517
  1. Crypt_RSA::loadKey() is called
    in sources/main.functions.php on line 2517
  2. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1816
  3. Data is passed through _parseKey(), and $this->_parseKey($key, $type) is assigned to $components
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1875
  4. $components['comment'] is assigned to property Crypt_RSA::$comment
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1898
  5. Read from property Crypt_RSA::$comment, and 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . $this->comment is assigned to $RSAPublicKey
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1096
  6. $RSAPublicKey is returned
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1098
  7. array('privatekey' => $this->_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients), 'publickey' => $this->_convertPublicKey($n, $e), 'partialkey' => false) is returned
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 760
  8. $rsa->createKey(4096) is assigned to $res
    in sources/main.functions.php on line 2364
  9. Data is passed through base64_encode()
    in sources/main.functions.php on line 2370
  10. array('private_key' => base64_encode($privatekey), 'public_key' => base64_encode($res['publickey']), 'private_key_clear' => base64_encode($res['privatekey'])) is returned
    in sources/main.functions.php on line 2368
  11. generateUserKeys($passwordClear) is assigned to $userKeys
    in sources/identify.php on line 1506
  12. encryptUserObjectKey() is called
    in sources/identify.php on line 1547
  13. Enters via parameter $publicKey
    in sources/main.functions.php on line 2512
  14. Data is passed through base64_decode()
    in sources/main.functions.php on line 2517

Used in variable context

  1. Crypt_RSA::loadKey() is called
    in sources/main.functions.php on line 2517
  2. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1816
  3. Crypt_RSA::_parseKey() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1875
  4. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1154
  5. Data is passed through _extractBER()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1610
  6. $this->_extractBER($key) is assigned to $decoded
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1610
  7. Data is passed through _string_shift()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1625
  8. Data is passed through unpack()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1625
  9. extract() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1625

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
Security Variable Injection introduced by
base64_decode($publicKey) can contain request data and is used in variable name context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Crypt_RSA::loadKey() is called
    in sources/main.functions.php on line 2517
  2. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1816
  3. Data is passed through _parseKey(), and $this->_parseKey($key, $type) is assigned to $components
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1875
  4. $components['comment'] is assigned to property Crypt_RSA::$comment
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1898
  5. Read from property Crypt_RSA::$comment, and 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . $this->comment is assigned to $RSAPublicKey
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1096
  6. $RSAPublicKey is returned
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1098
  7. array('privatekey' => $this->_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients), 'publickey' => $this->_convertPublicKey($n, $e), 'partialkey' => false) is returned
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 760
  8. $rsa->createKey(4096) is assigned to $res
    in sources/main.functions.php on line 2364
  9. Data is passed through base64_encode()
    in sources/main.functions.php on line 2370
  10. array('private_key' => base64_encode($privatekey), 'public_key' => base64_encode($res['publickey']), 'private_key_clear' => base64_encode($res['privatekey'])) is returned
    in sources/main.functions.php on line 2368
  11. generateUserKeys($passwordClear) is assigned to $userKeys
    in sources/identify.php on line 1506
  12. encryptUserObjectKey() is called
    in sources/identify.php on line 1547
  13. Enters via parameter $publicKey
    in sources/main.functions.php on line 2512
  14. Data is passed through base64_decode()
    in sources/main.functions.php on line 2517

Used in variable context

  1. Crypt_RSA::loadKey() is called
    in sources/main.functions.php on line 2517
  2. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1816
  3. Crypt_RSA::_parseKey() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1875
  4. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1154
  5. Data is passed through preg_split()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1498
  6. preg_split('#\r\n|\r|\n#', $key) is assigned to $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1498
  7. Data is passed through array_slice()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1568
  8. Data is passed through array_map()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1568
  9. Data is passed through implode()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1568
  10. Data is passed through base64_decode()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1568
  11. base64_decode(implode('', array_map('trim', array_slice($key, $offset, $privateLength)))) is assigned to $private
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1568
  12. Data is passed through _string_shift()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1590
  13. Data is passed through unpack()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1590
  14. extract() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1590

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
Security Variable Injection introduced by
base64_decode($publicKey) can contain request data and is used in variable name context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Crypt_RSA::loadKey() is called
    in sources/main.functions.php on line 2517
  2. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1816
  3. Data is passed through _parseKey(), and $this->_parseKey($key, $type) is assigned to $components
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1875
  4. $components['comment'] is assigned to property Crypt_RSA::$comment
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1898
  5. Read from property Crypt_RSA::$comment, and 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . $this->comment is assigned to $RSAPublicKey
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1096
  6. $RSAPublicKey is returned
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1098
  7. array('privatekey' => $this->_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients), 'publickey' => $this->_convertPublicKey($n, $e), 'partialkey' => false) is returned
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 760
  8. $rsa->createKey(4096) is assigned to $res
    in sources/main.functions.php on line 2364
  9. Data is passed through base64_encode()
    in sources/main.functions.php on line 2370
  10. array('private_key' => base64_encode($privatekey), 'public_key' => base64_encode($res['publickey']), 'private_key_clear' => base64_encode($res['privatekey'])) is returned
    in sources/main.functions.php on line 2368
  11. generateUserKeys($passwordClear) is assigned to $userKeys
    in sources/identify.php on line 1506
  12. encryptUserObjectKey() is called
    in sources/identify.php on line 1547
  13. Enters via parameter $publicKey
    in sources/main.functions.php on line 2512
  14. Data is passed through base64_decode()
    in sources/main.functions.php on line 2517

Used in variable context

  1. Crypt_RSA::loadKey() is called
    in sources/main.functions.php on line 2517
  2. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1816
  3. Crypt_RSA::_parseKey() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1875
  4. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1154
  5. Data is passed through _extractBER()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1610
  6. $this->_extractBER($key) is assigned to $decoded
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1610
  7. Data is passed through _string_shift()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1629
  8. $this->_string_shift($decoded, $length) is assigned to $kdfoptions
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1629
  9. Data is passed through _string_shift()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1643
  10. Data is passed through unpack()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1643
  11. extract() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1643

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
Security Variable Injection introduced by
base64_decode($publicKey) can contain request data and is used in variable name context(s) leading to a potential security vulnerability.

2 paths for user data to reach this point

  1. Path: ParameterBag::get() returns request data in vendor/symfony/http-foundation/ParameterBag.php on line 82
  1. ParameterBag::get() returns request data
    in vendor/symfony/http-foundation/ParameterBag.php on line 82
  2. Data is passed through identifyGetUserCredentials(), and identifyGetUserCredentials($SETTINGS, (string)$server['PHP_AUTH_USER'], (string)$server['PHP_AUTH_PW'], (string)filter_var($dataReceived['pw'], FILTER_SANITIZE_FULL_SPECIAL_CHARS), (string)filter_var($dataReceived['login'], FILTER_SANITIZE_FULL_SPECIAL_CHARS)) is assigned to $userCredentials
    in sources/identify.php on line 293
  3. $userCredentials['passwordClear'] is assigned to $passwordClear
    in sources/identify.php on line 301
  4. prepareUserEncryptionKeys() is called
    in sources/identify.php on line 499
  5. Enters via parameter $passwordClear
    in sources/identify.php on line 1042
  6. decryptPrivateKey() is called
    in sources/identify.php on line 1079
  7. Enters via parameter $userPwd
    in sources/main.functions.php on line 2393
  8. Crypt_Base::setPassword() is called
    in sources/main.functions.php on line 2400
  9. Enters via parameter $password
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 685
  10. Data is passed through hash_pbkdf2(), and hash_pbkdf2($hash, $password, $salt, $count, $dkLen, true) is assigned to $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 779
  11. Crypt_Base::setKey() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 783
  12. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 653
  13. $key is assigned to property Crypt_AES::$key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 660
  14. Read from property Crypt_AES::$key, and Data is passed through openssl_encrypt(), and openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options) is returned
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 821
  15. $cipher->encrypt($res['privatekey']) is assigned to $privatekey
    in sources/main.functions.php on line 2367
  16. Data is passed through base64_encode()
    in sources/main.functions.php on line 2369
  17. array('private_key' => base64_encode($privatekey), 'public_key' => base64_encode($res['publickey']), 'private_key_clear' => base64_encode($res['privatekey'])) is returned
    in sources/main.functions.php on line 2368
  18. generateUserKeys($passwordClear) is assigned to $userKeys
    in sources/identify.php on line 1506
  19. encryptUserObjectKey() is called
    in sources/identify.php on line 1547
  20. Enters via parameter $publicKey
    in sources/main.functions.php on line 2512
  21. Data is passed through base64_decode()
    in sources/main.functions.php on line 2517
  2. Path: Crypt_RSA::loadKey() is called in sources/main.functions.php on line 2517
  1. Crypt_RSA::loadKey() is called
    in sources/main.functions.php on line 2517
  2. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1816
  3. Data is passed through _parseKey(), and $this->_parseKey($key, $type) is assigned to $components
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1875
  4. $components['comment'] is assigned to property Crypt_RSA::$comment
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1898
  5. Read from property Crypt_RSA::$comment, and 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . $this->comment is assigned to $RSAPublicKey
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1096
  6. $RSAPublicKey is returned
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1098
  7. array('privatekey' => $this->_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients), 'publickey' => $this->_convertPublicKey($n, $e), 'partialkey' => false) is returned
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 760
  8. $rsa->createKey(4096) is assigned to $res
    in sources/main.functions.php on line 2364
  9. Data is passed through base64_encode()
    in sources/main.functions.php on line 2370
  10. array('private_key' => base64_encode($privatekey), 'public_key' => base64_encode($res['publickey']), 'private_key_clear' => base64_encode($res['privatekey'])) is returned
    in sources/main.functions.php on line 2368
  11. generateUserKeys($passwordClear) is assigned to $userKeys
    in sources/identify.php on line 1506
  12. encryptUserObjectKey() is called
    in sources/identify.php on line 1547
  13. Enters via parameter $publicKey
    in sources/main.functions.php on line 2512
  14. Data is passed through base64_decode()
    in sources/main.functions.php on line 2517

Used in variable context

  1. Crypt_RSA::loadKey() is called
    in sources/main.functions.php on line 2517
  2. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1816
  3. Crypt_RSA::_parseKey() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1875
  4. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1154
  5. Data is passed through _extractBER()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1610
  6. $this->_extractBER($key) is assigned to $decoded
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1610
  7. Data is passed through _string_shift()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1620
  8. Data is passed through unpack()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1620
  9. extract() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1620

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
Security Variable Injection introduced by
base64_decode($publicKey) can contain request data and is used in variable name context(s) leading to a potential security vulnerability.

2 paths for user data to reach this point

  1. Path: ParameterBag::get() returns request data in vendor/symfony/http-foundation/ParameterBag.php on line 82
  1. ParameterBag::get() returns request data
    in vendor/symfony/http-foundation/ParameterBag.php on line 82
  2. Data is passed through identifyGetUserCredentials(), and identifyGetUserCredentials($SETTINGS, (string)$server['PHP_AUTH_USER'], (string)$server['PHP_AUTH_PW'], (string)filter_var($dataReceived['pw'], FILTER_SANITIZE_FULL_SPECIAL_CHARS), (string)filter_var($dataReceived['login'], FILTER_SANITIZE_FULL_SPECIAL_CHARS)) is assigned to $userCredentials
    in sources/identify.php on line 293
  3. $userCredentials['passwordClear'] is assigned to $passwordClear
    in sources/identify.php on line 301
  4. prepareUserEncryptionKeys() is called
    in sources/identify.php on line 499
  5. Enters via parameter $passwordClear
    in sources/identify.php on line 1042
  6. decryptPrivateKey() is called
    in sources/identify.php on line 1079
  7. Enters via parameter $userPwd
    in sources/main.functions.php on line 2393
  8. Crypt_Base::setPassword() is called
    in sources/main.functions.php on line 2400
  9. Enters via parameter $password
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 685
  10. Data is passed through hash_pbkdf2(), and hash_pbkdf2($hash, $password, $salt, $count, $dkLen, true) is assigned to $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 779
  11. Crypt_Base::setKey() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 783
  12. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 653
  13. $key is assigned to property Crypt_AES::$key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 660
  14. Read from property Crypt_AES::$key, and Data is passed through openssl_encrypt(), and openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options) is returned
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 821
  15. $cipher->encrypt($res['privatekey']) is assigned to $privatekey
    in sources/main.functions.php on line 2367
  16. Data is passed through base64_encode()
    in sources/main.functions.php on line 2369
  17. array('private_key' => base64_encode($privatekey), 'public_key' => base64_encode($res['publickey']), 'private_key_clear' => base64_encode($res['privatekey'])) is returned
    in sources/main.functions.php on line 2368
  18. generateUserKeys($password) is assigned to $userKeys
    in sources/users.queries.php on line 2614
  19. encryptUserObjectKey() is called
    in sources/users.queries.php on line 2652
  20. Enters via parameter $publicKey
    in sources/main.functions.php on line 2512
  21. Data is passed through base64_decode()
    in sources/main.functions.php on line 2517
  2. Path: Crypt_RSA::loadKey() is called in sources/main.functions.php on line 2517
  1. Crypt_RSA::loadKey() is called
    in sources/main.functions.php on line 2517
  2. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1816
  3. Data is passed through _parseKey(), and $this->_parseKey($key, $type) is assigned to $components
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1875
  4. $components['comment'] is assigned to property Crypt_RSA::$comment
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1898
  5. Read from property Crypt_RSA::$comment, and 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . $this->comment is assigned to $RSAPublicKey
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1096
  6. $RSAPublicKey is returned
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1098
  7. array('privatekey' => $this->_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients), 'publickey' => $this->_convertPublicKey($n, $e), 'partialkey' => false) is returned
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 760
  8. $rsa->createKey(4096) is assigned to $res
    in sources/main.functions.php on line 2364
  9. Data is passed through base64_encode()
    in sources/main.functions.php on line 2370
  10. array('private_key' => base64_encode($privatekey), 'public_key' => base64_encode($res['publickey']), 'private_key_clear' => base64_encode($res['privatekey'])) is returned
    in sources/main.functions.php on line 2368
  11. generateUserKeys($passwordClear) is assigned to $userKeys
    in sources/identify.php on line 1506
  12. encryptUserObjectKey() is called
    in sources/identify.php on line 1547
  13. Enters via parameter $publicKey
    in sources/main.functions.php on line 2512
  14. Data is passed through base64_decode()
    in sources/main.functions.php on line 2517

Used in variable context

  1. Crypt_RSA::loadKey() is called
    in sources/main.functions.php on line 2517
  2. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1816
  3. Crypt_RSA::_parseKey() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1875
  4. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1154
  5. Data is passed through _extractBER()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1610
  6. $this->_extractBER($key) is assigned to $decoded
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1610
  7. Data is passed through _string_shift()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1661
  8. Data is passed through unpack()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1661
  9. extract() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1661

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
Security Variable Injection introduced by
base64_decode($publicKey) can contain request data and is used in variable name context(s) leading to a potential security vulnerability.

2 paths for user data to reach this point

  1. Path: ParameterBag::get() returns request data in vendor/symfony/http-foundation/ParameterBag.php on line 82
  1. ParameterBag::get() returns request data
    in vendor/symfony/http-foundation/ParameterBag.php on line 82
  2. Data is passed through identifyGetUserCredentials(), and identifyGetUserCredentials($SETTINGS, (string)$server['PHP_AUTH_USER'], (string)$server['PHP_AUTH_PW'], (string)filter_var($dataReceived['pw'], FILTER_SANITIZE_FULL_SPECIAL_CHARS), (string)filter_var($dataReceived['login'], FILTER_SANITIZE_FULL_SPECIAL_CHARS)) is assigned to $userCredentials
    in sources/identify.php on line 293
  3. $userCredentials['passwordClear'] is assigned to $passwordClear
    in sources/identify.php on line 301
  4. prepareUserEncryptionKeys() is called
    in sources/identify.php on line 499
  5. Enters via parameter $passwordClear
    in sources/identify.php on line 1042
  6. decryptPrivateKey() is called
    in sources/identify.php on line 1079
  7. Enters via parameter $userPwd
    in sources/main.functions.php on line 2393
  8. Crypt_Base::setPassword() is called
    in sources/main.functions.php on line 2400
  9. Enters via parameter $password
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 685
  10. Data is passed through hash_pbkdf2(), and hash_pbkdf2($hash, $password, $salt, $count, $dkLen, true) is assigned to $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 779
  11. Crypt_Base::setKey() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 783
  12. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 653
  13. $key is assigned to property Crypt_AES::$key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 660
  14. Read from property Crypt_AES::$key, and Data is passed through openssl_encrypt(), and openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options) is returned
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 821
  15. $cipher->encrypt($res['privatekey']) is assigned to $privatekey
    in sources/main.functions.php on line 2367
  16. Data is passed through base64_encode()
    in sources/main.functions.php on line 2369
  17. array('private_key' => base64_encode($privatekey), 'public_key' => base64_encode($res['publickey']), 'private_key_clear' => base64_encode($res['privatekey'])) is returned
    in sources/main.functions.php on line 2368
  18. generateUserKeys($password) is assigned to $userKeys
    in sources/users.queries.php on line 2614
  19. encryptUserObjectKey() is called
    in sources/users.queries.php on line 2652
  20. Enters via parameter $publicKey
    in sources/main.functions.php on line 2512
  21. Data is passed through base64_decode()
    in sources/main.functions.php on line 2517
  2. Path: Crypt_RSA::loadKey() is called in sources/main.functions.php on line 2517
  1. Crypt_RSA::loadKey() is called
    in sources/main.functions.php on line 2517
  2. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1816
  3. Data is passed through _parseKey(), and $this->_parseKey($key, $type) is assigned to $components
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1875
  4. $components['comment'] is assigned to property Crypt_RSA::$comment
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1898
  5. Read from property Crypt_RSA::$comment, and 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . $this->comment is assigned to $RSAPublicKey
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1096
  6. $RSAPublicKey is returned
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1098
  7. array('privatekey' => $this->_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients), 'publickey' => $this->_convertPublicKey($n, $e), 'partialkey' => false) is returned
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 760
  8. $rsa->createKey(4096) is assigned to $res
    in sources/main.functions.php on line 2364
  9. Data is passed through base64_encode()
    in sources/main.functions.php on line 2370
  10. array('private_key' => base64_encode($privatekey), 'public_key' => base64_encode($res['publickey']), 'private_key_clear' => base64_encode($res['privatekey'])) is returned
    in sources/main.functions.php on line 2368
  11. generateUserKeys($passwordClear) is assigned to $userKeys
    in sources/identify.php on line 1506
  12. encryptUserObjectKey() is called
    in sources/identify.php on line 1547
  13. Enters via parameter $publicKey
    in sources/main.functions.php on line 2512
  14. Data is passed through base64_decode()
    in sources/main.functions.php on line 2517

Used in variable context

  1. Crypt_RSA::loadKey() is called
    in sources/main.functions.php on line 2517
  2. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1816
  3. Crypt_RSA::_parseKey() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1875
  4. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1154
  5. Data is passed through _extractBER()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1610
  6. $this->_extractBER($key) is assigned to $decoded
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1610
  7. Data is passed through _string_shift()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1656
  8. Data is passed through unpack()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1656
  9. extract() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1656

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
Security Variable Injection introduced by
base64_decode($publicKey) can contain request data and is used in variable name context(s) leading to a potential security vulnerability.

2 paths for user data to reach this point

  1. Path: ParameterBag::get() returns request data in vendor/symfony/http-foundation/ParameterBag.php on line 82
  1. ParameterBag::get() returns request data
    in vendor/symfony/http-foundation/ParameterBag.php on line 82
  2. Data is passed through identifyGetUserCredentials(), and identifyGetUserCredentials($SETTINGS, (string)$server['PHP_AUTH_USER'], (string)$server['PHP_AUTH_PW'], (string)filter_var($dataReceived['pw'], FILTER_SANITIZE_FULL_SPECIAL_CHARS), (string)filter_var($dataReceived['login'], FILTER_SANITIZE_FULL_SPECIAL_CHARS)) is assigned to $userCredentials
    in sources/identify.php on line 293
  3. $userCredentials['passwordClear'] is assigned to $passwordClear
    in sources/identify.php on line 301
  4. prepareUserEncryptionKeys() is called
    in sources/identify.php on line 499
  5. Enters via parameter $passwordClear
    in sources/identify.php on line 1042
  6. decryptPrivateKey() is called
    in sources/identify.php on line 1079
  7. Enters via parameter $userPwd
    in sources/main.functions.php on line 2393
  8. Crypt_Base::setPassword() is called
    in sources/main.functions.php on line 2400
  9. Enters via parameter $password
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 685
  10. Data is passed through hash_pbkdf2(), and hash_pbkdf2($hash, $password, $salt, $count, $dkLen, true) is assigned to $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 779
  11. Crypt_Base::setKey() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 783
  12. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 653
  13. $key is assigned to property Crypt_AES::$key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 660
  14. Read from property Crypt_AES::$key, and Data is passed through openssl_encrypt(), and openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options) is returned
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 821
  15. $cipher->encrypt($res['privatekey']) is assigned to $privatekey
    in sources/main.functions.php on line 2367
  16. Data is passed through base64_encode()
    in sources/main.functions.php on line 2369
  17. array('private_key' => base64_encode($privatekey), 'public_key' => base64_encode($res['publickey']), 'private_key_clear' => base64_encode($res['privatekey'])) is returned
    in sources/main.functions.php on line 2368
  18. generateUserKeys($password) is assigned to $userKeys
    in sources/users.queries.php on line 2614
  19. encryptUserObjectKey() is called
    in sources/users.queries.php on line 2652
  20. Enters via parameter $publicKey
    in sources/main.functions.php on line 2512
  21. Data is passed through base64_decode()
    in sources/main.functions.php on line 2517
  2. Path: Crypt_RSA::loadKey() is called in sources/main.functions.php on line 2517
  1. Crypt_RSA::loadKey() is called
    in sources/main.functions.php on line 2517
  2. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1816
  3. Data is passed through _parseKey(), and $this->_parseKey($key, $type) is assigned to $components
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1875
  4. $components['comment'] is assigned to property Crypt_RSA::$comment
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1898
  5. Read from property Crypt_RSA::$comment, and 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . $this->comment is assigned to $RSAPublicKey
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1096
  6. $RSAPublicKey is returned
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1098
  7. array('privatekey' => $this->_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients), 'publickey' => $this->_convertPublicKey($n, $e), 'partialkey' => false) is returned
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 760
  8. $rsa->createKey(4096) is assigned to $res
    in sources/main.functions.php on line 2364
  9. Data is passed through base64_encode()
    in sources/main.functions.php on line 2370
  10. array('private_key' => base64_encode($privatekey), 'public_key' => base64_encode($res['publickey']), 'private_key_clear' => base64_encode($res['privatekey'])) is returned
    in sources/main.functions.php on line 2368
  11. generateUserKeys($passwordClear) is assigned to $userKeys
    in sources/identify.php on line 1506
  12. encryptUserObjectKey() is called
    in sources/identify.php on line 1547
  13. Enters via parameter $publicKey
    in sources/main.functions.php on line 2512
  14. Data is passed through base64_decode()
    in sources/main.functions.php on line 2517

Used in variable context

  1. Crypt_RSA::loadKey() is called
    in sources/main.functions.php on line 2517
  2. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1816
  3. Crypt_RSA::_parseKey() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1875
  4. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1154
  5. Data is passed through _extractBER()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1610
  6. $this->_extractBER($key) is assigned to $decoded
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1610
  7. Data is passed through _string_shift()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1615
  8. Data is passed through unpack()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1615
  9. extract() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1615

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
Security Variable Injection introduced by
base64_decode($publicKey) can contain request data and is used in variable name context(s) leading to a potential security vulnerability.

2 paths for user data to reach this point

  1. Path: ParameterBag::get() returns request data in vendor/symfony/http-foundation/ParameterBag.php on line 82
  1. ParameterBag::get() returns request data
    in vendor/symfony/http-foundation/ParameterBag.php on line 82
  2. Data is passed through identifyGetUserCredentials(), and identifyGetUserCredentials($SETTINGS, (string)$server['PHP_AUTH_USER'], (string)$server['PHP_AUTH_PW'], (string)filter_var($dataReceived['pw'], FILTER_SANITIZE_FULL_SPECIAL_CHARS), (string)filter_var($dataReceived['login'], FILTER_SANITIZE_FULL_SPECIAL_CHARS)) is assigned to $userCredentials
    in sources/identify.php on line 293
  3. $userCredentials['passwordClear'] is assigned to $passwordClear
    in sources/identify.php on line 301
  4. prepareUserEncryptionKeys() is called
    in sources/identify.php on line 499
  5. Enters via parameter $passwordClear
    in sources/identify.php on line 1042
  6. generateUserKeys() is called
    in sources/identify.php on line 1047
  7. Enters via parameter $userPwd
    in sources/main.functions.php on line 2357
  8. Crypt_Base::setPassword() is called
    in sources/main.functions.php on line 2366
  9. Enters via parameter $password
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 685
  10. Data is passed through hash_pbkdf2(), and hash_pbkdf2($hash, $password, $salt, $count, $dkLen, true) is assigned to $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 779
  11. Crypt_Base::setKey() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 783
  12. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 653
  13. $key is assigned to property Crypt_AES::$key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 660
  14. Read from property Crypt_AES::$key, and Data is passed through openssl_encrypt(), and openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options) is returned
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 821
  15. $cipher->encrypt($res['privatekey']) is assigned to $privatekey
    in sources/main.functions.php on line 2367
  16. Data is passed through base64_encode()
    in sources/main.functions.php on line 2369
  17. array('private_key' => base64_encode($privatekey), 'public_key' => base64_encode($res['publickey']), 'private_key_clear' => base64_encode($res['privatekey'])) is returned
    in sources/main.functions.php on line 2368
  18. generateUserKeys($passwordClear) is assigned to $userKeys
    in sources/identify.php on line 1506
  19. encryptUserObjectKey() is called
    in sources/identify.php on line 1547
  20. Enters via parameter $publicKey
    in sources/main.functions.php on line 2512
  21. Data is passed through base64_decode()
    in sources/main.functions.php on line 2517
  2. Path: Crypt_RSA::loadKey() is called in sources/main.functions.php on line 2517
  1. Crypt_RSA::loadKey() is called
    in sources/main.functions.php on line 2517
  2. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1816
  3. Data is passed through _parseKey(), and $this->_parseKey($key, $type) is assigned to $components
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1875
  4. $components['comment'] is assigned to property Crypt_RSA::$comment
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1898
  5. Read from property Crypt_RSA::$comment, and 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . $this->comment is assigned to $RSAPublicKey
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1096
  6. $RSAPublicKey is returned
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1098
  7. array('privatekey' => $this->_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients), 'publickey' => $this->_convertPublicKey($n, $e), 'partialkey' => false) is returned
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 760
  8. $rsa->createKey(4096) is assigned to $res
    in sources/main.functions.php on line 2364
  9. Data is passed through base64_encode()
    in sources/main.functions.php on line 2370
  10. array('private_key' => base64_encode($privatekey), 'public_key' => base64_encode($res['publickey']), 'private_key_clear' => base64_encode($res['privatekey'])) is returned
    in sources/main.functions.php on line 2368
  11. generateUserKeys($passwordClear) is assigned to $userKeys
    in sources/identify.php on line 1506
  12. encryptUserObjectKey() is called
    in sources/identify.php on line 1547
  13. Enters via parameter $publicKey
    in sources/main.functions.php on line 2512
  14. Data is passed through base64_decode()
    in sources/main.functions.php on line 2517

Used in variable context

  1. Crypt_RSA::loadKey() is called
    in sources/main.functions.php on line 2517
  2. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1816
  3. Crypt_RSA::_parseKey() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1875
  4. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1154
  5. Data is passed through _extractBER()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1610
  6. $this->_extractBER($key) is assigned to $decoded
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1610
  7. Data is passed through _string_shift()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1630
  8. Data is passed through unpack()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1630
  9. extract() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1630

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
2518
        // Encrypt
2519
        return base64_encode($rsa->encrypt(base64_decode($key)));
2520
    /*} else {
2521
2522
    }*/
2523
}
2524
2525
/**
2526
 * Decrypts using RSA an encrypted string using a private key.
2527
 *
2528
 * @param string $key        Encrypted key
2529
 * @param string $privateKey User private key
2530
 *
2531
 * @return string
2532
 */
2533
function decryptUserObjectKey(string $key, string $privateKey): string
2534
{
2535
    //if (WIP === false) {
2536
        // Load classes
2537
        $rsa = new Crypt_RSA();
2538
        $rsa->loadKey(base64_decode($privateKey));
0 ignored issues
show
Security Variable Injection introduced by
base64_decode($privateKey) can contain request data and is used in variable name context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. ParameterBag::get() returns request data
    in vendor/symfony/http-foundation/ParameterBag.php on line 82
  2. Data is passed through identifyGetUserCredentials(), and identifyGetUserCredentials($SETTINGS, (string)$server['PHP_AUTH_USER'], (string)$server['PHP_AUTH_PW'], (string)filter_var($dataReceived['pw'], FILTER_SANITIZE_FULL_SPECIAL_CHARS), (string)filter_var($dataReceived['login'], FILTER_SANITIZE_FULL_SPECIAL_CHARS)) is assigned to $userCredentials
    in sources/identify.php on line 293
  3. $userCredentials['passwordClear'] is assigned to $passwordClear
    in sources/identify.php on line 301
  4. prepareUserEncryptionKeys() is called
    in sources/identify.php on line 499
  5. Enters via parameter $passwordClear
    in sources/identify.php on line 1042
  6. decryptPrivateKey() is called
    in sources/identify.php on line 1079
  7. Enters via parameter $userPwd
    in sources/main.functions.php on line 2393
  8. Crypt_Base::setPassword() is called
    in sources/main.functions.php on line 2400
  9. Enters via parameter $password
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 685
  10. Data is passed through hash_pbkdf2(), and hash_pbkdf2($hash, $password, $salt, $count, $dkLen, true) is assigned to $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 779
  11. Crypt_Base::setKey() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 783
  12. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 653
  13. $key is assigned to property Crypt_AES::$key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 660
  14. Read from property Crypt_AES::$key, and Data is passed through openssl_decrypt(), and openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options) is assigned to $plaintext
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 1129
  15. $this->paddable ? $this->_unpad($plaintext) : $plaintext is returned
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 1202
  16. Data is passed through base64_encode(), and base64_encode((string)$cipher->decrypt(base64_decode($userPrivateKey))) is returned
    in sources/main.functions.php on line 2402
  17. decryptPrivateKey($post_previous_pwd, $userData['private_key']) is assigned to $privateKey
    in sources/main.queries.php on line 3102
  18. decryptUserObjectKey() is called
    in sources/main.queries.php on line 3134
  19. Enters via parameter $privateKey
    in sources/main.functions.php on line 2533
  20. Data is passed through base64_decode()
    in sources/main.functions.php on line 2538

Used in variable context

  1. Crypt_RSA::loadKey() is called
    in sources/main.functions.php on line 2538
  2. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1816
  3. Crypt_RSA::_parseKey() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1875
  4. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1154
  5. Data is passed through _extractBER()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1610
  6. $this->_extractBER($key) is assigned to $decoded
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1610
  7. Data is passed through _string_shift()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1615
  8. Data is passed through unpack()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1615
  9. extract() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1615

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
Security Variable Injection introduced by
base64_decode($privateKey) can contain request data and is used in variable name context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. ParameterBag::get() returns request data
    in vendor/symfony/http-foundation/ParameterBag.php on line 82
  2. Data is passed through identifyGetUserCredentials(), and identifyGetUserCredentials($SETTINGS, (string)$server['PHP_AUTH_USER'], (string)$server['PHP_AUTH_PW'], (string)filter_var($dataReceived['pw'], FILTER_SANITIZE_FULL_SPECIAL_CHARS), (string)filter_var($dataReceived['login'], FILTER_SANITIZE_FULL_SPECIAL_CHARS)) is assigned to $userCredentials
    in sources/identify.php on line 293
  3. $userCredentials['passwordClear'] is assigned to $passwordClear
    in sources/identify.php on line 301
  4. prepareUserEncryptionKeys() is called
    in sources/identify.php on line 499
  5. Enters via parameter $passwordClear
    in sources/identify.php on line 1042
  6. decryptPrivateKey() is called
    in sources/identify.php on line 1079
  7. Enters via parameter $userPwd
    in sources/main.functions.php on line 2393
  8. Crypt_Base::setPassword() is called
    in sources/main.functions.php on line 2400
  9. Enters via parameter $password
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 685
  10. Data is passed through hash_pbkdf2(), and hash_pbkdf2($hash, $password, $salt, $count, $dkLen, true) is assigned to $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 779
  11. Crypt_Base::setKey() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 783
  12. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 653
  13. $key is assigned to property Crypt_AES::$key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 660
  14. Read from property Crypt_AES::$key, and Data is passed through openssl_decrypt(), and openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options) is assigned to $plaintext
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 1129
  15. $this->paddable ? $this->_unpad($plaintext) : $plaintext is returned
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 1202
  16. Data is passed through base64_encode(), and base64_encode((string)$cipher->decrypt(base64_decode($userPrivateKey))) is returned
    in sources/main.functions.php on line 2402
  17. decryptPrivateKey($post_previous_pwd, $userData['private_key']) is assigned to $privateKey
    in sources/main.queries.php on line 3102
  18. decryptUserObjectKey() is called
    in sources/main.queries.php on line 3134
  19. Enters via parameter $privateKey
    in sources/main.functions.php on line 2533
  20. Data is passed through base64_decode()
    in sources/main.functions.php on line 2538

Used in variable context

  1. Crypt_RSA::loadKey() is called
    in sources/main.functions.php on line 2538
  2. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1816
  3. Crypt_RSA::_parseKey() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1875
  4. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1154
  5. Data is passed through _extractBER()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1610
  6. $this->_extractBER($key) is assigned to $decoded
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1610
  7. Data is passed through _string_shift()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1625
  8. Data is passed through unpack()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1625
  9. extract() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1625

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
Security Variable Injection introduced by
base64_decode($privateKey) can contain request data and is used in variable name context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. ParameterBag::get() returns request data
    in vendor/symfony/http-foundation/ParameterBag.php on line 82
  2. Data is passed through identifyGetUserCredentials(), and identifyGetUserCredentials($SETTINGS, (string)$server['PHP_AUTH_USER'], (string)$server['PHP_AUTH_PW'], (string)filter_var($dataReceived['pw'], FILTER_SANITIZE_FULL_SPECIAL_CHARS), (string)filter_var($dataReceived['login'], FILTER_SANITIZE_FULL_SPECIAL_CHARS)) is assigned to $userCredentials
    in sources/identify.php on line 293
  3. $userCredentials['passwordClear'] is assigned to $passwordClear
    in sources/identify.php on line 301
  4. prepareUserEncryptionKeys() is called
    in sources/identify.php on line 499
  5. Enters via parameter $passwordClear
    in sources/identify.php on line 1042
  6. generateUserKeys() is called
    in sources/identify.php on line 1047
  7. Enters via parameter $userPwd
    in sources/main.functions.php on line 2357
  8. Crypt_Base::setPassword() is called
    in sources/main.functions.php on line 2366
  9. Enters via parameter $password
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 685
  10. Data is passed through hash_pbkdf2(), and hash_pbkdf2($hash, $password, $salt, $count, $dkLen, true) is assigned to $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 779
  11. Crypt_Base::setKey() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 783
  12. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 653
  13. $key is assigned to property Crypt_AES::$key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 660
  14. Read from property Crypt_AES::$key, and Data is passed through openssl_decrypt(), and openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options) is assigned to $plaintext
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 1129
  15. $this->paddable ? $this->_unpad($plaintext) : $plaintext is returned
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 1202
  16. Data is passed through base64_encode(), and base64_encode((string)$cipher->decrypt(base64_decode($userPrivateKey))) is returned
    in sources/main.functions.php on line 2402
  17. array('public_key' => $userInfo['public_key'], 'private_key_clear' => decryptPrivateKey($passwordClear, $userInfo['private_key']), 'update_keys_in_db' => array()) is returned
    in sources/identify.php on line 1077
  18. prepareUserEncryptionKeys($userInfo, $passwordClear) is assigned to $returnKeys
    in sources/identify.php on line 499
  19. decryptUserObjectKey() is called
    in sources/identify.php on line 506
  20. Enters via parameter $privateKey
    in sources/main.functions.php on line 2533
  21. Data is passed through base64_decode()
    in sources/main.functions.php on line 2538

Used in variable context

  1. Crypt_RSA::loadKey() is called
    in sources/main.functions.php on line 2538
  2. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1816
  3. Crypt_RSA::_parseKey() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1875
  4. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1154
  5. Data is passed through _extractBER()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1610
  6. $this->_extractBER($key) is assigned to $decoded
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1610
  7. Data is passed through _string_shift()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1661
  8. Data is passed through unpack()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1661
  9. extract() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1661

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
Security Variable Injection introduced by
base64_decode($privateKey) can contain request data and is used in variable name context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. ParameterBag::get() returns request data
    in vendor/symfony/http-foundation/ParameterBag.php on line 82
  2. Data is passed through identifyGetUserCredentials(), and identifyGetUserCredentials($SETTINGS, (string)$server['PHP_AUTH_USER'], (string)$server['PHP_AUTH_PW'], (string)filter_var($dataReceived['pw'], FILTER_SANITIZE_FULL_SPECIAL_CHARS), (string)filter_var($dataReceived['login'], FILTER_SANITIZE_FULL_SPECIAL_CHARS)) is assigned to $userCredentials
    in sources/identify.php on line 293
  3. $userCredentials['passwordClear'] is assigned to $passwordClear
    in sources/identify.php on line 301
  4. prepareUserEncryptionKeys() is called
    in sources/identify.php on line 499
  5. Enters via parameter $passwordClear
    in sources/identify.php on line 1042
  6. generateUserKeys() is called
    in sources/identify.php on line 1047
  7. Enters via parameter $userPwd
    in sources/main.functions.php on line 2357
  8. Crypt_Base::setPassword() is called
    in sources/main.functions.php on line 2366
  9. Enters via parameter $password
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 685
  10. Data is passed through hash_pbkdf2(), and hash_pbkdf2($hash, $password, $salt, $count, $dkLen, true) is assigned to $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 779
  11. Crypt_Base::setKey() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 783
  12. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 653
  13. $key is assigned to property Crypt_AES::$key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 660
  14. Read from property Crypt_AES::$key, and Data is passed through openssl_decrypt(), and openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options) is assigned to $plaintext
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 1129
  15. $this->paddable ? $this->_unpad($plaintext) : $plaintext is returned
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 1202
  16. Data is passed through base64_encode(), and base64_encode((string)$cipher->decrypt(base64_decode($userPrivateKey))) is returned
    in sources/main.functions.php on line 2402
  17. array('public_key' => $userInfo['public_key'], 'private_key_clear' => decryptPrivateKey($passwordClear, $userInfo['private_key']), 'update_keys_in_db' => array()) is returned
    in sources/identify.php on line 1077
  18. prepareUserEncryptionKeys($userInfo, $passwordClear) is assigned to $returnKeys
    in sources/identify.php on line 499
  19. decryptUserObjectKey() is called
    in sources/identify.php on line 506
  20. Enters via parameter $privateKey
    in sources/main.functions.php on line 2533
  21. Data is passed through base64_decode()
    in sources/main.functions.php on line 2538

Used in variable context

  1. Crypt_RSA::loadKey() is called
    in sources/main.functions.php on line 2538
  2. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1816
  3. Crypt_RSA::_parseKey() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1875
  4. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1154
  5. Data is passed through _extractBER()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1610
  6. $this->_extractBER($key) is assigned to $decoded
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1610
  7. Data is passed through _string_shift()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1656
  8. Data is passed through unpack()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1656
  9. extract() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1656

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
Security Variable Injection introduced by
base64_decode($privateKey) can contain request data and is used in variable name context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. ParameterBag::get() returns request data
    in vendor/symfony/http-foundation/ParameterBag.php on line 82
  2. Data is passed through identifyGetUserCredentials(), and identifyGetUserCredentials($SETTINGS, (string)$server['PHP_AUTH_USER'], (string)$server['PHP_AUTH_PW'], (string)filter_var($dataReceived['pw'], FILTER_SANITIZE_FULL_SPECIAL_CHARS), (string)filter_var($dataReceived['login'], FILTER_SANITIZE_FULL_SPECIAL_CHARS)) is assigned to $userCredentials
    in sources/identify.php on line 293
  3. $userCredentials['passwordClear'] is assigned to $passwordClear
    in sources/identify.php on line 301
  4. prepareUserEncryptionKeys() is called
    in sources/identify.php on line 499
  5. Enters via parameter $passwordClear
    in sources/identify.php on line 1042
  6. decryptPrivateKey() is called
    in sources/identify.php on line 1079
  7. Enters via parameter $userPwd
    in sources/main.functions.php on line 2393
  8. Crypt_Base::setPassword() is called
    in sources/main.functions.php on line 2400
  9. Enters via parameter $password
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 685
  10. Data is passed through hash_pbkdf2(), and hash_pbkdf2($hash, $password, $salt, $count, $dkLen, true) is assigned to $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 779
  11. Crypt_Base::setKey() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 783
  12. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 653
  13. $key is assigned to property Crypt_AES::$key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 660
  14. Read from property Crypt_AES::$key, and Data is passed through openssl_decrypt(), and openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options) is assigned to $plaintext
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 1129
  15. $this->paddable ? $this->_unpad($plaintext) : $plaintext is returned
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 1202
  16. Data is passed through base64_encode(), and base64_encode((string)$cipher->decrypt(base64_decode($userPrivateKey))) is returned
    in sources/main.functions.php on line 2402
  17. array('public_key' => $userInfo['public_key'], 'private_key_clear' => decryptPrivateKey($passwordClear, $userInfo['private_key']), 'update_keys_in_db' => array()) is returned
    in sources/identify.php on line 1077
  18. prepareUserEncryptionKeys($userInfo, $passwordClear) is assigned to $returnKeys
    in sources/identify.php on line 499
  19. decryptUserObjectKey() is called
    in sources/identify.php on line 506
  20. Enters via parameter $privateKey
    in sources/main.functions.php on line 2533
  21. Data is passed through base64_decode()
    in sources/main.functions.php on line 2538

Used in variable context

  1. Crypt_RSA::loadKey() is called
    in sources/main.functions.php on line 2538
  2. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1816
  3. Crypt_RSA::_parseKey() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1875
  4. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1154
  5. Data is passed through _extractBER()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1610
  6. $this->_extractBER($key) is assigned to $decoded
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1610
  7. Data is passed through _string_shift()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1630
  8. Data is passed through unpack()
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1630
  9. extract() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/RSA.php on line 1630

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
2539
        // Decrypt
2540
        try {
2541
            $tmpValue = $rsa->decrypt(base64_decode($key));
2542
            if (is_bool($tmpValue) === false) {
0 ignored issues
show
introduced by
The condition is_bool($tmpValue) === false is always true.
Loading history...
2543
                $ret = base64_encode((string) /** @scrutinizer ignore-type */$tmpValue);
2544
            } else {
2545
                $ret = '';
2546
            }
2547
        } catch (Exception $e) {
2548
            return $e;
2549
        }
2550
        /*} else {
2551
2552
        }*/
2553
2554
    return $ret;
2555
}
2556
2557
/**
2558
 * Encrypts a file.
2559
 *
2560
 * @param string $fileInName File name
2561
 * @param string $fileInPath Path to file
2562
 *
2563
 * @return array
2564
 */
2565
function encryptFile(string $fileInName, string $fileInPath): array
2566
{
2567
    if (defined('FILE_BUFFER_SIZE') === false) {
2568
        define('FILE_BUFFER_SIZE', 128 * 1024);
2569
    }
2570
    //if (WIP === false) {
2571
        // Load classes
2572
        $cipher = new Crypt_AES();
2573
        // Generate an object key
2574
        $objectKey = uniqidReal(32);
2575
        // Set it as password
2576
        $cipher->setPassword($objectKey);
2577
        // Prevent against out of memory
2578
        $cipher->enableContinuousBuffer();
2579
        //$cipher->disablePadding();
2580
2581
        // Encrypt the file content
2582
        $plaintext = file_get_contents(
2583
            filter_var($fileInPath . '/' . $fileInName, FILTER_SANITIZE_URL)
2584
        );
2585
        $ciphertext = $cipher->encrypt($plaintext);
2586
        // Save new file
2587
        $hash = md5($plaintext);
2588
        $fileOut = $fileInPath . '/' . TP_FILE_PREFIX . $hash;
2589
        file_put_contents($fileOut, $ciphertext);
0 ignored issues
show
Security File Manipulation introduced by
$ciphertext can contain request data and is used in file manipulation context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. ParameterBag::get() returns request data
    in vendor/symfony/http-foundation/ParameterBag.php on line 82
  2. Data is passed through identifyGetUserCredentials(), and identifyGetUserCredentials($SETTINGS, (string)$server['PHP_AUTH_USER'], (string)$server['PHP_AUTH_PW'], (string)filter_var($dataReceived['pw'], FILTER_SANITIZE_FULL_SPECIAL_CHARS), (string)filter_var($dataReceived['login'], FILTER_SANITIZE_FULL_SPECIAL_CHARS)) is assigned to $userCredentials
    in sources/identify.php on line 293
  3. $userCredentials['passwordClear'] is assigned to $passwordClear
    in sources/identify.php on line 301
  4. identifyDoLDAPChecks() is called
    in sources/identify.php on line 327
  5. Enters via parameter $passwordClear
    in sources/identify.php on line 2272
  6. authenticateThroughAD() is called
    in sources/identify.php on line 2286
  7. Enters via parameter $passwordClear
    in sources/identify.php on line 1145
  8. ldapCreateUser() is called
    in sources/identify.php on line 1237
  9. Enters via parameter $passwordClear
    in sources/identify.php on line 1503
  10. generateUserKeys() is called
    in sources/identify.php on line 1506
  11. Enters via parameter $userPwd
    in sources/main.functions.php on line 2357
  12. Crypt_Base::setPassword() is called
    in sources/main.functions.php on line 2366
  13. Enters via parameter $password
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 685
  14. Data is passed through hash_pbkdf2(), and hash_pbkdf2($hash, $password, $salt, $count, $dkLen, true) is assigned to $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 779
  15. Crypt_Base::setKey() is called
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 783
  16. Enters via parameter $key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 653
  17. $key is assigned to property Crypt_AES::$key
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 660
  18. Read from property Crypt_AES::$key, and Data is passed through openssl_encrypt(), and openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options) is returned
    in vendor/phpseclib/phpseclib/phpseclib/Crypt/Base.php on line 821
  19. $cipher->encrypt($plaintext) is assigned to $ciphertext
    in sources/main.functions.php on line 2585

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
2590
        unlink($fileInPath . '/' . $fileInName);
2591
        return [
2592
            'fileHash' => base64_encode($hash),
2593
            'objectKey' => base64_encode($objectKey),
2594
        ];
2595
    /*} else {
2596
2597
    }*/
2598
}
2599
2600
/**
2601
 * Decrypt a file.
2602
 *
2603
 * @param string $fileName File name
2604
 * @param string $filePath Path to file
2605
 * @param string $key      Key to use
2606
 *
2607
 * @return string
2608
 */
2609
function decryptFile(string $fileName, string $filePath, string $key): string
2610
{
2611
    if (! defined('FILE_BUFFER_SIZE')) {
2612
        define('FILE_BUFFER_SIZE', 128 * 1024);
2613
    }
2614
    
2615
    // Get file name
2616
    $fileName = base64_decode($fileName);
2617
2618
    //if (WIP === false) {
2619
        // Load classes
2620
        $cipher = new Crypt_AES();
2621
        // Set the object key
2622
        $cipher->setPassword(base64_decode($key));
2623
        // Prevent against out of memory
2624
        $cipher->enableContinuousBuffer();
2625
        $cipher->disablePadding();
2626
        // Get file content
2627
        $ciphertext = file_get_contents($filePath . '/' . TP_FILE_PREFIX . $fileName);
2628
        // Decrypt file content and return
2629
        return base64_encode($cipher->decrypt($ciphertext));
2630
    /*} else {
2631
        
2632
    }*/
2633
}
2634
2635
/**
2636
 * Generate a simple password
2637
 *
2638
 * @param int $length Length of string
2639
 * @param bool $symbolsincluded Allow symbols
2640
 *
2641
 * @return string
2642
 */
2643
function generateQuickPassword(int $length = 16, bool $symbolsincluded = true): string
2644
{
2645
    // Generate new user password
2646
    $small_letters = range('a', 'z');
2647
    $big_letters = range('A', 'Z');
2648
    $digits = range(0, 9);
2649
    $symbols = $symbolsincluded === true ?
2650
        ['#', '_', '-', '@', '$', '+', '!'] : [];
2651
    $res = array_merge($small_letters, $big_letters, $digits, $symbols);
2652
    $count = count($res);
2653
    // first variant
2654
2655
    $random_string = '';
2656
    for ($i = 0; $i < $length; ++$i) {
2657
        $random_string .= $res[random_int(0, $count - 1)];
2658
    }
2659
2660
    return $random_string;
2661
}
2662
2663
/**
2664
 * Permit to store the sharekey of an object for users.
2665
 *
2666
 * @param string $object_name             Type for table selection
2667
 * @param int    $post_folder_is_personal Personal
2668
 * @param int    $post_folder_id          Folder
2669
 * @param int    $post_object_id          Object
2670
 * @param string $objectKey               Object key
2671
 * @param array  $SETTINGS                Teampass settings
2672
 * @param int    $user_id                 User ID if needed
2673
 * @param bool   $onlyForUser                 User ID if needed
2674
 * @param bool   $deleteAll                 User ID if needed
2675
 * @param array  $objectKeyArray                 User ID if needed
2676
 *
2677
 * @return void
2678
 */
2679
function storeUsersShareKey(
2680
    string $object_name,
2681
    int $post_folder_is_personal,
2682
    int $post_folder_id,
2683
    int $post_object_id,
2684
    string $objectKey,
2685
    array $SETTINGS,
2686
    bool $onlyForUser = false,
2687
    bool $deleteAll = true,
2688
    array $objectKeyArray = []
2689
): void {
2690
    
2691
    $session = SessionManager::getSession();
2692
    loadClasses('DB');
2693
2694
    // Delete existing entries for this object
2695
    if ($deleteAll === true) {
2696
        DB::delete(
2697
            $object_name,
2698
            'object_id = %i',
2699
            $post_object_id
2700
        );
2701
    }
2702
    
2703
    if (
2704
        $onlyForUser === true || (int) $post_folder_is_personal === 1
2705
    ) {
2706
        // Only create the sharekey for a user
2707
        $user = DB::queryFirstRow(
2708
            'SELECT public_key
2709
            FROM ' . prefixTable('users') . '
2710
            WHERE id = ' . (int) $session->get('user-id') . '
2711
            AND public_key != ""'
2712
        );
2713
2714
        if (empty($objectKey) === false) {
2715
            DB::insert(
2716
                $object_name,
2717
                [
2718
                    'object_id' => (int) $post_object_id,
2719
                    'user_id' => (int) $session->get('user-id'),
2720
                    'share_key' => encryptUserObjectKey(
2721
                        $objectKey,
2722
                        $user['public_key']
2723
                    ),
2724
                ]
2725
            );
2726
        } else if (count($objectKeyArray) > 0) {
2727
            foreach ($objectKeyArray as $object) {
2728
                DB::insert(
2729
                    $object_name,
2730
                    [
2731
                        'object_id' => (int) $object['objectId'],
2732
                        'user_id' => (int) $session->get('user-id'),
2733
                        'share_key' => encryptUserObjectKey(
2734
                            $object['objectKey'],
2735
                            $user['public_key']
2736
                        ),
2737
                    ]
2738
                );
2739
            }
2740
        }
2741
    } else {
2742
        // Create sharekey for each user
2743
        //DB::debugmode(true);
2744
        $users = DB::query(
2745
            'SELECT id, public_key
2746
            FROM ' . prefixTable('users') . '
2747
            WHERE ' . ($onlyForUser === true ? 
0 ignored issues
show
introduced by
The condition $onlyForUser === true is always false.
Loading history...
2748
                'id IN ("' . TP_USER_ID . '","' . $session->get('user-id') . '") ' : 
2749
                'id NOT IN ("' . OTV_USER_ID . '","' . SSH_USER_ID . '","' . API_USER_ID . '") ') . '
2750
            AND public_key != ""'
2751
        );
2752
        //DB::debugmode(false);
2753
        foreach ($users as $user) {
2754
            // Insert in DB the new object key for this item by user
2755
            if (count($objectKeyArray) === 0) {
2756
                DB::insert(
2757
                    $object_name,
2758
                    [
2759
                        'object_id' => $post_object_id,
2760
                        'user_id' => (int) $user['id'],
2761
                        'share_key' => encryptUserObjectKey(
2762
                            $objectKey,
2763
                            $user['public_key']
2764
                        ),
2765
                    ]
2766
                );
2767
            } else {
2768
                foreach ($objectKeyArray as $object) {
2769
                    DB::insert(
2770
                        $object_name,
2771
                        [
2772
                            'object_id' => (int) $object['objectId'],
2773
                            'user_id' => (int) $user['id'],
2774
                            'share_key' => encryptUserObjectKey(
2775
                                $object['objectKey'],
2776
                                $user['public_key']
2777
                            ),
2778
                        ]
2779
                    );
2780
                }
2781
            }
2782
        }
2783
    }
2784
}
2785
2786
/**
2787
 * Is this string base64 encoded?
2788
 *
2789
 * @param string $str Encoded string?
2790
 *
2791
 * @return bool
2792
 */
2793
function isBase64(string $str): bool
2794
{
2795
    $str = (string) trim($str);
2796
    if (! isset($str[0])) {
2797
        return false;
2798
    }
2799
2800
    $base64String = (string) base64_decode($str, true);
2801
    if ($base64String && base64_encode($base64String) === $str) {
2802
        return true;
2803
    }
2804
2805
    return false;
2806
}
2807
2808
/**
2809
 * Undocumented function
2810
 *
2811
 * @param string $field Parameter
2812
 *
2813
 * @return array|bool|resource|string
2814
 */
2815
function filterString(string $field)
2816
{
2817
    // Sanitize string
2818
    $field = filter_var(trim($field), FILTER_SANITIZE_FULL_SPECIAL_CHARS);
2819
    if (empty($field) === false) {
2820
        // Load AntiXSS
2821
        $antiXss = new AntiXSS();
2822
        // Return
2823
        return $antiXss->xss_clean($field);
2824
    }
2825
2826
    return false;
2827
}
2828
2829
/**
2830
 * CHeck if provided credentials are allowed on server
2831
 *
2832
 * @param string $login    User Login
2833
 * @param string $password User Pwd
2834
 * @param array  $SETTINGS Teampass settings
2835
 *
2836
 * @return bool
2837
 */
2838
function ldapCheckUserPassword(string $login, string $password, array $SETTINGS): bool
2839
{
2840
    // Build ldap configuration array
2841
    $config = [
2842
        // Mandatory Configuration Options
2843
        'hosts' => [$SETTINGS['ldap_hosts']],
2844
        'base_dn' => $SETTINGS['ldap_bdn'],
2845
        'username' => $SETTINGS['ldap_username'],
2846
        'password' => $SETTINGS['ldap_password'],
2847
2848
        // Optional Configuration Options
2849
        'port' => $SETTINGS['ldap_port'],
2850
        'use_ssl' => (int) $SETTINGS['ldap_ssl'] === 1 ? true : false,
2851
        'use_tls' => (int) $SETTINGS['ldap_tls'] === 1 ? true : false,
2852
        'version' => 3,
2853
        'timeout' => 5,
2854
        'follow_referrals' => false,
2855
2856
        // Custom LDAP Options
2857
        'options' => [
2858
            // See: http://php.net/ldap_set_option
2859
            LDAP_OPT_X_TLS_REQUIRE_CERT => (isset($SETTINGS['ldap_tls_certiface_check']) ? $SETTINGS['ldap_tls_certiface_check'] : LDAP_OPT_X_TLS_HARD),
2860
        ],
2861
    ];
2862
    
2863
    $connection = new Connection($config);
2864
    // Connect to LDAP
2865
    try {
2866
        $connection->connect();
2867
    } catch (\LdapRecord\Auth\BindException $e) {
2868
        $error = $e->getDetailedError();
2869
        echo 'Error : '.$error->getErrorCode().' - '.$error->getErrorMessage(). '<br>'.$error->getDiagnosticMessage();
2870
        return false;
2871
    }
2872
2873
    // Authenticate user
2874
    try {
2875
        if ($SETTINGS['ldap_type'] === 'ActiveDirectory') {
2876
            $connection->auth()->attempt($login, $password, $stayAuthenticated = true);
2877
        } else {
2878
            $connection->auth()->attempt($SETTINGS['ldap_user_attribute'].'='.$login.','.(isset($SETTINGS['ldap_dn_additional_user_dn']) && !empty($SETTINGS['ldap_dn_additional_user_dn']) ? $SETTINGS['ldap_dn_additional_user_dn'].',' : '').$SETTINGS['ldap_bdn'], $password, $stayAuthenticated = true);
2879
        }
2880
    } catch (\LdapRecord\Auth\BindException $e) {
2881
        $error = $e->getDetailedError();
2882
        echo 'Error : '.$error->getErrorCode().' - '.$error->getErrorMessage(). '<br>'.$error->getDiagnosticMessage();
2883
        return false;
2884
    }
2885
2886
    return true;
2887
}
2888
2889
/**
2890
 * Removes from DB all sharekeys of this user
2891
 *
2892
 * @param int $userId User's id
2893
 * @param array   $SETTINGS Teampass settings
2894
 *
2895
 * @return bool
2896
 */
2897
function deleteUserObjetsKeys(int $userId, array $SETTINGS = []): bool
2898
{
2899
    // Load class DB
2900
    loadClasses('DB');
2901
2902
    // Remove all item sharekeys items
2903
    // expect if personal item
2904
    DB::delete(
2905
        prefixTable('sharekeys_items'),
2906
        'user_id = %i AND object_id NOT IN (SELECT i.id FROM ' . prefixTable('items') . ' AS i WHERE i.perso = 1)',
2907
        $userId
2908
    );
2909
    // Remove all item sharekeys files
2910
    DB::delete(
2911
        prefixTable('sharekeys_files'),
2912
        'user_id = %i AND object_id NOT IN (
2913
            SELECT f.id 
2914
            FROM ' . prefixTable('items') . ' AS i 
2915
            INNER JOIN ' . prefixTable('files') . ' AS f ON f.id_item = i.id
2916
            WHERE i.perso = 1
2917
        )',
2918
        $userId
2919
    );
2920
    // Remove all item sharekeys fields
2921
    DB::delete(
2922
        prefixTable('sharekeys_fields'),
2923
        'user_id = %i AND object_id NOT IN (
2924
            SELECT c.id 
2925
            FROM ' . prefixTable('items') . ' AS i 
2926
            INNER JOIN ' . prefixTable('categories_items') . ' AS c ON c.item_id = i.id
2927
            WHERE i.perso = 1
2928
        )',
2929
        $userId
2930
    );
2931
    // Remove all item sharekeys logs
2932
    DB::delete(
2933
        prefixTable('sharekeys_logs'),
2934
        'user_id = %i AND object_id NOT IN (SELECT i.id FROM ' . prefixTable('items') . ' AS i WHERE i.perso = 1)',
2935
        $userId
2936
    );
2937
    // Remove all item sharekeys suggestions
2938
    DB::delete(
2939
        prefixTable('sharekeys_suggestions'),
2940
        'user_id = %i AND object_id NOT IN (SELECT i.id FROM ' . prefixTable('items') . ' AS i WHERE i.perso = 1)',
2941
        $userId
2942
    );
2943
    return false;
2944
}
2945
2946
/**
2947
 * Manage list of timezones   $SETTINGS Teampass settings
2948
 *
2949
 * @return array
2950
 */
2951
function timezone_list()
2952
{
2953
    static $timezones = null;
2954
    if ($timezones === null) {
2955
        $timezones = [];
2956
        $offsets = [];
2957
        $now = new DateTime('now', new DateTimeZone('UTC'));
2958
        foreach (DateTimeZone::listIdentifiers() as $timezone) {
2959
            $now->setTimezone(new DateTimeZone($timezone));
2960
            $offsets[] = $offset = $now->getOffset();
2961
            $timezones[$timezone] = '(' . format_GMT_offset($offset) . ') ' . format_timezone_name($timezone);
2962
        }
2963
2964
        array_multisort($offsets, $timezones);
2965
    }
2966
2967
    return $timezones;
2968
}
2969
2970
/**
2971
 * Provide timezone offset
2972
 *
2973
 * @param int $offset Timezone offset
2974
 *
2975
 * @return string
2976
 */
2977
function format_GMT_offset($offset): string
2978
{
2979
    $hours = intval($offset / 3600);
2980
    $minutes = abs(intval($offset % 3600 / 60));
2981
    return 'GMT' . ($offset ? sprintf('%+03d:%02d', $hours, $minutes) : '');
2982
}
2983
2984
/**
2985
 * Provides timezone name
2986
 *
2987
 * @param string $name Timezone name
2988
 *
2989
 * @return string
2990
 */
2991
function format_timezone_name($name): string
2992
{
2993
    $name = str_replace('/', ', ', $name);
2994
    $name = str_replace('_', ' ', $name);
2995
2996
    return str_replace('St ', 'St. ', $name);
2997
}
2998
2999
/**
3000
 * Provides info if user should use MFA based on roles
3001
 *
3002
 * @param string $userRolesIds  User roles ids
3003
 * @param string $mfaRoles      Roles for which MFA is requested
3004
 *
3005
 * @return bool
3006
 */
3007
function mfa_auth_requested_roles(string $userRolesIds, string $mfaRoles): bool
3008
{
3009
    if (empty($mfaRoles) === true) {
3010
        return true;
3011
    }
3012
3013
    $mfaRoles = array_values(json_decode($mfaRoles, true));
3014
    $userRolesIds = array_filter(explode(';', $userRolesIds));
3015
    if (count($mfaRoles) === 0 || count(array_intersect($mfaRoles, $userRolesIds)) > 0) {
3016
        return true;
3017
    }
3018
3019
    return false;
3020
}
3021
3022
/**
3023
 * Permits to clean a string for export purpose
3024
 *
3025
 * @param string $text
3026
 * @param bool $emptyCheckOnly
3027
 * 
3028
 * @return string
3029
 */
3030
function cleanStringForExport(string $text, bool $emptyCheckOnly = false): string
3031
{
3032
    if (is_null($text) === true || empty($text) === true) {
3033
        return '';
3034
    }
3035
    // only expected to check if $text was empty
3036
    elseif ($emptyCheckOnly === true) {
3037
        return $text;
3038
    }
3039
3040
    return strip_tags(
3041
        cleanString(
3042
            html_entity_decode($text, ENT_QUOTES | ENT_XHTML, 'UTF-8'),
3043
            true)
3044
        );
3045
}
3046
3047
/**
3048
 * Permits to check if user ID is valid
3049
 *
3050
 * @param integer $post_user_id
3051
 * @return bool
3052
 */
3053
function isUserIdValid($userId): bool
3054
{
3055
    if (is_null($userId) === false
3056
        && isset($userId) === true
3057
        && empty($userId) === false
3058
    ) {
3059
        return true;
3060
    }
3061
    return false;
3062
}
3063
3064
/**
3065
 * Check if a key exists and if its value equal the one expected
3066
 *
3067
 * @param string $key
3068
 * @param integer|string $value
3069
 * @param array $array
3070
 * 
3071
 * @return boolean
3072
 */
3073
function isKeyExistingAndEqual(
3074
    string $key,
3075
    /*PHP8 - integer|string*/$value,
3076
    array $array
3077
): bool
3078
{
3079
    if (isset($array[$key]) === true
3080
        && (is_int($value) === true ?
3081
            (int) $array[$key] === $value :
3082
            (string) $array[$key] === $value)
3083
    ) {
3084
        return true;
3085
    }
3086
    return false;
3087
}
3088
3089
/**
3090
 * Check if a variable is not set or equal to a value
3091
 *
3092
 * @param string|null $var
3093
 * @param integer|string $value
3094
 * 
3095
 * @return boolean
3096
 */
3097
function isKeyNotSetOrEqual(
3098
    /*PHP8 - string|null*/$var,
3099
    /*PHP8 - integer|string*/$value
3100
): bool
3101
{
3102
    if (isset($var) === false
3103
        || (is_int($value) === true ?
3104
            (int) $var === $value :
3105
            (string) $var === $value)
3106
    ) {
3107
        return true;
3108
    }
3109
    return false;
3110
}
3111
3112
/**
3113
 * Check if a key exists and if its value < to the one expected
3114
 *
3115
 * @param string $key
3116
 * @param integer $value
3117
 * @param array $array
3118
 * 
3119
 * @return boolean
3120
 */
3121
function isKeyExistingAndInferior(string $key, int $value, array $array): bool
3122
{
3123
    if (isset($array[$key]) === true && (int) $array[$key] < $value) {
3124
        return true;
3125
    }
3126
    return false;
3127
}
3128
3129
/**
3130
 * Check if a key exists and if its value > to the one expected
3131
 *
3132
 * @param string $key
3133
 * @param integer $value
3134
 * @param array $array
3135
 * 
3136
 * @return boolean
3137
 */
3138
function isKeyExistingAndSuperior(string $key, int $value, array $array): bool
3139
{
3140
    if (isset($array[$key]) === true && (int) $array[$key] > $value) {
3141
        return true;
3142
    }
3143
    return false;
3144
}
3145
3146
/**
3147
 * Check if values in array are set
3148
 * Return true if all set
3149
 * Return false if one of them is not set
3150
 *
3151
 * @param array $arrayOfValues
3152
 * @return boolean
3153
 */
3154
function isSetArrayOfValues(array $arrayOfValues): bool
3155
{
3156
    foreach($arrayOfValues as $value) {
3157
        if (isset($value) === false) {
3158
            return false;
3159
        }
3160
    }
3161
    return true;
3162
}
3163
3164
/**
3165
 * Check if values in array are set
3166
 * Return true if all set
3167
 * Return false if one of them is not set
3168
 *
3169
 * @param array $arrayOfValues
3170
 * @param integer|string $value
3171
 * @return boolean
3172
 */
3173
function isArrayOfVarsEqualToValue(
3174
    array $arrayOfVars,
3175
    /*PHP8 - integer|string*/$value
3176
) : bool
3177
{
3178
    foreach($arrayOfVars as $variable) {
3179
        if ($variable !== $value) {
3180
            return false;
3181
        }
3182
    }
3183
    return true;
3184
}
3185
3186
/**
3187
 * Checks if at least one variable in array is equal to value
3188
 *
3189
 * @param array $arrayOfValues
3190
 * @param integer|string $value
3191
 * @return boolean
3192
 */
3193
function isOneVarOfArrayEqualToValue(
3194
    array $arrayOfVars,
3195
    /*PHP8 - integer|string*/$value
3196
) : bool
3197
{
3198
    foreach($arrayOfVars as $variable) {
3199
        if ($variable === $value) {
3200
            return true;
3201
        }
3202
    }
3203
    return false;
3204
}
3205
3206
/**
3207
 * Checks is value is null, not set OR empty
3208
 *
3209
 * @param string|int|null $value
3210
 * @return boolean
3211
 */
3212
function isValueSetNullEmpty(/*PHP8 - string|int|null*/ $value) : bool
3213
{
3214
    if (is_null($value) === true || isset($value) === false || empty($value) === true) {
3215
        return true;
3216
    }
3217
    return false;
3218
}
3219
3220
/**
3221
 * Checks if value is set and if empty is equal to passed boolean
3222
 *
3223
 * @param string|int $value
3224
 * @param boolean $boolean
3225
 * @return boolean
3226
 */
3227
function isValueSetEmpty($value, $boolean = true) : bool
3228
{
3229
    if (isset($value) === true && empty($value) === $boolean) {
3230
        return true;
3231
    }
3232
    return false;
3233
}
3234
3235
/**
3236
 * Ensure Complexity is translated
3237
 *
3238
 * @return void
3239
 */
3240
function defineComplexity() : void
3241
{
3242
    // Load user's language
3243
    $lang = new Language(); 
3244
    
3245
    if (defined('TP_PW_COMPLEXITY') === false) {
3246
        define(
3247
            'TP_PW_COMPLEXITY',
3248
            [
3249
                TP_PW_STRENGTH_1 => array(TP_PW_STRENGTH_1, $lang->get('complex_level1'), 'fas fa-thermometer-empty text-danger'),
3250
                TP_PW_STRENGTH_2 => array(TP_PW_STRENGTH_2, $lang->get('complex_level2'), 'fas fa-thermometer-quarter text-warning'),
3251
                TP_PW_STRENGTH_3 => array(TP_PW_STRENGTH_3, $lang->get('complex_level3'), 'fas fa-thermometer-half text-warning'),
3252
                TP_PW_STRENGTH_4 => array(TP_PW_STRENGTH_4, $lang->get('complex_level4'), 'fas fa-thermometer-three-quarters text-success'),
3253
                TP_PW_STRENGTH_5 => array(TP_PW_STRENGTH_5, $lang->get('complex_level5'), 'fas fa-thermometer-full text-success'),
3254
            ]
3255
        );
3256
    }
3257
}
3258
3259
/**
3260
 * Uses Sanitizer to perform data sanitization
3261
 *
3262
 * @param array     $data
3263
 * @param array     $filters
3264
 * @return array|string
3265
 */
3266
function dataSanitizer(array $data, array $filters): array|string
3267
{
3268
    // Load Sanitizer library
3269
    $sanitizer = new Sanitizer($data, $filters);
3270
3271
    // Load AntiXSS
3272
    $antiXss = new AntiXSS();
3273
3274
    // Sanitize post and get variables
3275
    return $antiXss->xss_clean($sanitizer->sanitize());
3276
}
3277
3278
/**
3279
 * Permits to manage the cache tree for a user
3280
 *
3281
 * @param integer $user_id
3282
 * @param string $data
3283
 * @param array $SETTINGS
3284
 * @param string $field_update
3285
 * @return void
3286
 */
3287
function cacheTreeUserHandler(int $user_id, string $data, array $SETTINGS, string $field_update = '')
3288
{
3289
    // Load class DB
3290
    loadClasses('DB');
3291
3292
    // Exists ?
3293
    $userCacheId = DB::queryfirstrow(
3294
        'SELECT increment_id
3295
        FROM ' . prefixTable('cache_tree') . '
3296
        WHERE user_id = %i',
3297
        $user_id
3298
    );
3299
    
3300
    if (is_null($userCacheId) === true || count($userCacheId) === 0) {
3301
        // insert in table
3302
        DB::insert(
3303
            prefixTable('cache_tree'),
3304
            array(
3305
                'data' => $data,
3306
                'timestamp' => time(),
3307
                'user_id' => $user_id,
3308
                'visible_folders' => '',
3309
            )
3310
        );
3311
    } else {
3312
        if (empty($field_update) === true) {
3313
            DB::update(
3314
                prefixTable('cache_tree'),
3315
                [
3316
                    'timestamp' => time(),
3317
                    'data' => $data,
3318
                ],
3319
                'increment_id = %i',
3320
                $userCacheId['increment_id']
3321
            );
3322
        /* USELESS
3323
        } else {
3324
            DB::update(
3325
                prefixTable('cache_tree'),
3326
                [
3327
                    $field_update => $data,
3328
                ],
3329
                'increment_id = %i',
3330
                $userCacheId['increment_id']
3331
            );*/
3332
        }
3333
    }
3334
}
3335
3336
/**
3337
 * Permits to calculate a %
3338
 *
3339
 * @param float $nombre
3340
 * @param float $total
3341
 * @param float $pourcentage
3342
 * @return float
3343
 */
3344
function pourcentage(float $nombre, float $total, float $pourcentage): float
3345
{ 
3346
    $resultat = ($nombre/$total) * $pourcentage;
3347
    return round($resultat);
3348
}
3349
3350
/**
3351
 * Load the folders list from the cache
3352
 *
3353
 * @param string $fieldName
3354
 * @param string $sessionName
3355
 * @param boolean $forceRefresh
3356
 * @return array
3357
 */
3358
function loadFoldersListByCache(
3359
    string $fieldName,
3360
    string $sessionName,
3361
    bool $forceRefresh = false
3362
): array
3363
{
3364
    // Case when refresh is EXPECTED / MANDATORY
3365
    if ($forceRefresh === true) {
3366
        return [
3367
            'state' => false,
3368
            'data' => [],
3369
        ];
3370
    }
3371
    
3372
    $session = SessionManager::getSession();
3373
3374
    // Get last folder update
3375
    $lastFolderChange = DB::queryfirstrow(
3376
        'SELECT valeur FROM ' . prefixTable('misc') . '
3377
        WHERE type = %s AND intitule = %s',
3378
        'timestamp',
3379
        'last_folder_change'
3380
    );
3381
    if (DB::count() === 0) {
3382
        $lastFolderChange['valeur'] = 0;
3383
    }
3384
3385
    // Case when an update in the tree has been done
3386
    // Refresh is then mandatory
3387
    if ((int) $lastFolderChange['valeur'] > (int) (null !== $session->get('user-tree_last_refresh_timestamp') ? $session->get('user-tree_last_refresh_timestamp') : 0)) {
3388
        return [
3389
            'state' => false,
3390
            'data' => [],
3391
        ];
3392
    }
3393
3394
    // Does this user has the tree structure in session?
3395
    // If yes then use it
3396
    if (count(null !== $session->get('user-folders_list') ? $session->get('user-folders_list') : []) > 0) {
3397
        return [
3398
            'state' => true,
3399
            'data' => json_encode($session->get('user-folders_list')),
3400
        ];
3401
    }
3402
3403
    // Does this user has a tree cache
3404
    $userCacheTree = DB::queryfirstrow(
3405
        'SELECT '.$fieldName.'
3406
        FROM ' . prefixTable('cache_tree') . '
3407
        WHERE user_id = %i',
3408
        $session->get('user-id')
3409
    );
3410
    if (empty($userCacheTree[$fieldName]) === false && $userCacheTree[$fieldName] !== '[]') {
3411
        SessionManager::addRemoveFromSessionAssociativeArray(
3412
            'user-folders_list',
3413
            [$userCacheTree[$fieldName]],
3414
            'add'
3415
        );
3416
        return [
3417
            'state' => true,
3418
            'data' => $userCacheTree[$fieldName],
3419
        ];
3420
    }
3421
3422
    return [
3423
        'state' => false,
3424
        'data' => [],
3425
    ];
3426
}
3427
3428
3429
/**
3430
 * Permits to refresh the categories of folders
3431
 *
3432
 * @param array $folderIds
3433
 * @return void
3434
 */
3435
function handleFoldersCategories(
3436
    array $folderIds
3437
)
3438
{
3439
    // Load class DB
3440
    loadClasses('DB');
3441
3442
    $arr_data = array();
3443
3444
    // force full list of folders
3445
    if (count($folderIds) === 0) {
3446
        $folderIds = DB::queryFirstColumn(
3447
            'SELECT id
3448
            FROM ' . prefixTable('nested_tree') . '
3449
            WHERE personal_folder=%i',
3450
            0
3451
        );
3452
    }
3453
3454
    // Get complexity
3455
    defineComplexity();
3456
3457
    // update
3458
    foreach ($folderIds as $folder) {
3459
        // Do we have Categories
3460
        // get list of associated Categories
3461
        $arrCatList = array();
3462
        $rows_tmp = DB::query(
3463
            'SELECT c.id, c.title, c.level, c.type, c.masked, c.order, c.encrypted_data, c.role_visibility, c.is_mandatory,
3464
            f.id_category AS category_id
3465
            FROM ' . prefixTable('categories_folders') . ' AS f
3466
            INNER JOIN ' . prefixTable('categories') . ' AS c ON (f.id_category = c.parent_id)
3467
            WHERE id_folder=%i',
3468
            $folder
3469
        );
3470
        if (DB::count() > 0) {
3471
            foreach ($rows_tmp as $row) {
3472
                $arrCatList[$row['id']] = array(
3473
                    'id' => $row['id'],
3474
                    'title' => $row['title'],
3475
                    'level' => $row['level'],
3476
                    'type' => $row['type'],
3477
                    'masked' => $row['masked'],
3478
                    'order' => $row['order'],
3479
                    'encrypted_data' => $row['encrypted_data'],
3480
                    'role_visibility' => $row['role_visibility'],
3481
                    'is_mandatory' => $row['is_mandatory'],
3482
                    'category_id' => $row['category_id'],
3483
                );
3484
            }
3485
        }
3486
        $arr_data['categories'] = $arrCatList;
3487
3488
        // Now get complexity
3489
        $valTemp = '';
3490
        $data = DB::queryFirstRow(
3491
            'SELECT valeur
3492
            FROM ' . prefixTable('misc') . '
3493
            WHERE type = %s AND intitule=%i',
3494
            'complex',
3495
            $folder
3496
        );
3497
        if (DB::count() > 0 && empty($data['valeur']) === false) {
3498
            $valTemp = array(
3499
                'value' => $data['valeur'],
3500
                'text' => TP_PW_COMPLEXITY[$data['valeur']][1],
3501
            );
3502
        }
3503
        $arr_data['complexity'] = $valTemp;
3504
3505
        // Now get Roles
3506
        $valTemp = '';
3507
        $rows_tmp = DB::query(
3508
            'SELECT t.title
3509
            FROM ' . prefixTable('roles_values') . ' as v
3510
            INNER JOIN ' . prefixTable('roles_title') . ' as t ON (v.role_id = t.id)
3511
            WHERE v.folder_id = %i
3512
            GROUP BY title',
3513
            $folder
3514
        );
3515
        foreach ($rows_tmp as $record) {
3516
            $valTemp .= (empty($valTemp) === true ? '' : ' - ') . $record['title'];
3517
        }
3518
        $arr_data['visibilityRoles'] = $valTemp;
3519
3520
        // now save in DB
3521
        DB::update(
3522
            prefixTable('nested_tree'),
3523
            array(
3524
                'categories' => json_encode($arr_data),
3525
            ),
3526
            'id = %i',
3527
            $folder
3528
        );
3529
    }
3530
}
3531
3532
/**
3533
 * List all users that have specific roles
3534
 *
3535
 * @param array $roles
3536
 * @return array
3537
 */
3538
function getUsersWithRoles(
3539
    array $roles
3540
): array
3541
{
3542
    $session = SessionManager::getSession();
3543
    $arrUsers = array();
3544
3545
    foreach ($roles as $role) {
3546
        // loop on users and check if user has this role
3547
        $rows = DB::query(
3548
            'SELECT id, fonction_id
3549
            FROM ' . prefixTable('users') . '
3550
            WHERE id != %i AND admin = 0 AND fonction_id IS NOT NULL AND fonction_id != ""',
3551
            $session->get('user-id')
3552
        );
3553
        foreach ($rows as $user) {
3554
            $userRoles = is_null($user['fonction_id']) === false && empty($user['fonction_id']) === false ? explode(';', $user['fonction_id']) : [];
3555
            if (in_array($role, $userRoles, true) === true) {
3556
                array_push($arrUsers, $user['id']);
3557
            }
3558
        }
3559
    }
3560
3561
    return $arrUsers;
3562
}
3563
3564
3565
/**
3566
 * Get all users informations
3567
 *
3568
 * @param integer $userId
3569
 * @return array
3570
 */
3571
function getFullUserInfos(
3572
    int $userId
3573
): array
3574
{
3575
    if (empty($userId) === true) {
3576
        return array();
3577
    }
3578
3579
    $val = DB::queryfirstrow(
3580
        'SELECT *
3581
        FROM ' . prefixTable('users') . '
3582
        WHERE id = %i',
3583
        $userId
3584
    );
3585
3586
    return $val;
3587
}
3588
3589
/**
3590
 * Is required an upgrade
3591
 *
3592
 * @return boolean
3593
 */
3594
function upgradeRequired(): bool
3595
{
3596
    // Get settings.php
3597
    include_once __DIR__. '/../includes/config/settings.php';
3598
3599
    // Get timestamp in DB
3600
    $val = DB::queryfirstrow(
3601
        'SELECT valeur
3602
        FROM ' . prefixTable('misc') . '
3603
        WHERE type = %s AND intitule = %s',
3604
        'admin',
3605
        'upgrade_timestamp'
3606
    );
3607
    
3608
    // if not exists then error
3609
    if (is_null($val) === true || count($val) === 0 || defined('UPGRADE_MIN_DATE') === false) return true;
3610
3611
    // if empty or too old then error
3612
    if (empty($val['valeur']) === true || (int) $val['valeur'] < (int) UPGRADE_MIN_DATE) {
3613
        return true;
3614
    }
3615
3616
    return false;
3617
}
3618
3619
/**
3620
 * Permits to change the user keys on his demand
3621
 *
3622
 * @param integer $userId
3623
 * @param string $passwordClear
3624
 * @param integer $nbItemsToTreat
3625
 * @param string $encryptionKey
3626
 * @param boolean $deleteExistingKeys
3627
 * @param boolean $sendEmailToUser
3628
 * @param boolean $encryptWithUserPassword
3629
 * @param boolean $generate_user_new_password
3630
 * @param string $emailBody
3631
 * @param boolean $user_self_change
3632
 * @param string $recovery_public_key
3633
 * @param string $recovery_private_key
3634
 * @return string
3635
 */
3636
function handleUserKeys(
3637
    int $userId,
3638
    string $passwordClear,
3639
    int $nbItemsToTreat,
3640
    string $encryptionKey = '',
3641
    bool $deleteExistingKeys = false,
3642
    bool $sendEmailToUser = true,
3643
    bool $encryptWithUserPassword = false,
3644
    bool $generate_user_new_password = false,
3645
    string $emailBody = '',
3646
    bool $user_self_change = false,
3647
    string $recovery_public_key = '',
3648
    string $recovery_private_key = ''
3649
): string
3650
{
3651
    $session = SessionManager::getSession();
3652
    $lang = new Language(); 
3653
3654
    // prepapre background tasks for item keys generation        
3655
    $userTP = DB::queryFirstRow(
3656
        'SELECT pw, public_key, private_key
3657
        FROM ' . prefixTable('users') . '
3658
        WHERE id = %i',
3659
        TP_USER_ID
3660
    );
3661
    if (DB::count() === 0) {
3662
        return prepareExchangedData(
3663
            array(
3664
                'error' => true,
3665
                'message' => 'User not exists',
3666
            ),
3667
            'encode'
3668
        );
3669
    }
3670
3671
    // Do we need to generate new user password
3672
    if ($generate_user_new_password === true) {
3673
        // Generate a new password
3674
        $passwordClear = GenerateCryptKey(20, false, true, true, false, true);
3675
    }
3676
3677
    // Hash the password
3678
    $pwdlib = new PasswordLib();
3679
    $hashedPassword = $pwdlib->createPasswordHash($passwordClear);
3680
    if ($pwdlib->verifyPasswordHash($passwordClear, $hashedPassword) === false) {
3681
        return prepareExchangedData(
3682
            array(
3683
                'error' => true,
3684
                'message' => $lang->get('pw_hash_not_correct'),
3685
            ),
3686
            'encode'
3687
        );
3688
    }
3689
3690
    // Generate new keys
3691
    if ($user_self_change === true && empty($recovery_public_key) === false && empty($recovery_private_key) === false){
3692
        $userKeys = [
3693
            'public_key' => $recovery_public_key,
3694
            'private_key_clear' => $recovery_private_key,
3695
            'private_key' => encryptPrivateKey($passwordClear, $recovery_private_key),
3696
        ];
3697
    } else {
3698
        $userKeys = generateUserKeys($passwordClear);
3699
    }
3700
3701
    // Save in DB
3702
    DB::update(
3703
        prefixTable('users'),
3704
        array(
3705
            'pw' => $hashedPassword,
3706
            'public_key' => $userKeys['public_key'],
3707
            'private_key' => $userKeys['private_key'],
3708
        ),
3709
        'id=%i',
3710
        $userId
3711
    );
3712
3713
    // update session too
3714
    if ($userId === $session->get('user-id')) {
3715
        $session->set('user-private_key', $userKeys['private_key_clear']);
3716
        $session->set('user-public_key', $userKeys['public_key']);
3717
    }
3718
3719
    // Manage empty encryption key
3720
    // Let's take the user's password if asked and if no encryption key provided
3721
    $encryptionKey = $encryptWithUserPassword === true && empty($encryptionKey) === true ? $passwordClear : $encryptionKey;
3722
3723
    // Create process
3724
    DB::insert(
3725
        prefixTable('processes'),
3726
        array(
3727
            'created_at' => time(),
3728
            'process_type' => 'create_user_keys',
3729
            'arguments' => json_encode([
3730
                'new_user_id' => (int) $userId,
3731
                'new_user_pwd' => cryption($passwordClear, '','encrypt')['string'],
3732
                'new_user_code' => cryption(empty($encryptionKey) === true ? uniqidReal(20) : $encryptionKey, '','encrypt')['string'],
3733
                'owner_id' => (int) TP_USER_ID,
3734
                'creator_pwd' => $userTP['pw'],
3735
                'send_email' => $sendEmailToUser === true ? 1 : 0,
3736
                'otp_provided_new_value' => 1,
3737
                'email_body' => empty($emailBody) === true ? '' : $lang->get($emailBody),
3738
                'user_self_change' => $user_self_change === true ? 1 : 0,
3739
            ]),
3740
            'updated_at' => '',
3741
            'finished_at' => '',
3742
            'output' => '',
3743
        )
3744
    );
3745
    $processId = DB::insertId();
3746
3747
    // Delete existing keys
3748
    if ($deleteExistingKeys === true) {
3749
        deleteUserObjetsKeys(
3750
            (int) $userId,
3751
        );
3752
    }
3753
3754
    // Create tasks
3755
    createUserTasks($processId, $nbItemsToTreat);
3756
3757
    // update user's new status
3758
    DB::update(
3759
        prefixTable('users'),
3760
        [
3761
            'is_ready_for_usage' => 0,
3762
            'otp_provided' => 1,
3763
            'ongoing_process_id' => $processId,
3764
            'special' => 'generate-keys',
3765
        ],
3766
        'id=%i',
3767
        $userId
3768
    );
3769
3770
    return prepareExchangedData(
3771
        array(
3772
            'error' => false,
3773
            'message' => '',
3774
        ),
3775
        'encode'
3776
    );
3777
}
3778
3779
/**
3780
 * Permits to generate a new password for a user
3781
 *
3782
 * @param integer $processId
3783
 * @param integer $nbItemsToTreat
3784
 * @return void
3785
 
3786
 */
3787
function createUserTasks($processId, $nbItemsToTreat): void
3788
{
3789
    DB::insert(
3790
        prefixTable('processes_tasks'),
3791
        array(
3792
            'process_id' => $processId,
3793
            'created_at' => time(),
3794
            'task' => json_encode([
3795
                'step' => 'step0',
3796
                'index' => 0,
3797
                'nb' => $nbItemsToTreat,
3798
            ]),
3799
        )
3800
    );
3801
3802
    DB::insert(
3803
        prefixTable('processes_tasks'),
3804
        array(
3805
            'process_id' => $processId,
3806
            'created_at' => time(),
3807
            'task' => json_encode([
3808
                'step' => 'step10',
3809
                'index' => 0,
3810
                'nb' => $nbItemsToTreat,
3811
            ]),
3812
        )
3813
    );
3814
3815
    DB::insert(
3816
        prefixTable('processes_tasks'),
3817
        array(
3818
            'process_id' => $processId,
3819
            'created_at' => time(),
3820
            'task' => json_encode([
3821
                'step' => 'step20',
3822
                'index' => 0,
3823
                'nb' => $nbItemsToTreat,
3824
            ]),
3825
        )
3826
    );
3827
3828
    DB::insert(
3829
        prefixTable('processes_tasks'),
3830
        array(
3831
            'process_id' => $processId,
3832
            'created_at' => time(),
3833
            'task' => json_encode([
3834
                'step' => 'step30',
3835
                'index' => 0,
3836
                'nb' => $nbItemsToTreat,
3837
            ]),
3838
        )
3839
    );
3840
3841
    DB::insert(
3842
        prefixTable('processes_tasks'),
3843
        array(
3844
            'process_id' => $processId,
3845
            'created_at' => time(),
3846
            'task' => json_encode([
3847
                'step' => 'step40',
3848
                'index' => 0,
3849
                'nb' => $nbItemsToTreat,
3850
            ]),
3851
        )
3852
    );
3853
3854
    DB::insert(
3855
        prefixTable('processes_tasks'),
3856
        array(
3857
            'process_id' => $processId,
3858
            'created_at' => time(),
3859
            'task' => json_encode([
3860
                'step' => 'step50',
3861
                'index' => 0,
3862
                'nb' => $nbItemsToTreat,
3863
            ]),
3864
        )
3865
    );
3866
3867
    DB::insert(
3868
        prefixTable('processes_tasks'),
3869
        array(
3870
            'process_id' => $processId,
3871
            'created_at' => time(),
3872
            'task' => json_encode([
3873
                'step' => 'step60',
3874
                'index' => 0,
3875
                'nb' => $nbItemsToTreat,
3876
            ]),
3877
        )
3878
    );
3879
}
3880
3881
/**
3882
 * Permeits to check the consistency of date versus columns definition
3883
 *
3884
 * @param string $table
3885
 * @param array $dataFields
3886
 * @return array
3887
 */
3888
function validateDataFields(
3889
    string $table,
3890
    array $dataFields
3891
): array
3892
{
3893
    // Get table structure
3894
    $result = DB::query(
3895
        "SELECT `COLUMN_NAME`, `CHARACTER_MAXIMUM_LENGTH` FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = '%l' AND TABLE_NAME = '%l';",
3896
        DB_NAME,
3897
        $table
3898
    );
3899
3900
    foreach ($result as $row) {
3901
        $field = $row['COLUMN_NAME'];
3902
        $maxLength = is_null($row['CHARACTER_MAXIMUM_LENGTH']) === false ? (int) $row['CHARACTER_MAXIMUM_LENGTH'] : '';
3903
3904
        if (isset($dataFields[$field]) === true && is_array($dataFields[$field]) === false && empty($maxLength) === false) {
3905
            if (strlen((string) $dataFields[$field]) > $maxLength) {
3906
                return [
3907
                    'state' => false,
3908
                    'field' => $field,
3909
                    'maxLength' => $maxLength,
3910
                    'currentLength' => strlen((string) $dataFields[$field]),
3911
                ];
3912
            }
3913
        }
3914
    }
3915
    
3916
    return [
3917
        'state' => true,
3918
        'message' => '',
3919
    ];
3920
}
3921
3922
/**
3923
 * Adapt special characters sanitized during filter_var with option FILTER_SANITIZE_SPECIAL_CHARS operation
3924
 *
3925
 * @param string $string
3926
 * @return string
3927
 */
3928
function filterVarBack(string $string): string
3929
{
3930
    $arr = [
3931
        '&#060;' => '<',
3932
        '&#062;' => '>',
3933
        '&#034;' => '"',
3934
        '&#039;' => "'",
3935
        '&#038;' => '&',
3936
    ];
3937
3938
    foreach ($arr as $key => $value) {
3939
        $string = str_replace($key, $value, $string);
3940
    }
3941
3942
    return $string;
3943
}
3944
3945
/**
3946
 * 
3947
 */
3948
function storeTask(
3949
    string $taskName,
3950
    int $user_id,
3951
    int $is_personal_folder,
3952
    int $folder_destination_id,
3953
    int $item_id,
3954
    string $object_keys
3955
)
3956
{
3957
    if (in_array($taskName, ['item_copy', 'new_item', 'update_item'])) {
3958
        // Create process
3959
        DB::insert(
3960
            prefixTable('processes'),
3961
            array(
3962
                'created_at' => time(),
3963
                'process_type' => $taskName,
3964
                'arguments' => json_encode([
3965
                    'item_id' => $item_id,
3966
                    'object_key' => $object_keys,
3967
                ]),
3968
                'updated_at' => '',
3969
                'finished_at' => '',
3970
                'output' => '',
3971
                'item_id' => $item_id,
3972
            )
3973
        );
3974
        $processId = DB::insertId();
3975
3976
        // Create tasks
3977
        // 1- Create password sharekeys for users of this new ITEM
3978
        DB::insert(
3979
            prefixTable('processes_tasks'),
3980
            array(
3981
                'process_id' => $processId,
3982
                'created_at' => time(),
3983
                'task' => json_encode([
3984
                    'step' => 'create_users_pwd_key',
3985
                    'index' => 0,
3986
                ]),
3987
            )
3988
        );
3989
3990
        // 2- Create fields sharekeys for users of this new ITEM
3991
        DB::insert(
3992
            prefixTable('processes_tasks'),
3993
            array(
3994
                'process_id' => $processId,
3995
                'created_at' => time(),
3996
                'task' => json_encode([
3997
                    'step' => 'create_users_fields_key',
3998
                    'index' => 0,
3999
                ]),
4000
            )
4001
        );
4002
4003
        // 3- Create files sharekeys for users of this new ITEM
4004
        DB::insert(
4005
            prefixTable('processes_tasks'),
4006
            array(
4007
                'process_id' => $processId,
4008
                'created_at' => time(),
4009
                'task' => json_encode([
4010
                    'step' => 'create_users_files_key',
4011
                    'index' => 0,
4012
                ]),
4013
            )
4014
        );
4015
    }
4016
}
4017
4018
/**
4019
 * Return PHP binary path
4020
 *
4021
 * @return string
4022
 */
4023
function getPHPBinary(): string
4024
{
4025
    // Get PHP binary path
4026
    $phpBinaryFinder = new PhpExecutableFinder();
4027
    $phpBinaryPath = $phpBinaryFinder->find();
4028
    return $phpBinaryPath === false ? 'false' : $phpBinaryPath;
4029
}
4030
4031
4032
4033
/**
4034
 * Delete unnecessary keys for personal items
4035
 *
4036
 * @param boolean $allUsers
4037
 * @param integer $user_id
4038
 * @return void
4039
 */
4040
function purgeUnnecessaryKeys(bool $allUsers = true, int $user_id=0)
4041
{
4042
    if ($allUsers === true) {
4043
        // Load class DB
4044
        if (class_exists('DB') === false) {
4045
            loadClasses('DB');
4046
        }
4047
4048
        $users = DB::query(
4049
            'SELECT id
4050
            FROM ' . prefixTable('users') . '
4051
            WHERE id NOT IN ('.OTV_USER_ID.', '.TP_USER_ID.', '.SSH_USER_ID.', '.API_USER_ID.')
4052
            ORDER BY login ASC'
4053
        );
4054
        foreach ($users as $user) {
4055
            purgeUnnecessaryKeysForUser((int) $user['id']);
4056
        }
4057
    } else {
4058
        purgeUnnecessaryKeysForUser((int) $user_id);
4059
    }
4060
}
4061
4062
/**
4063
 * Delete unnecessary keys for personal items
4064
 *
4065
 * @param integer $user_id
4066
 * @return void
4067
 */
4068
function purgeUnnecessaryKeysForUser(int $user_id=0)
4069
{
4070
    if ($user_id === 0) {
4071
        return;
4072
    }
4073
4074
    // Load class DB
4075
    loadClasses('DB');
4076
4077
    $personalItems = DB::queryFirstColumn(
4078
        'SELECT id
4079
        FROM ' . prefixTable('items') . ' AS i
4080
        INNER JOIN ' . prefixTable('log_items') . ' AS li ON li.id_item = i.id
4081
        WHERE i.perso = 1 AND li.action = "at_creation" AND li.id_user IN (%i, '.TP_USER_ID.')',
4082
        $user_id
4083
    );
4084
    if (count($personalItems) > 0) {
4085
        // Item keys
4086
        DB::delete(
4087
            prefixTable('sharekeys_items'),
4088
            'object_id IN %li AND user_id NOT IN (%i, '.TP_USER_ID.')',
4089
            $personalItems,
4090
            $user_id
4091
        );
4092
        // Files keys
4093
        DB::delete(
4094
            prefixTable('sharekeys_files'),
4095
            'object_id IN %li AND user_id NOT IN (%i, '.TP_USER_ID.')',
4096
            $personalItems,
4097
            $user_id
4098
        );
4099
        // Fields keys
4100
        DB::delete(
4101
            prefixTable('sharekeys_fields'),
4102
            'object_id IN %li AND user_id NOT IN (%i, '.TP_USER_ID.')',
4103
            $personalItems,
4104
            $user_id
4105
        );
4106
        // Logs keys
4107
        DB::delete(
4108
            prefixTable('sharekeys_logs'),
4109
            'object_id IN %li AND user_id NOT IN (%i, '.TP_USER_ID.')',
4110
            $personalItems,
4111
            $user_id
4112
        );
4113
    }
4114
}
4115
4116
/**
4117
 * Generate recovery keys file
4118
 *
4119
 * @param integer $userId
4120
 * @param array $SETTINGS
4121
 * @return string
4122
 */
4123
function handleUserRecoveryKeysDownload(int $userId, array $SETTINGS):string
4124
{
4125
    $session = SessionManager::getSession();
4126
    // Check if user exists
4127
    $userInfo = DB::queryFirstRow(
4128
        'SELECT pw, public_key, private_key, login, name
4129
        FROM ' . prefixTable('users') . '
4130
        WHERE id = %i',
4131
        $userId
4132
    );
4133
4134
    if (DB::count() > 0) {
4135
        $now = (int) time();
4136
4137
        // Prepare file content
4138
        $export_value = file_get_contents(__DIR__."/../includes/core/teampass_ascii.txt")."\n".
4139
            "Generation date: ".date($SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'], $now)."\n\n".
4140
            "RECOVERY KEYS - Not to be shared - To be store safely\n\n".
4141
            "Public Key:\n".$userInfo['public_key']."\n\n".
4142
            "Private Key:\n".decryptPrivateKey($session->get('user-password'), $userInfo['private_key'])."\n\n";
4143
4144
        // Update user's keys_recovery_time
4145
        DB::update(
4146
            prefixTable('users'),
4147
            [
4148
                'keys_recovery_time' => $now,
4149
            ],
4150
            'id=%i',
4151
            $userId
4152
        );
4153
        $session->set('user-keys_recovery_time', $now);
4154
4155
        //Log into DB the user's disconnection
4156
        logEvents($SETTINGS, 'user_mngt', 'at_user_keys_download', (string) $userId, $userInfo['login']);
4157
        
4158
        // Return data
4159
        return prepareExchangedData(
4160
            array(
4161
                'error' => false,
4162
                'datetime' => date($SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'], $now),
4163
                'timestamp' => $now,
4164
                'content' => base64_encode($export_value),
4165
                'login' => $userInfo['login'],
4166
            ),
4167
            'encode'
4168
        );
4169
    }
4170
4171
    return prepareExchangedData(
4172
        array(
4173
            'error' => true,
4174
            'datetime' => '',
4175
        ),
4176
        'encode'
4177
    );
4178
}
4179
4180
/**
4181
 * Permits to load expected classes
4182
 *
4183
 * @param string $className
4184
 * @return void
4185
 */
4186
function loadClasses(string $className = ''): void
4187
{
4188
    require_once __DIR__. '/../includes/config/include.php';
4189
    require_once __DIR__. '/../includes/config/settings.php';
4190
    require_once __DIR__.'/../vendor/autoload.php';
4191
4192
    if (defined('DB_PASSWD_CLEAR') === false) {
4193
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, []));
4194
    }
4195
4196
    if (empty($className) === false) {
4197
        // Load class DB
4198
        if ((string) $className === 'DB') {
4199
            //Connect to DB
4200
            DB::$host = DB_HOST;
4201
            DB::$user = DB_USER;
4202
            DB::$password = DB_PASSWD_CLEAR;
4203
            DB::$dbName = DB_NAME;
4204
            DB::$port = DB_PORT;
4205
            DB::$encoding = DB_ENCODING;
4206
            DB::$ssl = DB_SSL;
4207
            DB::$connect_options = DB_CONNECT_OPTIONS;
4208
        }
4209
    }
4210
4211
}
4212
4213
/**
4214
 * Returns the page the user is visiting.
4215
 *
4216
 * @return string The page name
4217
 */
4218
function getCurrectPage($SETTINGS)
4219
{
4220
    
4221
    $request = Request::createFromGlobals();
4222
4223
    // Parse the url
4224
    parse_str(
4225
        substr(
4226
            (string) $request->server->get('REQUEST_URI'),
4227
            strpos((string) $request->server->get('REQUEST_URI'), '?') + 1
4228
        ),
4229
        $result
4230
    );
4231
4232
    return $result['page'];
4233
}
4234
4235
/**
4236
 * Permits to return value if set
4237
 *
4238
 * @param string|int $value
4239
 * @param string|int|null $retFalse
4240
 * @param string|int $retTrue
4241
 * @return mixed
4242
 */
4243
function returnIfSet($value, $retFalse = '', $retTrue = null): mixed
4244
{
4245
4246
    return isset($value) === true ? ($retTrue === null ? $value : $retTrue) : $retFalse;
4247
}
4248