Passed
Push — teampass_3.0 ( 23c792...d6f83d )
by Nils
04:32
created

encryptPrivateKey()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 2
dl 0
loc 12
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Teampass - a collaborative passwords manager.
5
 * ---
6
 * This library is distributed in the hope that it will be useful,
7
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
9
 * ---
10
 * @project   Teampass
11
 * @file      main.functions.php
12
 * ---
13
 * @author    Nils Laumaillé ([email protected])
14
 * @copyright 2009-2019 Teampass.net
15
 * @license   https://spdx.org/licenses/GPL-3.0-only.html#licenseText GPL-3.0
16
 * ---
17
 * @see       https://www.teampass.net
18
 */
19
20
use LdapRecord\Connection;
21
22
23
if (isset($_SESSION['CPM']) === false || (int) $_SESSION['CPM'] !== 1) {
24
    die('Hacking attempt...');
25
}
26
27
// Load config if $SETTINGS not defined
28
if (isset($SETTINGS['cpassman_dir']) === false || empty($SETTINGS['cpassman_dir']) === true) {
29
    if (file_exists('../includes/config/tp.config.php')) {
30
        include_once '../includes/config/tp.config.php';
31
    } elseif (file_exists('./includes/config/tp.config.php')) {
32
        include_once './includes/config/tp.config.php';
33
    } elseif (file_exists('../../includes/config/tp.config.php')) {
34
        include_once '../../includes/config/tp.config.php';
35
    } else {
36
        throw new Exception("Error file '/includes/config/tp.config.php' not exists", 1);
37
    }
38
}
39
40
header('Content-type: text/html; charset=utf-8');
41
header('Cache-Control: no-cache, must-revalidate');
42
43
/**
44
 * Convert language code to string.
45
 *
46
 * @param string $string String to get
47
 *
48
 * @return string
49
 */
50
function langHdl($string)
51
{
52
    if (empty($string) === true) {
53
        // Manage error
54
        return 'ERROR in language strings!';
55
    }
56
57
    // Load superglobal
58
    if (file_exists('../includes/libraries/protect/SuperGlobal/SuperGlobal.php')) {
59
        include_once '../includes/libraries/protect/SuperGlobal/SuperGlobal.php';
60
    } elseif (file_exists('./includes/libraries/protect/SuperGlobal/SuperGlobal.php')) {
61
        include_once './includes/libraries/protect/SuperGlobal/SuperGlobal.php';
62
    } elseif (file_exists('../../includes/libraries/protect/SuperGlobal/SuperGlobal.php')) {
63
        include_once '../../includes/libraries/protect/SuperGlobal/SuperGlobal.php';
64
    } else {
65
        throw new Exception("Error file '/includes/libraries/protect/SuperGlobal/SuperGlobal.php' not exists", 1);
66
    }
67
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
68
69
    // Get language string
70
    $session_language = $superGlobal->get(trim($string), 'SESSION', 'lang');
71
72
    if (isset($session_language) === false) {
73
        // Manage error
74
        return 'ERROR in language strings!';
75
    } else {
76
        return str_replace(
77
            array('"', "'"),
78
            array('&quot;', '&apos;'),
79
            $session_language
80
        );
81
    }
82
}
83
84
//Generate N# of random bits for use as salt
85
/**
86
 * Undocumented function.
87
 *
88
 * @param int $size Length
89
 *
90
 * @return string
91
 */
92
function getBits($size)
93
{
94
    $str = '';
95
    $var_x = $size + 10;
96
    for ($var_i = 0; $var_i < $var_x; ++$var_i) {
97
        $str .= base_convert(mt_rand(1, 36), 10, 36);
98
    }
99
100
    return substr($str, 0, $size);
101
}
102
103
//generate pbkdf2 compliant hash
104
function strHashPbkdf2($var_p, $var_s, $var_c, $var_kl, $var_a = 'sha256', $var_st = 0)
105
{
106
    $var_kb = $var_st + $var_kl; // Key blocks to compute
107
    $var_dk = ''; // Derived key
108
109
    for ($block = 1; $block <= $var_kb; ++$block) { // Create key
110
        $var_ib = $var_h = hash_hmac($var_a, $var_s . pack('N', $block), $var_p, true); // Initial hash for this block
111
        for ($var_i = 1; $var_i < $var_c; ++$var_i) { // Perform block iterations
112
            $var_ib ^= ($var_h = hash_hmac($var_a, $var_h, $var_p, true)); // XOR each iterate
113
        }
114
        $var_dk .= $var_ib; // Append iterated block
115
    }
116
117
    return substr($var_dk, $var_st, $var_kl); // Return derived key of correct length
118
}
119
120
/**
121
 * stringUtf8Decode().
122
 *
123
 * utf8_decode
124
 */
125
function stringUtf8Decode($string)
126
{
127
    return str_replace(' ', '+', utf8_decode($string));
128
}
129
130
/**
131
 * encryptOld().
132
 *
133
 * crypt a string
134
 *
135
 * @param string $text
136
 */
137
function encryptOld($text, $personalSalt = '')
138
{
139
    if (empty($personalSalt) === false) {
140
        return trim(
141
            base64_encode(
142
                /** @scrutinizer ignore-deprecated */ mcrypt_encrypt(
143
                    MCRYPT_RIJNDAEL_256,
144
                    $personalSalt,
145
                    $text,
146
                    MCRYPT_MODE_ECB,
147
                    /** @scrutinizer ignore-deprecated */ mcrypt_create_iv(
148
                        /** @scrutinizer ignore-deprecated */ mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB),
149
                        MCRYPT_RAND
150
                    )
151
                )
152
            )
153
        );
154
    }
155
156
    // If $personalSalt is not empty
157
    return trim(
158
        base64_encode(
159
            /** @scrutinizer ignore-deprecated */ mcrypt_encrypt(
160
                MCRYPT_RIJNDAEL_256,
161
                SALT,
162
                $text,
163
                MCRYPT_MODE_ECB,
164
                /** @scrutinizer ignore-deprecated */ mcrypt_create_iv(
165
                    /** @scrutinizer ignore-deprecated */ mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB),
166
                    MCRYPT_RAND
167
                )
168
            )
169
        )
170
    );
171
}
172
173
/**
174
 * decryptOld().
175
 *
176
 * decrypt a crypted string
177
 */
178
function decryptOld($text, $personalSalt = '')
179
{
180
    if (!empty($personalSalt)) {
181
        return trim(
182
            /** @scrutinizer ignore-deprecated */ mcrypt_decrypt(
183
                MCRYPT_RIJNDAEL_256,
184
                $personalSalt,
185
                base64_decode($text),
186
                MCRYPT_MODE_ECB,
187
                /** @scrutinizer ignore-deprecated */ mcrypt_create_iv(
188
                    /** @scrutinizer ignore-deprecated */ mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB),
189
                    MCRYPT_RAND
190
                )
191
            )
192
        );
193
    }
194
195
    // No personal SK
196
    return trim(
197
        /** @scrutinizer ignore-deprecated */ mcrypt_decrypt(
198
            MCRYPT_RIJNDAEL_256,
199
            SALT,
200
            base64_decode($text),
201
            MCRYPT_MODE_ECB,
202
            /** @scrutinizer ignore-deprecated */ mcrypt_create_iv(
203
                /** @scrutinizer ignore-deprecated */ mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB),
204
                MCRYPT_RAND
205
            )
206
        )
207
    );
208
}
209
210
/**
211
 * encrypt().
212
 *
213
 * crypt a string
214
 *
215
 * @param string $decrypted
216
 */
217
/*function encrypt($decrypted, $personalSalt = '')
218
{
219
    global $SETTINGS;
220
221
    if (isset($SETTINGS['cpassman_dir']) === false || empty($SETTINGS['cpassman_dir']) === true) {
222
        require_once '../includes/libraries/Encryption/PBKDF2/PasswordHash.php';
223
    } else {
224
        require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Encryption/PBKDF2/PasswordHash.php';
225
    }
226
227
    if (!empty($personalSalt)) {
228
        $staticSalt = $personalSalt;
229
    } else {
230
        $staticSalt = SALT;
231
    }
232
233
    //set our salt to a variable
234
    // Get 64 random bits for the salt for pbkdf2
235
    $pbkdf2Salt = getBits(64);
236
    // generate a pbkdf2 key to use for the encryption.
237
    $key = substr(pbkdf2('sha256', $staticSalt, $pbkdf2Salt, ITCOUNT, 16 + 32, true), 32, 16);
238
    // Build $init_vect and $ivBase64.  We use a block size of 256 bits (AES compliant)
239
    // and CTR mode.  (Note: ECB mode is inadequate as IV is not used.)
240
    $init_vect = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, 'ctr'), MCRYPT_RAND);
241
242
    //base64 trim
243
    if (strlen($ivBase64 = rtrim(base64_encode($init_vect), '=')) != 43) {
244
        return false;
245
    }
246
    // Encrypt $decrypted
247
    $encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $decrypted, 'ctr', $init_vect);
248
    // MAC the encrypted text
249
    $mac = hash_hmac('sha256', $encrypted, $staticSalt);
250
    // We're done!
251
    return base64_encode($ivBase64 . $encrypted . $mac . $pbkdf2Salt);
252
}
253
/*
254
255
/**
256
 * decrypt().
257
 *
258
 * decrypt a crypted string
259
 */
260
/*function decrypt($encrypted, $personalSalt = '')
261
{
262
    global $SETTINGS;
263
264
    if (isset($SETTINGS['cpassman_dir']) === false || empty($SETTINGS['cpassman_dir']) === true) {
265
        include_once '../includes/libraries/Encryption/PBKDF2/PasswordHash.php';
266
    } else {
267
        include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Encryption/PBKDF2/PasswordHash.php';
268
    }
269
270
    if (!empty($personalSalt)) {
271
        $staticSalt = $personalSalt;
272
    } else {
273
        $staticSalt = file_get_contents(SECUREPATH . '/teampass-seckey.txt');
274
    }
275
    //base64 decode the entire payload
276
    $encrypted = base64_decode($encrypted);
277
    // get the salt
278
    $pbkdf2Salt = substr($encrypted, -64);
279
    //remove the salt from the string
280
    $encrypted = substr($encrypted, 0, -64);
281
    $key = substr(pbkdf2('sha256', $staticSalt, $pbkdf2Salt, ITCOUNT, 16 + 32, true), 32, 16);
282
    // Retrieve $init_vect which is the first 22 characters plus ==, base64_decoded.
283
    $init_vect = base64_decode(substr($encrypted, 0, 43) . '==');
284
    // Remove $init_vect from $encrypted.
285
    $encrypted = substr($encrypted, 43);
286
    // Retrieve $mac which is the last 64 characters of $encrypted.
287
    $mac = substr($encrypted, -64);
288
    // Remove the last 64 chars from encrypted (remove MAC)
289
    $encrypted = substr($encrypted, 0, -64);
290
    //verify the sha256hmac from the encrypted data before even trying to decrypt it
291
    if (hash_hmac('sha256', $encrypted, $staticSalt) != $mac) {
292
        return false;
293
    }
294
    // Decrypt the data.
295
    $decrypted = rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $encrypted, 'ctr', $init_vect), "\0\4");
296
    // Yay!
297
    return $decrypted;
298
}
299
*/
300
301
/**
302
 * genHash().
303
 *
304
 * Generate a hash for user login
305
 *
306
 * @param string $password
307
 */
308
function bCrypt($password, $cost)
309
{
310
    $salt = sprintf('$2y$%02d$', $cost);
311
    if (function_exists('openssl_random_pseudo_bytes')) {
312
        $salt .= bin2hex(openssl_random_pseudo_bytes(11));
313
    } else {
314
        $chars = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
315
        for ($i = 0; $i < 22; ++$i) {
316
            $salt .= $chars[mt_rand(0, 63)];
317
        }
318
    }
319
320
    return crypt($password, $salt);
321
}
322
323
function testHex2Bin($val)
324
{
325
    if (!@hex2bin($val)) {
326
        throw new Exception('ERROR');
327
    }
328
329
    return hex2bin($val);
330
}
331
332
/**
333
 * Defuse cryption function.
334
 *
335
 * @param string $message   what to de/crypt
336
 * @param string $ascii_key key to use
337
 * @param string $type      operation to perform
338
 * @param array  $SETTINGS  Teampass settings
339
 *
340
 * @return array
341
 */
342
function cryption($message, $ascii_key, $type, $SETTINGS)
343
{
344
    $ascii_key = (empty($ascii_key) === true) ? file_get_contents(SECUREPATH . '/teampass-seckey.txt') : $ascii_key;
345
    $err = false;
346
347
    // load PhpEncryption library
348
    if (isset($SETTINGS['cpassman_dir']) === false || empty($SETTINGS['cpassman_dir']) === true) {
349
        $path = '../includes/libraries/Encryption/Encryption/';
350
    } else {
351
        $path = $SETTINGS['cpassman_dir'] . '/includes/libraries/Encryption/Encryption/';
352
    }
353
354
    include_once $path . 'Crypto.php';
355
    include_once $path . 'Encoding.php';
356
    include_once $path . 'DerivedKeys.php';
357
    include_once $path . 'Key.php';
358
    include_once $path . 'KeyOrPassword.php';
359
    include_once $path . 'File.php';
360
    include_once $path . 'RuntimeTests.php';
361
    include_once $path . 'KeyProtectedByPassword.php';
362
    include_once $path . 'Core.php';
363
364
    // convert KEY
365
    $key = \Defuse\Crypto\Key::loadFromAsciiSafeString($ascii_key);
366
367
    try {
368
        if ($type === 'encrypt') {
369
            $text = \Defuse\Crypto\Crypto::encrypt($message, $key);
370
        } else if ($type === 'decrypt') {
371
            $text = \Defuse\Crypto\Crypto::decrypt($message, $key);
372
        }
373
    } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
374
        $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.';
375
    } catch (Defuse\Crypto\Exception\BadFormatException $ex) {
376
        $err = $ex;
377
    } catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) {
378
        $err = $ex;
379
    } catch (Defuse\Crypto\Exception\CryptoException $ex) {
380
        $err = $ex;
381
    } catch (Defuse\Crypto\Exception\IOException $ex) {
382
        $err = $ex;
383
    }
384
    //echo \Defuse\Crypto\Crypto::decrypt($message, $key).' ## ';
385
386
    return array(
387
        'string' => isset($text) ? $text : '',
388
        'error' => $err,
389
    );
390
}
391
392
/**
393
 * Generating a defuse key.
394
 *
395
 * @return string
396
 */
397
function defuse_generate_key()
398
{
399
    // load PhpEncryption library
400
    if (file_exists('../includes/config/tp.config.php') === true) {
401
        $path = '../includes/libraries/Encryption/Encryption/';
402
    } elseif (file_exists('./includes/config/tp.config.php') === true) {
403
        $path = './includes/libraries/Encryption/Encryption/';
404
    } else {
405
        $path = '../includes/libraries/Encryption/Encryption/';
406
    }
407
408
    include_once $path . 'Crypto.php';
409
    include_once $path . 'Encoding.php';
410
    include_once $path . 'DerivedKeys.php';
411
    include_once $path . 'Key.php';
412
    include_once $path . 'KeyOrPassword.php';
413
    include_once $path . 'File.php';
414
    include_once $path . 'RuntimeTests.php';
415
    include_once $path . 'KeyProtectedByPassword.php';
416
    include_once $path . 'Core.php';
417
418
    $key = \Defuse\Crypto\Key::createNewRandomKey();
419
    $key = $key->saveToAsciiSafeString();
420
421
    return $key;
422
}
423
424
/**
425
 * Generate a Defuse personal key.
426
 *
427
 * @param string $psk psk used
428
 *
429
 * @return string
430
 */
431
function defuse_generate_personal_key($psk)
432
{
433
    // load PhpEncryption library
434
    if (file_exists('../includes/config/tp.config.php') === true) {
435
        $path = '../includes/libraries/Encryption/Encryption/';
436
    } elseif (file_exists('./includes/config/tp.config.php') === true) {
437
        $path = './includes/libraries/Encryption/Encryption/';
438
    } else {
439
        $path = '../includes/libraries/Encryption/Encryption/';
440
    }
441
442
    include_once $path . 'Crypto.php';
443
    include_once $path . 'Encoding.php';
444
    include_once $path . 'DerivedKeys.php';
445
    include_once $path . 'Key.php';
446
    include_once $path . 'KeyOrPassword.php';
447
    include_once $path . 'File.php';
448
    include_once $path . 'RuntimeTests.php';
449
    include_once $path . 'KeyProtectedByPassword.php';
450
    include_once $path . 'Core.php';
451
452
    $protected_key = \Defuse\Crypto\KeyProtectedByPassword::createRandomPasswordProtectedKey($psk);
453
    $protected_key_encoded = $protected_key->saveToAsciiSafeString();
454
455
    return $protected_key_encoded; // save this in user table
456
}
457
458
/**
459
 * Validate persoanl key with defuse.
460
 *
461
 * @param string $psk                   the user's psk
462
 * @param string $protected_key_encoded special key
463
 *
464
 * @return string
465
 */
466
function defuse_validate_personal_key($psk, $protected_key_encoded)
467
{
468
    // load PhpEncryption library
469
    if (file_exists('../includes/config/tp.config.php') === true) {
470
        $path = '../includes/libraries/Encryption/Encryption/';
471
    } elseif (file_exists('./includes/config/tp.config.php') === true) {
472
        $path = './includes/libraries/Encryption/Encryption/';
473
    } else {
474
        $path = '../includes/libraries/Encryption/Encryption/';
475
    }
476
477
    include_once $path . 'Crypto.php';
478
    include_once $path . 'Encoding.php';
479
    include_once $path . 'DerivedKeys.php';
480
    include_once $path . 'Key.php';
481
    include_once $path . 'KeyOrPassword.php';
482
    include_once $path . 'File.php';
483
    include_once $path . 'RuntimeTests.php';
484
    include_once $path . 'KeyProtectedByPassword.php';
485
    include_once $path . 'Core.php';
486
487
    try {
488
        $protected_key = \Defuse\Crypto\KeyProtectedByPassword::loadFromAsciiSafeString($protected_key_encoded);
489
        $user_key = $protected_key->unlockKey($psk);
490
        $user_key_encoded = $user_key->saveToAsciiSafeString();
491
    } catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) {
492
        return 'Error - Major issue as the encryption is broken.';
493
    } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
494
        return 'Error - The saltkey is not the correct one.';
495
    }
496
497
    return $user_key_encoded; // store it in session once user has entered his psk
498
}
499
500
/**
501
 * Decrypt a defuse string if encrypted.
502
 *
503
 * @param string $value Encrypted string
504
 *
505
 * @return string Decrypted string
506
 */
507
function defuseReturnDecrypted($value, $SETTINGS)
508
{
509
    if (substr($value, 0, 3) === 'def') {
510
        $value = cryption($value, '', 'decrypt', $SETTINGS)['string'];
511
    }
512
513
    return $value;
514
}
515
516
/**
517
 * Trims a string depending on a specific string.
518
 *
519
 * @param string|array $chaine  what to trim
520
 * @param string       $element trim on what
521
 *
522
 * @return string
523
 */
524
function trimElement($chaine, $element)
525
{
526
    if (!empty($chaine)) {
527
        if (is_array($chaine) === true) {
528
            $chaine = implode(';', $chaine);
529
        }
530
        $chaine = trim($chaine);
531
        if (substr($chaine, 0, 1) === $element) {
532
            $chaine = substr($chaine, 1);
533
        }
534
        if (substr($chaine, strlen($chaine) - 1, 1) === $element) {
535
            $chaine = substr($chaine, 0, strlen($chaine) - 1);
536
        }
537
    }
538
539
    return $chaine;
540
}
541
542
/**
543
 * Permits to suppress all "special" characters from string.
544
 *
545
 * @param string $string  what to clean
546
 * @param bool   $special use of special chars?
547
 *
548
 * @return string
549
 */
550
function cleanString($string, $special = false)
551
{
552
    // Create temporary table for special characters escape
553
    $tabSpecialChar = array();
554
    for ($i = 0; $i <= 31; ++$i) {
555
        $tabSpecialChar[] = chr($i);
556
    }
557
    array_push($tabSpecialChar, '<br />');
558
    if ((int) $special === 1) {
559
        $tabSpecialChar = array_merge($tabSpecialChar, array('</li>', '<ul>', '<ol>'));
560
    }
561
562
    return str_replace($tabSpecialChar, "\n", $string);
563
}
564
565
/**
566
 * Erro manager for DB.
567
 *
568
 * @param array $params output from query
569
 */
570
function db_error_handler($params)
571
{
572
    echo 'Error: ' . $params['error'] . "<br>\n";
573
    echo 'Query: ' . $params['query'] . "<br>\n";
574
    throw new Exception('Error - Query', 1);
575
}
576
577
/**
578
 * [identifyUserRights description].
579
 *
580
 * @param string $groupesVisiblesUser  [description]
581
 * @param string $groupesInterditsUser [description]
582
 * @param string $isAdmin              [description]
583
 * @param string $idFonctions          [description]
584
 *
585
 * @return string [description]
586
 */
587
function identifyUserRights(
588
    $groupesVisiblesUser,
589
    $groupesInterditsUser,
590
    $isAdmin,
591
    $idFonctions,
592
    $SETTINGS
593
) {
594
    //load ClassLoader
595
    include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php';
596
597
    // Load superglobal
598
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
599
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
600
601
    //Connect to DB
602
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
603
    if (defined('DB_PASSWD_CLEAR') === false) {
604
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
605
    }
606
    DB::$host = DB_HOST;
607
    DB::$user = DB_USER;
608
    DB::$password = DB_PASSWD_CLEAR;
609
    DB::$dbName = DB_NAME;
610
    DB::$port = DB_PORT;
611
    DB::$encoding = DB_ENCODING;
612
613
    //Build tree
614
    $tree = new SplClassLoader('Tree\NestedTree', $SETTINGS['cpassman_dir'] . '/includes/libraries');
615
    $tree->register();
616
    $tree = new Tree\NestedTree\NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
617
618
    // Check if user is ADMINISTRATOR
619
    if ((int) $isAdmin === 1) {
620
        identAdmin(
621
            $idFonctions,
622
            $SETTINGS,
623
            /** @scrutinizer ignore-type */ $tree
624
        );
625
    } else {
626
        identUser(
627
            $groupesVisiblesUser,
628
            $groupesInterditsUser,
629
            $idFonctions,
630
            $SETTINGS,
631
            /** @scrutinizer ignore-type */ $tree
632
        );
633
    }
634
635
    // update user's timestamp
636
    DB::update(
637
        prefixTable('users'),
638
        array(
639
            'timestamp' => time(),
640
        ),
641
        'id=%i',
642
        $superGlobal->get('user_id', 'SESSION')
643
    );
644
}
645
646
/**
647
 * Identify administrator.
648
 *
649
 * @param string $idFonctions Roles of user
650
 * @param array  $SETTINGS    Teampass settings
651
 * @param array  $tree        Tree of folders
652
 */
653
function identAdmin($idFonctions, $SETTINGS, $tree)
654
{
655
    // Load superglobal
656
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
657
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
658
659
    // Init
660
    $groupesVisibles = array();
661
    $superGlobal->put('personal_folders', array(), 'SESSION');
662
    $superGlobal->put('groupes_visibles', array(), 'SESSION');
663
    $superGlobal->put('no_access_folders', array(), 'SESSION');
664
    $superGlobal->put('personal_visible_groups', array(), 'SESSION');
665
    $superGlobal->put('read_only_folders', array(), 'SESSION');
666
    $superGlobal->put('list_restricted_folders_for_items', array(), 'SESSION');
667
    $superGlobal->put('list_folders_editable_by_role', array(), 'SESSION');
668
    $superGlobal->put('list_folders_limited', array(), 'SESSION');
669
    $superGlobal->put('no_access_folders', array(), 'SESSION');
670
    $superGlobal->put('forbiden_pfs', array(), 'SESSION');
671
672
    // Get superglobals
673
    $globalsUserId = $superGlobal->get('user_id', 'SESSION');
674
    $globalsVisibleFolders = $superGlobal->get('groupes_visibles', 'SESSION');
675
    $globalsPersonalVisibleFolders = $superGlobal->get('personal_visible_groups', 'SESSION');
676
677
    // Get list of Folders
678
    $rows = DB::query('SELECT id FROM ' . prefixTable('nested_tree') . ' WHERE personal_folder = %i', 0);
679
    foreach ($rows as $record) {
680
        array_push($groupesVisibles, $record['id']);
681
    }
682
    $superGlobal->put('groupes_visibles', $groupesVisibles, 'SESSION');
683
    $superGlobal->put('all_non_personal_folders', $groupesVisibles, 'SESSION');
684
685
    // Exclude all PF
686
    $where = new WhereClause('and'); // create a WHERE statement of pieces joined by ANDs
687
    $where->add('personal_folder=%i', 1);
688
    if (
689
        isset($SETTINGS['enable_pf_feature']) === true
690
        && (int) $SETTINGS['enable_pf_feature'] === 1
691
    ) {
692
        $where->add('title=%s', $globalsUserId);
693
        $where->negateLast();
694
    }
695
    // Get ID of personal folder
696
    $persfld = DB::queryfirstrow(
697
        'SELECT id FROM ' . prefixTable('nested_tree') . ' WHERE title = %s',
698
        $globalsUserId
699
    );
700
    if (empty($persfld['id']) === false) {
701
        if (in_array($persfld['id'], $globalsVisibleFolders) === false) {
702
            array_push($globalsVisibleFolders, $persfld['id']);
703
            array_push($globalsPersonalVisibleFolders, $persfld['id']);
704
            // get all descendants
705
            $tree = new Tree\NestedTree\NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
706
            $tree->rebuild();
707
            $tst = $tree->getDescendants($persfld['id']);
708
            foreach ($tst as $t) {
709
                array_push($globalsVisibleFolders, $t->id);
710
                array_push($globalsPersonalVisibleFolders, $t->id);
711
            }
712
        }
713
    }
714
715
    // get complete list of ROLES
716
    $tmp = explode(';', $idFonctions);
717
    $rows = DB::query(
718
        'SELECT * FROM ' . prefixTable('roles_title') . '
719
        ORDER BY title ASC'
720
    );
721
    foreach ($rows as $record) {
722
        if (!empty($record['id']) && !in_array($record['id'], $tmp)) {
723
            array_push($tmp, $record['id']);
724
        }
725
    }
726
    $superGlobal->put('fonction_id', implode(';', $tmp), 'SESSION');
727
    $superGlobal->put('is_admin', 1, 'SESSION');
728
729
    // Check if admin has created Folders and Roles
730
    DB::query('SELECT * FROM ' . prefixTable('nested_tree') . '');
731
    $superGlobal->put('nb_folders', DB::count(), 'SESSION');
732
    DB::query('SELECT * FROM ' . prefixTable('roles_title'));
733
    $superGlobal->put('nb_roles', DB::count(), 'SESSION');
734
}
735
736
/**
737
 * Permits to convert an element to array.
738
 *
739
 * @param string|array $element Any value to be returned as array
740
 *
741
 * @return array
742
 */
743
function convertToArray($element)
744
{
745
    if (is_string($element) === true) {
746
        if (empty($element) === true) {
747
            return array();
748
        } else {
749
            return explode(
750
                ';',
751
                trimElement($element, ';')
752
            );
753
        }
754
    } else {
755
        return $element;
756
    }
757
}
758
759
/**
760
 * Defines the rights the user has.
761
 *
762
 * @param string|array $allowedFolders  Allowed folders
763
 * @param string|array $noAccessFolders Not allowed folders
764
 * @param string|array $userRoles       Roles of user
765
 * @param array        $SETTINGS        Teampass settings
766
 * @param array        $tree            Tree of folders
767
 */
768
function identUser(
769
    $allowedFolders,
770
    $noAccessFolders,
771
    $userRoles,
772
    $SETTINGS,
773
    $tree
774
) {
775
    // Load superglobal
776
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
777
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
778
779
    // Init
780
    $superGlobal->put('groupes_visibles', array(), 'SESSION');
781
    $superGlobal->put('personal_folders', array(), 'SESSION');
782
    $superGlobal->put('no_access_folders', array(), 'SESSION');
783
    $superGlobal->put('personal_visible_groups', array(), 'SESSION');
784
    $superGlobal->put('read_only_folders', array(), 'SESSION');
785
    $superGlobal->put('fonction_id', $userRoles, 'SESSION');
786
    $superGlobal->put('is_admin', 0, 'SESSION');
787
788
    // init
789
    $personalFolders = array();
790
    $readOnlyFolders = array();
791
    $noAccessPersonalFolders = array();
792
    $restrictedFoldersForItems = array();
793
    $foldersLimited = array();
794
    $foldersLimitedFull = array();
795
    $allowedFoldersByRoles = array();
796
797
    // Get superglobals
798
    $globalsUserId = $superGlobal->get('user_id', 'SESSION');
799
    $globalsPersonalFolders = $superGlobal->get('personal_folder', 'SESSION');
800
801
    // Ensure consistency in array format
802
    $noAccessFolders = convertToArray($noAccessFolders);
803
    $userRoles = convertToArray($userRoles);
804
    $allowedFolders = convertToArray($allowedFolders);
805
806
    // Get list of folders depending on Roles
807
    $rows = DB::query(
808
        'SELECT *
809
        FROM ' . prefixTable('roles_values') . '
810
        WHERE role_id IN %li AND type IN %ls',
811
        $userRoles,
812
        array('W', 'ND', 'NE', 'NDNE', 'R')
813
    );
814
    foreach ($rows as $record) {
815
        if ($record['type'] === 'R') {
816
            array_push($readOnlyFolders, $record['folder_id']);
817
        } elseif (in_array($record['folder_id'], $allowedFolders) === false) {
818
            array_push($allowedFoldersByRoles, $record['folder_id']);
819
        }
820
    }
821
    $allowedFoldersByRoles = array_unique($allowedFoldersByRoles);
822
    $readOnlyFolders = array_unique($readOnlyFolders);
823
824
    // Clean arrays
825
    foreach ($allowedFoldersByRoles as $value) {
826
        if (($key = array_search($value, $readOnlyFolders)) !== false) {
827
            unset($readOnlyFolders[$key]);
828
        }
829
    }
830
831
    // Does this user is allowed to see other items
832
    $inc = 0;
833
    $rows = DB::query(
834
        'SELECT id, id_tree FROM ' . prefixTable('items') . '
835
        WHERE restricted_to LIKE %ss AND inactif = %s',
836
        $globalsUserId . ';',
837
        '0'
838
    );
839
    foreach ($rows as $record) {
840
        // Exclude restriction on item if folder is fully accessible
841
        if (in_array($record['id_tree'], $allowedFolders) === false) {
842
            $restrictedFoldersForItems[$record['id_tree']][$inc] = $record['id'];
843
            ++$inc;
844
        }
845
    }
846
847
    // Check for the users roles if some specific rights exist on items
848
    $rows = DB::query(
849
        'SELECT i.id_tree, r.item_id
850
        FROM ' . prefixTable('items') . ' as i
851
        INNER JOIN ' . prefixTable('restriction_to_roles') . ' as r ON (r.item_id=i.id)
852
        WHERE r.role_id IN %li
853
        ORDER BY i.id_tree ASC',
854
        $userRoles
855
    );
856
    $inc = 0;
857
    foreach ($rows as $record) {
858
        if (isset($record['id_tree'])) {
859
            $foldersLimited[$record['id_tree']][$inc] = $record['item_id'];
860
            array_push($foldersLimitedFull, $record['item_id']);
861
            ++$inc;
862
        }
863
    }
864
865
    // Get list of Personal Folders
866
    if (
867
        isset($SETTINGS['enable_pf_feature']) === true && (int) $SETTINGS['enable_pf_feature'] === 1
868
        && isset($globalsPersonalFolders) === true && (int) $globalsPersonalFolders === 1
869
    ) {
870
        $persoFld = DB::queryfirstrow(
871
            'SELECT id
872
            FROM ' . prefixTable('nested_tree') . '
873
            WHERE title = %s AND personal_folder = %i',
874
            $globalsUserId,
875
            1
876
        );
877
        if (empty($persoFld['id']) === false) {
878
            if (in_array($persoFld['id'], $allowedFolders) === false) {
879
                array_push($personalFolders, $persoFld['id']);
880
                array_push($allowedFolders, $persoFld['id']);
881
882
                // get all descendants
883
                $ids = $tree->getChildren($persoFld['id'], false);
884
                foreach ($ids as $ident) {
885
                    if ((int) $ident->personal_folder === 1) {
886
                        array_push($allowedFolders, $ident->id);
887
                        array_push($personalFolders, $ident->id);
888
                    }
889
                }
890
            }
891
        }
892
    }
893
894
    // Exclude all other PF
895
    $where = new WhereClause('and');
896
    $where->add('personal_folder=%i', 1);
897
    if (
898
        isset($SETTINGS['enable_pf_feature']) === true && (int) $SETTINGS['enable_pf_feature'] === 1
899
        && isset($globalsPersonalFolders) === true && (int) $globalsPersonalFolders === 1
900
    ) {
901
        $where->add('title=%s', $globalsUserId);
902
        $where->negateLast();
903
    }
904
    $persoFlds = DB::query(
905
        'SELECT id
906
        FROM ' . prefixTable('nested_tree') . '
907
        WHERE %l',
908
        $where
909
    );
910
    foreach ($persoFlds as $persoFldId) {
911
        array_push($noAccessPersonalFolders, $persoFldId['id']);
912
    }
913
914
    // All folders visibles
915
    $allowedFolders = array_merge(
916
        $foldersLimitedFull,
917
        $allowedFoldersByRoles,
918
        $restrictedFoldersForItems,
919
        $readOnlyFolders
920
    );
921
922
    // Exclude from allowed folders all the specific user forbidden folders
923
    if (count($noAccessFolders) > 0) {
924
        $allowedFolders = array_diff($allowedFolders, $noAccessFolders);
925
    }
926
927
    // Return data
928
    $superGlobal->put('all_non_personal_folders', $allowedFolders, 'SESSION');
929
    $superGlobal->put('groupes_visibles', array_merge($allowedFolders, $personalFolders), 'SESSION');
930
    $superGlobal->put('read_only_folders', $readOnlyFolders, 'SESSION');
931
    $superGlobal->put('no_access_folders', $noAccessFolders, 'SESSION');
932
    $superGlobal->put('personal_folders', $personalFolders, 'SESSION');
933
    $superGlobal->put('list_folders_limited', $foldersLimited, 'SESSION');
934
    $superGlobal->put('list_folders_editable_by_role', $allowedFoldersByRoles, 'SESSION');
935
    $superGlobal->put('list_restricted_folders_for_items', $restrictedFoldersForItems, 'SESSION');
936
    $superGlobal->put('forbiden_pfs', $noAccessPersonalFolders, 'SESSION');
937
    $superGlobal->put(
938
        'all_folders_including_no_access',
939
        array_merge(
940
            $allowedFolders,
941
            $personalFolders,
942
            $noAccessFolders,
943
            $readOnlyFolders
944
        ),
945
        'SESSION'
946
    );
947
948
    // Folders and Roles numbers
949
    DB::queryfirstrow('SELECT id FROM ' . prefixTable('nested_tree') . '');
950
    $superGlobal->put('nb_folders', DB::count(), 'SESSION');
951
    DB::queryfirstrow('SELECT id FROM ' . prefixTable('roles_title'));
952
    $superGlobal->put('nb_roles', DB::count(), 'SESSION');
953
954
    // check if change proposals on User's items
955
    if (isset($SETTINGS['enable_suggestion']) === true && (int) $SETTINGS['enable_suggestion'] === 1) {
956
        DB::query(
957
            'SELECT *
958
            FROM ' . prefixTable('items_change') . ' AS c
959
            LEFT JOIN ' . prefixTable('log_items') . ' AS i ON (c.item_id = i.id_item)
960
            WHERE i.action = %s AND i.id_user = %i',
961
            'at_creation',
962
            $globalsUserId
963
        );
964
        $superGlobal->put('nb_item_change_proposals', DB::count(), 'SESSION');
965
    } else {
966
        $superGlobal->put('nb_item_change_proposals', 0, 'SESSION');
967
    }
968
969
    return true;
970
}
971
972
/**
973
 * Update the CACHE table.
974
 *
975
 * @param string $action   What to do
976
 * @param array  $SETTINGS Teampass settings
977
 * @param string $ident    Ident format
978
 */
979
function updateCacheTable($action, $SETTINGS, $ident = null)
980
{
981
    if ($action === 'reload') {
982
        // Rebuild full cache table
983
        cacheTableRefresh($SETTINGS);
984
    } elseif ($action === 'update_value' && is_null($ident) === false) {
985
        // UPDATE an item
986
        cacheTableUpdate($SETTINGS, $ident);
987
    } elseif ($action === 'add_value' && is_null($ident) === false) {
988
        // ADD an item
989
        cacheTableAdd($SETTINGS, $ident);
990
    } elseif ($action === 'delete_value' && is_null($ident) === false) {
991
        // DELETE an item
992
        DB::delete(prefixTable('cache'), 'id = %i', $ident);
993
    }
994
}
995
996
/**
997
 * Cache table - refresh.
998
 *
999
 * @param array $SETTINGS Teampass settings
1000
 */
1001
function cacheTableRefresh($SETTINGS)
1002
{
1003
    include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php';
1004
1005
    //Connect to DB
1006
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
1007
    if (defined('DB_PASSWD_CLEAR') === false) {
1008
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
1009
    }
1010
    DB::$host = DB_HOST;
1011
    DB::$user = DB_USER;
1012
    DB::$password = DB_PASSWD_CLEAR;
1013
    DB::$dbName = DB_NAME;
1014
    DB::$port = DB_PORT;
1015
    DB::$encoding = DB_ENCODING;
1016
1017
    //Load Tree
1018
    $tree = new SplClassLoader('Tree\NestedTree', '../includes/libraries');
1019
    $tree->register();
1020
    $tree = new Tree\NestedTree\NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
1021
1022
    // truncate table
1023
    DB::query('TRUNCATE TABLE ' . prefixTable('cache'));
1024
1025
    // reload date
1026
    $rows = DB::query(
1027
        'SELECT *
1028
        FROM ' . prefixTable('items') . ' as i
1029
        INNER JOIN ' . prefixTable('log_items') . ' as l ON (l.id_item = i.id)
1030
        AND l.action = %s
1031
        AND i.inactif = %i',
1032
        'at_creation',
1033
        0
1034
    );
1035
    foreach ($rows as $record) {
1036
        if (empty($record['id_tree']) === false) {
1037
            // Get all TAGS
1038
            $tags = '';
1039
            $itemTags = DB::query(
1040
                'SELECT tag
1041
                FROM ' . prefixTable('tags') . '
1042
                WHERE item_id = %i AND tag != ""',
1043
                $record['id']
1044
            );
1045
            foreach ($itemTags as $itemTag) {
1046
                $tags .= $itemTag['tag'] . ' ';
1047
            }
1048
1049
            // Get renewal period
1050
            $resNT = DB::queryfirstrow(
1051
                'SELECT renewal_period
1052
                FROM ' . prefixTable('nested_tree') . '
1053
                WHERE id = %i',
1054
                $record['id_tree']
1055
            );
1056
1057
            // form id_tree to full foldername
1058
            $folder = array();
1059
            $arbo = $tree->getPath($record['id_tree'], true);
1060
            foreach ($arbo as $elem) {
1061
                // Check if title is the ID of a user
1062
                if (is_numeric($elem->title) === true) {
1063
                    // Is this a User id?
1064
                    $user = DB::queryfirstrow(
1065
                        'SELECT id, login
1066
                        FROM ' . prefixTable('users') . '
1067
                        WHERE id = %i',
1068
                        $elem->title
1069
                    );
1070
                    if (count($user) > 0) {
1071
                        $elem->title = $user['login'];
1072
                    }
1073
                }
1074
                // Build path
1075
                array_push($folder, stripslashes($elem->title));
1076
            }
1077
            // store data
1078
            DB::insert(
1079
                prefixTable('cache'),
1080
                array(
1081
                    'id' => $record['id'],
1082
                    'label' => $record['label'],
1083
                    'description' => isset($record['description']) ? $record['description'] : '',
1084
                    'url' => (isset($record['url']) && !empty($record['url'])) ? $record['url'] : '0',
1085
                    'tags' => $tags,
1086
                    'id_tree' => $record['id_tree'],
1087
                    'perso' => $record['perso'],
1088
                    'restricted_to' => (isset($record['restricted_to']) && !empty($record['restricted_to'])) ? $record['restricted_to'] : '0',
1089
                    'login' => isset($record['login']) ? $record['login'] : '',
1090
                    'folder' => implode(' > ', $folder),
1091
                    'author' => $record['id_user'],
1092
                    'renewal_period' => isset($resNT['renewal_period']) ? $resNT['renewal_period'] : '0',
1093
                    'timestamp' => $record['date'],
1094
                )
1095
            );
1096
        }
1097
    }
1098
}
1099
1100
/**
1101
 * Cache table - update existing value.
1102
 *
1103
 * @param array  $SETTINGS Teampass settings
1104
 * @param string $ident    Ident format
1105
 */
1106
function cacheTableUpdate($SETTINGS, $ident = null)
1107
{
1108
    include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php';
1109
1110
    // Load superglobal
1111
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
1112
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
1113
1114
    //Connect to DB
1115
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
1116
    if (defined('DB_PASSWD_CLEAR') === false) {
1117
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
1118
    }
1119
    DB::$host = DB_HOST;
1120
    DB::$user = DB_USER;
1121
    DB::$password = DB_PASSWD_CLEAR;
1122
    DB::$dbName = DB_NAME;
1123
    DB::$port = DB_PORT;
1124
    DB::$encoding = DB_ENCODING;
1125
1126
    //Load Tree
1127
    $tree = new SplClassLoader('Tree\NestedTree', '../includes/libraries');
1128
    $tree->register();
1129
    $tree = new Tree\NestedTree\NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
1130
1131
    // get new value from db
1132
    $data = DB::queryfirstrow(
1133
        'SELECT label, description, id_tree, perso, restricted_to, login, url
1134
        FROM ' . prefixTable('items') . '
1135
        WHERE id=%i',
1136
        $ident
1137
    );
1138
    // Get all TAGS
1139
    $tags = '';
1140
    $itemTags = DB::query(
1141
        'SELECT tag
1142
        FROM ' . prefixTable('tags') . '
1143
        WHERE item_id = %i AND tag != ""',
1144
        $ident
1145
    );
1146
    foreach ($itemTags as $itemTag) {
1147
        $tags .= $itemTag['tag'] . ' ';
1148
    }
1149
    // form id_tree to full foldername
1150
    $folder = array();
1151
    $arbo = $tree->getPath($data['id_tree'], true);
1152
    foreach ($arbo as $elem) {
1153
        // Check if title is the ID of a user
1154
        if (is_numeric($elem->title) === true) {
1155
            // Is this a User id?
1156
            $user = DB::queryfirstrow(
1157
                'SELECT id, login
1158
                FROM ' . prefixTable('users') . '
1159
                WHERE id = %i',
1160
                $elem->title
1161
            );
1162
            if (count($user) > 0) {
1163
                $elem->title = $user['login'];
1164
            }
1165
        }
1166
        // Build path
1167
        array_push($folder, stripslashes($elem->title));
1168
    }
1169
    // finaly update
1170
    DB::update(
1171
        prefixTable('cache'),
1172
        array(
1173
            'label' => $data['label'],
1174
            'description' => $data['description'],
1175
            'tags' => $tags,
1176
            'url' => (isset($data['url']) && !empty($data['url'])) ? $data['url'] : '0',
1177
            'id_tree' => $data['id_tree'],
1178
            'perso' => $data['perso'],
1179
            'restricted_to' => (isset($data['restricted_to']) && !empty($data['restricted_to'])) ? $data['restricted_to'] : '0',
1180
            'login' => isset($data['login']) ? $data['login'] : '',
1181
            'folder' => implode(' » ', $folder),
1182
            'author' => $superGlobal->get('user_id', 'SESSION'),
1183
        ),
1184
        'id = %i',
1185
        $ident
1186
    );
1187
}
1188
1189
/**
1190
 * Cache table - add new value.
1191
 *
1192
 * @param array  $SETTINGS Teampass settings
1193
 * @param string $ident    Ident format
1194
 */
1195
function cacheTableAdd($SETTINGS, $ident = null)
1196
{
1197
    include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php';
1198
1199
    // Load superglobal
1200
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
1201
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
1202
1203
    // Get superglobals
1204
    $globalsUserId = $superGlobal->get('user_id', 'SESSION');
1205
1206
    //Connect to DB
1207
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
1208
    if (defined('DB_PASSWD_CLEAR') === false) {
1209
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
1210
    }
1211
    DB::$host = DB_HOST;
1212
    DB::$user = DB_USER;
1213
    DB::$password = DB_PASSWD_CLEAR;
1214
    DB::$dbName = DB_NAME;
1215
    DB::$port = DB_PORT;
1216
    DB::$encoding = DB_ENCODING;
1217
1218
    //Load Tree
1219
    $tree = new SplClassLoader('Tree\NestedTree', '../includes/libraries');
1220
    $tree->register();
1221
    $tree = new Tree\NestedTree\NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
1222
1223
    // get new value from db
1224
    $data = DB::queryFirstRow(
1225
        'SELECT i.label, i.description, i.id_tree as id_tree, i.perso, i.restricted_to, i.id, i.login, i.url, l.date
1226
        FROM ' . prefixTable('items') . ' as i
1227
        INNER JOIN ' . prefixTable('log_items') . ' as l ON (l.id_item = i.id)
1228
        WHERE i.id = %i
1229
        AND l.action = %s',
1230
        $ident,
1231
        'at_creation'
1232
    );
1233
    // Get all TAGS
1234
    $tags = '';
1235
    $itemTags = DB::query(
1236
        'SELECT tag
1237
        FROM ' . prefixTable('tags') . '
1238
        WHERE item_id = %i AND tag != ""',
1239
        $ident
1240
    );
1241
    foreach ($itemTags as $itemTag) {
1242
        $tags .= $itemTag['tag'] . ' ';
1243
    }
1244
    // form id_tree to full foldername
1245
    $folder = array();
1246
    $arbo = $tree->getPath($data['id_tree'], true);
1247
    foreach ($arbo as $elem) {
1248
        // Check if title is the ID of a user
1249
        if (is_numeric($elem->title) === true) {
1250
            // Is this a User id?
1251
            $user = DB::queryfirstrow(
1252
                'SELECT id, login
1253
                FROM ' . prefixTable('users') . '
1254
                WHERE id = %i',
1255
                $elem->title
1256
            );
1257
            if (count($user) > 0) {
1258
                $elem->title = $user['login'];
1259
            }
1260
        }
1261
        // Build path
1262
        array_push($folder, stripslashes($elem->title));
1263
    }
1264
    // finaly update
1265
    DB::insert(
1266
        prefixTable('cache'),
1267
        array(
1268
            'id' => $data['id'],
1269
            'label' => $data['label'],
1270
            'description' => $data['description'],
1271
            'tags' => (isset($tags) && empty($tags) === false) ? $tags : 'None',
1272
            'url' => (isset($data['url']) && !empty($data['url'])) ? $data['url'] : '0',
1273
            'id_tree' => $data['id_tree'],
1274
            'perso' => (isset($data['perso']) && empty($data['perso']) === false && $data['perso'] !== 'None') ? $data['perso'] : '0',
1275
            'restricted_to' => (isset($data['restricted_to']) && empty($data['restricted_to']) === false) ? $data['restricted_to'] : '0',
1276
            'login' => isset($data['login']) ? $data['login'] : '',
1277
            'folder' => implode(' » ', $folder),
1278
            'author' => $globalsUserId,
1279
            'timestamp' => $data['date'],
1280
        )
1281
    );
1282
}
1283
1284
/**
1285
 * Do statistics.
1286
 *
1287
 * @param array $SETTINGS Teampass settings
1288
 *
1289
 * @return array
1290
 */
1291
function getStatisticsData($SETTINGS)
1292
{
1293
    DB::query(
1294
        'SELECT id FROM ' . prefixTable('nested_tree') . ' WHERE personal_folder = %i',
1295
        0
1296
    );
1297
    $counter_folders = DB::count();
1298
1299
    DB::query(
1300
        'SELECT id FROM ' . prefixTable('nested_tree') . ' WHERE personal_folder = %i',
1301
        1
1302
    );
1303
    $counter_folders_perso = DB::count();
1304
1305
    DB::query(
1306
        'SELECT id FROM ' . prefixTable('items') . ' WHERE perso = %i',
1307
        0
1308
    );
1309
    $counter_items = DB::count();
1310
1311
    DB::query(
1312
        'SELECT id FROM ' . prefixTable('items') . ' WHERE perso = %i',
1313
        1
1314
    );
1315
    $counter_items_perso = DB::count();
1316
1317
    DB::query(
1318
        'SELECT id FROM ' . prefixTable('users') . ''
1319
    );
1320
    $counter_users = DB::count();
1321
1322
    DB::query(
1323
        'SELECT id FROM ' . prefixTable('users') . ' WHERE admin = %i',
1324
        1
1325
    );
1326
    $admins = DB::count();
1327
1328
    DB::query(
1329
        'SELECT id FROM ' . prefixTable('users') . ' WHERE gestionnaire = %i',
1330
        1
1331
    );
1332
    $managers = DB::count();
1333
1334
    DB::query(
1335
        'SELECT id FROM ' . prefixTable('users') . ' WHERE read_only = %i',
1336
        1
1337
    );
1338
    $readOnly = DB::count();
1339
1340
    // list the languages
1341
    $usedLang = [];
1342
    $tp_languages = DB::query(
1343
        'SELECT name FROM ' . prefixTable('languages')
1344
    );
1345
    foreach ($tp_languages as $tp_language) {
1346
        DB::query(
1347
            'SELECT * FROM ' . prefixTable('users') . ' WHERE user_language = %s',
1348
            $tp_language['name']
1349
        );
1350
        $usedLang[$tp_language['name']] = round((DB::count() * 100 / $counter_users), 0);
1351
    }
1352
1353
    // get list of ips
1354
    $usedIp = [];
1355
    $tp_ips = DB::query(
1356
        'SELECT user_ip FROM ' . prefixTable('users')
1357
    );
1358
    foreach ($tp_ips as $ip) {
1359
        if (array_key_exists($ip['user_ip'], $usedIp)) {
1360
            $usedIp[$ip['user_ip']] = $usedIp[$ip['user_ip']] + 1;
1361
        } elseif (!empty($ip['user_ip']) && $ip['user_ip'] !== 'none') {
1362
            $usedIp[$ip['user_ip']] = 1;
1363
        }
1364
    }
1365
1366
    return array(
1367
        'error' => '',
1368
        'stat_phpversion' => phpversion(),
1369
        'stat_folders' => $counter_folders,
1370
        'stat_folders_shared' => intval($counter_folders) - intval($counter_folders_perso),
1371
        'stat_items' => $counter_items,
1372
        'stat_items_shared' => intval($counter_items) - intval($counter_items_perso),
1373
        'stat_users' => $counter_users,
1374
        'stat_admins' => $admins,
1375
        'stat_managers' => $managers,
1376
        'stat_ro' => $readOnly,
1377
        'stat_kb' => $SETTINGS['enable_kb'],
1378
        'stat_pf' => $SETTINGS['enable_pf_feature'],
1379
        'stat_fav' => $SETTINGS['enable_favourites'],
1380
        'stat_teampassversion' => TP_VERSION_FULL,
1381
        'stat_ldap' => $SETTINGS['ldap_mode'],
1382
        'stat_agses' => $SETTINGS['agses_authentication_enabled'],
1383
        'stat_duo' => $SETTINGS['duo'],
1384
        'stat_suggestion' => $SETTINGS['enable_suggestion'],
1385
        'stat_api' => $SETTINGS['api'],
1386
        'stat_customfields' => $SETTINGS['item_extra_fields'],
1387
        'stat_syslog' => $SETTINGS['syslog_enable'],
1388
        'stat_2fa' => $SETTINGS['google_authentication'],
1389
        'stat_stricthttps' => $SETTINGS['enable_sts'],
1390
        'stat_mysqlversion' => DB::serverVersion(),
1391
        'stat_languages' => $usedLang,
1392
        'stat_country' => $usedIp,
1393
    );
1394
}
1395
1396
/**
1397
 * Permits to send an email.
1398
 *
1399
 * @param string $subject     email subject
1400
 * @param string $textMail    email message
1401
 * @param string $email       email
1402
 * @param array  $SETTINGS    settings
1403
 * @param string $textMailAlt email message alt
1404
 * @param bool   $silent      no errors
1405
 *
1406
 * @return string some json info
1407
 */
1408
function sendEmail(
1409
    $subject,
1410
    $textMail,
1411
    $email,
1412
    $SETTINGS,
1413
    $textMailAlt = null,
1414
    $silent = true
1415
) {
1416
    // CAse where email not defined
1417
    if ($email === 'none' || empty($email) === true) {
1418
        return '"error":"" , "message":"' . langHdl('forgot_my_pw_email_sent') . '"';
1419
    }
1420
1421
    // Load settings
1422
    include_once $SETTINGS['cpassman_dir'] . '/includes/config/settings.php';
1423
1424
    // Load superglobal
1425
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
1426
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
1427
1428
    // Get user language
1429
    $session_user_language = $superGlobal->get('user_language', 'SESSION');
1430
    $user_language = isset($session_user_language) ? $session_user_language : 'english';
1431
    include_once $SETTINGS['cpassman_dir'] . '/includes/language/' . $user_language . '.php';
1432
1433
    // Load library
1434
    include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php';
1435
1436
    // load PHPMailer
1437
    $mail = new SplClassLoader('PHPMailer\PHPMailer', '../includes/libraries');
1438
    $mail->register();
1439
    $mail = new PHPMailer\PHPMailer\PHPMailer(true);
1440
    try {
1441
        // send to user
1442
        $mail->setLanguage('en', $SETTINGS['cpassman_dir'] . '/includes/libraries/PHPMailer/PHPMailer/language/');
1443
        $mail->SMTPDebug = 0; //value 1 can be used to debug - 4 for debuging connections
1444
        $mail->Port = $SETTINGS['email_port']; //COULD BE USED
1445
        $mail->CharSet = 'utf-8';
1446
        $mail->SMTPSecure = ($SETTINGS['email_security'] === 'tls'
1447
            || $SETTINGS['email_security'] === 'ssl') ? $SETTINGS['email_security'] : '';
1448
        $mail->SMTPAutoTLS = ($SETTINGS['email_security'] === 'tls'
1449
            || $SETTINGS['email_security'] === 'ssl') ? true : false;
1450
        $mail->SMTPOptions = array(
1451
            'ssl' => array(
1452
                'verify_peer' => false,
1453
                'verify_peer_name' => false,
1454
                'allow_self_signed' => true,
1455
            ),
1456
        );
1457
        $mail->isSmtp(); // send via SMTP
1458
        $mail->Host = $SETTINGS['email_smtp_server']; // SMTP servers
1459
        $mail->SMTPAuth = (int) $SETTINGS['email_smtp_auth'] === 1 ? true : false; // turn on SMTP authentication
1460
        $mail->Username = $SETTINGS['email_auth_username']; // SMTP username
1461
        $mail->Password = $SETTINGS['email_auth_pwd']; // SMTP password
1462
        $mail->From = $SETTINGS['email_from'];
1463
        $mail->FromName = $SETTINGS['email_from_name'];
1464
1465
        // Prepare for each person
1466
        foreach (array_filter(explode(',', $email)) as $dest) {
1467
            if (empty($dest) === false) {
1468
                $mail->addAddress($dest);
1469
            }
1470
        }
1471
1472
        // Prepare HTML
1473
        $text_html = emailBody($textMail);
1474
1475
        $mail->WordWrap = 80; // set word wrap
1476
        $mail->isHtml(true); // send as HTML
1477
        $mail->Subject = $subject;
1478
        $mail->Body = $text_html;
1479
        $mail->AltBody = (is_null($textMailAlt) === false) ? $textMailAlt : '';
1480
        // send email
1481
        if ($mail->send()) {
1482
            $mail->smtpClose();
1483
            if ($silent === false) {
1484
                return json_encode(
1485
                    array(
1486
                        'error' => false,
1487
                        'message' => langHdl('forgot_my_pw_email_sent'),
1488
                    )
1489
                );
1490
            }
1491
        } elseif ($silent === false) {
1492
            $mail->smtpClose();
1493
            return json_encode(
1494
                array(
1495
                    'error' => true,
1496
                    'message' => str_replace(array("\n", "\t", "\r"), '', $mail->ErrorInfo),
1497
                )
1498
            );
1499
        }
1500
    } catch (Exception $e) {
1501
        if ($silent === false) {
1502
            return json_encode(
1503
                array(
1504
                    'error' => true,
1505
                    'message' => str_replace(array("\n", "\t", "\r"), '', $mail->ErrorInfo),
1506
                )
1507
            );
1508
        }
1509
    }
1510
}
1511
1512
/**
1513
 * Returns the email body.
1514
 *
1515
 * @param string $textMail Text for the email
1516
 *
1517
 * @return string
1518
 */
1519
function emailBody($textMail)
1520
{
1521
    return '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.=
1522
    w3.org/TR/html4/loose.dtd"><html>
1523
    <head><title>Email Template</title>
1524
    <style type="text/css">
1525
    body { background-color: #f0f0f0; padding: 10px 0; margin:0 0 10px =0; }
1526
    </style></head>
1527
    <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">
1528
    <table border="0" width="100%" height="100%" cellpadding="0" cellspacing="0" bgcolor="#f0f0f0" style="border-spacing: 0;">
1529
    <tr><td style="border-collapse: collapse;"><br>
1530
        <table border="0" width="100%" cellpadding="0" cellspacing="0" bgcolor="#17357c" style="border-spacing: 0; margin-bottom: 25px;">
1531
        <tr><td style="border-collapse: collapse; padding: 11px 20px;">
1532
            <div style="max-width:150px; max-height:34px; color:#f0f0f0; font-weight:bold;">Teampass</div>
1533
        </td></tr></table></td>
1534
    </tr>
1535
    <tr><td align="center" valign="top" bgcolor="#f0f0f0" style="border-collapse: collapse; background-color: #f0f0f0;">
1536
        <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;">
1537
        <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;">
1538
        <br><div style="float:right;">' .
1539
        $textMail .
1540
        '<br><br></td></tr></table>
1541
    </td></tr></table>
1542
    <br></body></html>';
1543
}
1544
1545
/**
1546
 * Generate a Key.
1547
 *
1548
 * @return string
1549
 */
1550
function generateKey()
1551
{
1552
    return substr(md5(rand() . rand()), 0, 15);
1553
}
1554
1555
/**
1556
 * Convert date to timestamp.
1557
 *
1558
 * @param string $date     The date
1559
 * @param array  $SETTINGS Teampass settings
1560
 *
1561
 * @return string
1562
 */
1563
function dateToStamp($date, $SETTINGS)
1564
{
1565
    $date = date_parse_from_format($SETTINGS['date_format'], $date);
1566
    if ((int) $date['warning_count'] === 0 && (int) $date['error_count'] === 0) {
1567
        return mktime(23, 59, 59, $date['month'], $date['day'], $date['year']);
1568
    } else {
1569
        return '';
1570
    }
1571
}
1572
1573
/**
1574
 * Is this a date.
1575
 *
1576
 * @param string $date Date
1577
 *
1578
 * @return bool
1579
 */
1580
function isDate($date)
1581
{
1582
    return strtotime($date) !== false;
1583
}
1584
1585
/**
1586
 * Check if isUTF8().
1587
 *
1588
 * @return int is the string in UTF8 format
1589
 */
1590
function isUTF8($string)
1591
{
1592
    if (is_array($string) === true) {
1593
        $string = $string['string'];
1594
    }
1595
1596
    return preg_match(
1597
        '%^(?:
1598
        [\x09\x0A\x0D\x20-\x7E] # ASCII
1599
        | [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
1600
        | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
1601
        | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
1602
        | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
1603
        | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
1604
        | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
1605
        | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
1606
        )*$%xs',
1607
        $string
1608
    );
1609
}
1610
1611
/**
1612
 * Prepare an array to UTF8 format before JSON_encode.
1613
 *
1614
 * @param array $array Array of values
1615
 *
1616
 * @return array
1617
 */
1618
function utf8Converter($array)
1619
{
1620
    array_walk_recursive(
1621
        $array,
1622
        function (&$item) {
1623
            if (mb_detect_encoding($item, 'utf-8', true) === false) {
1624
                $item = utf8_encode($item);
1625
            }
1626
        }
1627
    );
1628
1629
    return $array;
1630
}
1631
1632
/**
1633
 * Permits to prepare data to be exchanged.
1634
 *
1635
 * @param array|string $data Text
1636
 * @param string       $type Parameter
1637
 * @param string       $key  Optional key
1638
 *
1639
 * @return resource|string|array
1640
 */
1641
function prepareExchangedData($data, $type, $key = null)
1642
{
1643
    if (file_exists('../includes/config/tp.config.php')) {
1644
        include '../includes/config/tp.config.php';
1645
    } elseif (file_exists('./includes/config/tp.config.php')) {
1646
        include './includes/config/tp.config.php';
1647
    } elseif (file_exists('../../includes/config/tp.config.php')) {
1648
        include '../../includes/config/tp.config.php';
1649
    } else {
1650
        throw new Exception("Error file '/includes/config/tp.config.php' not exists", 1);
1651
    }
1652
1653
    if (isset($SETTINGS) === false) {
1654
        return 'ERROR';
1655
    }
1656
1657
    // Load superglobal
1658
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
1659
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
1660
1661
    // Get superglobals
1662
    if ($key !== null) {
1663
        $superGlobal->put('key', $key, 'SESSION');
1664
        $globalsKey = $key;
1665
    } else {
1666
        $globalsKey = $superGlobal->get('key', 'SESSION');
1667
    }
1668
1669
    //load ClassLoader
1670
    include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php';
1671
    //Load AES
1672
    $aes = new SplClassLoader('Encryption\Crypt', $SETTINGS['cpassman_dir'] . '/includes/libraries');
1673
    $aes->register();
1674
1675
    if ($type === 'encode' && is_array($data) === true) {
1676
        // Ensure UTF8 format
1677
        $data = utf8Converter($data);
1678
        // Now encode
1679
        if (isset($SETTINGS['encryptClientServer'])
1680
            && $SETTINGS['encryptClientServer'] === '0'
1681
        ) {
1682
            return json_encode(
1683
                $data,
1684
                JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
1685
            );
1686
        } else {
1687
            return Encryption\Crypt\aesctr::encrypt(
1688
                json_encode(
1689
                    $data,
1690
                    JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
1691
                ),
1692
                $globalsKey,
1693
                256
1694
            );
1695
        }
1696
    } elseif ($type === 'decode' && is_array($data) === false) {
1697
        if (isset($SETTINGS['encryptClientServer'])
1698
            && $SETTINGS['encryptClientServer'] === '0'
1699
        ) {
1700
            return json_decode(
1701
                /** @scrutinizer ignore-type */ (string) $data,
1702
                true
1703
            );
1704
        } else {
1705
            return json_decode(
1706
                Encryption\Crypt\aesctr::decrypt(
1707
                    /** @scrutinizer ignore-type */ (string) $data,
1708
                    $globalsKey,
1709
                    256
1710
                ),
1711
                true
1712
            );
1713
        }
1714
    }
1715
}
1716
1717
/**
1718
 * Create a thumbnail.
1719
 *
1720
 * @param string  $src           Source
1721
 * @param string  $dest          Destination
1722
 * @param integer $desired_width Size of width
1723
 */
1724
function makeThumbnail($src, $dest, $desired_width)
1725
{
1726
    /* read the source image */
1727
    if(is_file($src) === true && mime_content_type($src) === 'image/png'){
1728
        $source_image = imagecreatefrompng($src);
1729
        if ($source_image === false) {
1730
            return "Error: Not a valid PNG file! It's type is ".mime_content_type($src);
1731
        }
1732
    } else {
1733
        return "Error: Not a valid PNG file! It's type is ".mime_content_type($src);
1734
    }
1735
    
1736
    // Get height and width
1737
    $width = imagesx($source_image);
1738
    $height = imagesy($source_image);
1739
1740
    /* find the "desired height" of this thumbnail, relative to the desired width  */
1741
    $desired_height = (int) floor($height * ($desired_width / $width));
1742
1743
    /* create a new, "virtual" image */
1744
    $virtual_image = imagecreatetruecolor($desired_width, $desired_height);
1745
    if ($virtual_image === false) {
1746
        return false;
1747
    }
1748
    /* copy source image at a resized size */
1749
    imagecopyresampled($virtual_image, $source_image, 0, 0, 0, 0, $desired_width, $desired_height, $width, $height);
1750
1751
    /* create the physical thumbnail image to its destination */
1752
    imagejpeg($virtual_image, $dest);
1753
}
1754
1755
/**
1756
 * Check table prefix in SQL query.
1757
 *
1758
 * @param string $table Table name
1759
 *
1760
 * @return string
1761
 */
1762
function prefixTable($table)
1763
{
1764
    $safeTable = htmlspecialchars(DB_PREFIX . $table);
1765
    if (!empty($safeTable)) {
1766
        // sanitize string
1767
        return $safeTable;
1768
    } else {
1769
        // stop error no table
1770
        return 'table_not_exists';
1771
    }
1772
}
1773
1774
1775
/**
1776
 * GenerateCryptKey
1777
 *
1778
 * @param int     $size      Length
1779
 * @param boolean $secure    Secure
1780
 * @param boolean $numerals  Numerics
1781
 * @param boolean $uppercase Uppercase letters
1782
 * @param boolean $symbols   Symbols
1783
 * @param boolean $lowercase Lowercase
1784
 * @param array   $SETTINGS  SETTINGS
1785
 * @return string
1786
 */
1787
function GenerateCryptKey(
1788
    $size = null,
1789
    $secure = false,
1790
    $numerals = false,
1791
    $uppercase = false,
1792
    $symbols = false,
1793
    $lowercase = false,
1794
    $SETTINGS
1795
) {
1796
    include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php';
1797
    $generator = new SplClassLoader('PasswordGenerator\Generator', $SETTINGS['cpassman_dir'] . '/includes/libraries');
1798
    $generator->register();
1799
    $generator = new PasswordGenerator\Generator\ComputerPasswordGenerator();
1800
1801
    // Is PHP7 being used?
1802
    if (version_compare(PHP_VERSION, '7.0.0', '>=')) {
1803
        $php7generator = new SplClassLoader('PasswordGenerator\RandomGenerator', $SETTINGS['cpassman_dir'] . '/includes/libraries');
1804
        $php7generator->register();
1805
        $generator->setRandomGenerator(new PasswordGenerator\RandomGenerator\Php7RandomGenerator());
1806
    }
1807
1808
    // Manage size
1809
    $generator->setLength(($size === null) ? 10 : (int) $size);
1810
1811
    if ($secure === true) {
1812
        $generator->setSymbols(true);
1813
        $generator->setLowercase(true);
1814
        $generator->setUppercase(true);
1815
        $generator->setNumbers(true);
1816
    } else {
1817
        $generator->setLowercase($lowercase);
1818
        $generator->setUppercase($uppercase);
1819
        $generator->setNumbers($numerals);
1820
        $generator->setSymbols($symbols);
1821
    }
1822
1823
    return $generator->generatePasswords()[0];
1824
}
1825
1826
/*
1827
* Send sysLOG message
1828
* @param string $message
1829
* @param string $host
1830
*/
1831
function send_syslog($message, $host, $port, $component = 'teampass')
1832
{
1833
    $sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
1834
    $syslog_message = '<123>' . date('M d H:i:s ') . $component . ': ' . $message;
1835
    socket_sendto($sock, $syslog_message, strlen($syslog_message), 0, $host, $port);
1836
    socket_close($sock);
1837
}
1838
1839
/**
1840
 * Permits to log events into DB
1841
 *
1842
 * @param array  $SETTINGS Teampass settings
1843
 * @param string $type     Type
1844
 * @param string $label    Label
1845
 * @param string $who      Who
1846
 * @param string $login    Login
1847
 * @param string $field_1  Field
1848
 *
1849
 * @return void
1850
 */
1851
function logEvents($SETTINGS, $type, $label, $who, $login = null, $field_1 = null)
1852
{
1853
    if (empty($who)) {
1854
        $who = getClientIpServer();
1855
    }
1856
1857
    // include librairies & connect to DB
1858
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
1859
    if (defined('DB_PASSWD_CLEAR') === false) {
1860
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
1861
    }
1862
    DB::$host = DB_HOST;
1863
    DB::$user = DB_USER;
1864
    DB::$password = DB_PASSWD_CLEAR;
1865
    DB::$dbName = DB_NAME;
1866
    DB::$port = DB_PORT;
1867
    DB::$encoding = DB_ENCODING;
1868
1869
    DB::insert(
1870
        prefixTable('log_system'),
1871
        array(
1872
            'type' => $type,
1873
            'date' => time(),
1874
            'label' => $label,
1875
            'qui' => $who,
1876
            'field_1' => $field_1 === null ? '' : $field_1,
1877
        )
1878
    );
1879
1880
    // If SYSLOG
1881
    if (isset($SETTINGS['syslog_enable']) === true && (int) $SETTINGS['syslog_enable'] === 1) {
1882
        if ($type === 'user_mngt') {
1883
            send_syslog(
1884
                'action=' . str_replace('at_', '', $label) . ' attribute=user user=' . $who . ' userid="' . $login . '" change="' . $field_1 . '" ',
1885
                $SETTINGS['syslog_host'],
1886
                $SETTINGS['syslog_port'],
1887
                'teampass'
1888
            );
1889
        } else {
1890
            send_syslog(
1891
                'action=' . $type . ' attribute=' . $label . ' user=' . $who . ' userid="' . $login . '" ',
1892
                $SETTINGS['syslog_host'],
1893
                $SETTINGS['syslog_port'],
1894
                'teampass'
1895
            );
1896
        }
1897
    }
1898
}
1899
1900
/**
1901
 * Log events.
1902
 *
1903
 * @param array  $SETTINGS        Teampass settings
1904
 * @param int    $item_id         Item id
1905
 * @param string $item_label      Item label
1906
 * @param int    $id_user         User id
1907
 * @param string $action          Code for reason
1908
 * @param string $login           User login
1909
 * @param string $raison          Code for reason
1910
 * @param string $encryption_type Encryption on
1911
 */
1912
function logItems(
1913
    $SETTINGS,
1914
    $item_id,
1915
    $item_label,
1916
    $id_user,
1917
    $action,
1918
    $login = null,
1919
    $raison = null,
1920
    $encryption_type = null
1921
) {
1922
    // include librairies & connect to DB
1923
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
1924
    if (defined('DB_PASSWD_CLEAR') === false) {
1925
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
1926
    }
1927
    DB::$host = DB_HOST;
1928
    DB::$user = DB_USER;
1929
    DB::$password = DB_PASSWD_CLEAR;
1930
    DB::$dbName = DB_NAME;
1931
    DB::$port = DB_PORT;
1932
    DB::$encoding = DB_ENCODING;
1933
1934
    // Insert log in DB
1935
    DB::insert(
1936
        prefixTable('log_items'),
1937
        array(
1938
            'id_item' => $item_id,
1939
            'date' => time(),
1940
            'id_user' => $id_user,
1941
            'action' => $action,
1942
            'raison' => $raison,
1943
            'raison_iv' => '',
1944
            'encryption_type' => is_null($encryption_type) === true ? TP_ENCRYPTION_NAME : $encryption_type,
1945
        )
1946
    );
1947
    // Timestamp the last change
1948
    if ($action === 'at_creation' || $action === 'at_modifiation' || $action === 'at_delete' || $action === 'at_import') {
1949
        DB::update(
1950
            prefixTable('misc'),
1951
            array(
1952
                'valeur' => time(),
1953
            ),
1954
            'type = %s AND intitule = %s',
1955
            'timestamp',
1956
            'last_item_change'
1957
        );
1958
    }
1959
1960
    // SYSLOG
1961
    if (isset($SETTINGS['syslog_enable']) === true && $SETTINGS['syslog_enable'] === '1') {
1962
        // Extract reason
1963
        $attribute = explode(' : ', $raison);
1964
1965
        // Get item info if not known
1966
        if (empty($item_label) === true) {
1967
            $dataItem = DB::queryfirstrow(
1968
                'SELECT id, id_tree, label
1969
                FROM ' . prefixTable('items') . '
1970
                WHERE id = %i',
1971
                $item_id
1972
            );
1973
1974
            $item_label = $dataItem['label'];
1975
        }
1976
1977
        send_syslog(
1978
            'action=' . str_replace('at_', '', $action) . ' attribute=' . str_replace('at_', '', $attribute[0]) . ' itemno=' . $item_id . ' user=' . addslashes($login) . ' itemname="' . addslashes($item_label) . '"',
1979
            $SETTINGS['syslog_host'],
1980
            $SETTINGS['syslog_port'],
1981
            'teampass'
1982
        );
1983
    }
1984
1985
    // send notification if enabled
1986
    notifyOnChange($item_id, $action, $SETTINGS);
1987
}
1988
1989
/**
1990
 * If enabled, then notify admin/manager.
1991
 *
1992
 * @param int    $item_id  Item id
1993
 * @param string $action   Action to do
1994
 * @param array  $SETTINGS Teampass settings
1995
 */
1996
function notifyOnChange($item_id, $action, $SETTINGS)
1997
{
1998
    if (
1999
        isset($SETTINGS['enable_email_notification_on_item_shown']) === true
2000
        && (int) $SETTINGS['enable_email_notification_on_item_shown'] === 1
2001
        && $action === 'at_shown'
2002
    ) {
2003
        // Load superglobal
2004
        include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
2005
        $superGlobal = new protect\SuperGlobal\SuperGlobal();
2006
2007
        // Get superglobals
2008
        $globalsLastname = $superGlobal->get('lastname', 'SESSION');
2009
        $globalsName = $superGlobal->get('name', 'SESSION');
2010
        $globalsNotifiedEmails = $superGlobal->get('listNotificationEmails', 'SESSION');
2011
2012
        // Get info about item
2013
        $dataItem = DB::queryfirstrow(
2014
            'SELECT id, id_tree, label
2015
            FROM ' . prefixTable('items') . '
2016
            WHERE id = %i',
2017
            $item_id
2018
        );
2019
        $item_label = $dataItem['label'];
2020
2021
        // send back infos
2022
        DB::insert(
2023
            prefixTable('emails'),
2024
            array(
2025
                'timestamp' => time(),
2026
                'subject' => langHdl('email_on_open_notification_subject'),
2027
                'body' => str_replace(
2028
                    array('#tp_user#', '#tp_item#', '#tp_link#'),
2029
                    array(
2030
                        addslashes($globalsName . ' ' . $globalsLastname),
2031
                        addslashes($item_label),
2032
                        $SETTINGS['cpassman_url'] . '/index.php?page=items&group=' . $dataItem['id_tree'] . '&id=' . $item_id,
2033
                    ),
2034
                    langHdl('email_on_open_notification_mail')
2035
                ),
2036
                'receivers' => $globalsNotifiedEmails,
2037
                'status' => '',
2038
            )
2039
        );
2040
    }
2041
}
2042
2043
/**
2044
 * Prepare notification email to subscribers.
2045
 *
2046
 * @param int    $item_id  Item id
2047
 * @param string $label    Item label
2048
 * @param array  $changes  List of changes
2049
 * @param array  $SETTINGS Teampass settings
2050
 */
2051
function notifyChangesToSubscribers($item_id, $label, $changes, $SETTINGS)
2052
{
2053
    // Load superglobal
2054
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
2055
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
2056
2057
    // Get superglobals
2058
    $globalsUserId = $superGlobal->get('user_id', 'SESSION');
2059
    $globalsLastname = $superGlobal->get('lastname', 'SESSION');
2060
    $globalsName = $superGlobal->get('name', 'SESSION');
2061
2062
    // send email to user that what to be notified
2063
    $notification = DB::queryOneColumn(
2064
        'email',
2065
        'SELECT *
2066
        FROM ' . prefixTable('notification') . ' AS n
2067
        INNER JOIN ' . prefixTable('users') . ' AS u ON (n.user_id = u.id)
2068
        WHERE n.item_id = %i AND n.user_id != %i',
2069
        $item_id,
2070
        $globalsUserId
2071
    );
2072
2073
    if (DB::count() > 0) {
2074
        // Prepare path
2075
        $path = geItemReadablePath($item_id, '', $SETTINGS);
2076
2077
        // Get list of changes
2078
        $htmlChanges = '<ul>';
2079
        foreach ($changes as $change) {
2080
            $htmlChanges .= '<li>' . $change . '</li>';
2081
        }
2082
        $htmlChanges .= '</ul>';
2083
2084
        // send email
2085
        DB::insert(
2086
            prefixTable('emails'),
2087
            array(
2088
                'timestamp' => time(),
2089
                'subject' => langHdl('email_subject_item_updated'),
2090
                'body' => str_replace(
2091
                    array('#item_label#', '#folder_name#', '#item_id#', '#url#', '#name#', '#lastname#', '#changes#'),
2092
                    array($label, $path, $item_id, $SETTINGS['cpassman_url'], $globalsName, $globalsLastname, $htmlChanges),
2093
                    langHdl('email_body_item_updated')
2094
                ),
2095
                'receivers' => implode(',', $notification),
2096
                'status' => '',
2097
            )
2098
        );
2099
    }
2100
}
2101
2102
/**
2103
 * Returns the Item + path.
2104
 *
2105
 * @param int    $id_tree  Node id
2106
 * @param string $label    Label
2107
 * @param array  $SETTINGS TP settings
2108
 *
2109
 * @return string
2110
 */
2111
function geItemReadablePath($id_tree, $label, $SETTINGS)
2112
{
2113
    // Class loader
2114
    include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php';
2115
2116
    //Load Tree
2117
    $tree = new SplClassLoader('Tree\NestedTree', '../includes/libraries');
2118
    $tree->register();
2119
    $tree = new Tree\NestedTree\NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
2120
2121
    $arbo = $tree->getPath($id_tree, true);
2122
    $path = '';
2123
    foreach ($arbo as $elem) {
2124
        if (empty($path) === true) {
2125
            $path = htmlspecialchars(stripslashes(htmlspecialchars_decode($elem->title, ENT_QUOTES)), ENT_QUOTES) . ' ';
2126
        } else {
2127
            $path .= '&#8594; ' . htmlspecialchars(stripslashes(htmlspecialchars_decode($elem->title, ENT_QUOTES)), ENT_QUOTES);
2128
        }
2129
    }
2130
2131
    // Build text to show user
2132
    if (empty($label) === false) {
2133
        return empty($path) === true ? addslashes($label) : addslashes($label) . ' (' . $path . ')';
2134
    } else {
2135
        return empty($path) === true ? '' : $path;
2136
    }
2137
}
2138
2139
/**
2140
 * Get the client ip address.
2141
 *
2142
 * @return string IP address
2143
 */
2144
function getClientIpServer()
2145
{
2146
    if (getenv('HTTP_CLIENT_IP')) {
2147
        $ipaddress = getenv('HTTP_CLIENT_IP');
2148
    } elseif (getenv('HTTP_X_FORWARDED_FOR')) {
2149
        $ipaddress = getenv('HTTP_X_FORWARDED_FOR');
2150
    } elseif (getenv('HTTP_X_FORWARDED')) {
2151
        $ipaddress = getenv('HTTP_X_FORWARDED');
2152
    } elseif (getenv('HTTP_FORWARDED_FOR')) {
2153
        $ipaddress = getenv('HTTP_FORWARDED_FOR');
2154
    } elseif (getenv('HTTP_FORWARDED')) {
2155
        $ipaddress = getenv('HTTP_FORWARDED');
2156
    } elseif (getenv('REMOTE_ADDR')) {
2157
        $ipaddress = getenv('REMOTE_ADDR');
2158
    } else {
2159
        $ipaddress = 'UNKNOWN';
2160
    }
2161
2162
    return $ipaddress;
2163
}
2164
2165
/**
2166
 * Escape all HTML, JavaScript, and CSS.
2167
 *
2168
 * @param string $input    The input string
2169
 * @param string $encoding Which character encoding are we using?
2170
 *
2171
 * @return string
2172
 */
2173
function noHTML($input, $encoding = 'UTF-8')
2174
{
2175
    return htmlspecialchars($input, ENT_QUOTES | ENT_XHTML, $encoding, false);
2176
}
2177
2178
/**
2179
 * 
2180
 * Permits to handle the Teampass config file
2181
 * $action accepts "rebuild" and "update"
2182
 *
2183
 * @param string $action   Action to perform
2184
 * @param array  $SETTINGS Teampass settings
2185
 * @param string $field    Field to refresh
2186
 * @param string $value    Value to set
2187
 *
2188
 * @return string|boolean
2189
 */
2190
function handleConfigFile($action, $SETTINGS, $field = null, $value = null)
2191
{
2192
    $tp_config_file = $SETTINGS['cpassman_dir'] . '/includes/config/tp.config.php';
2193
2194
    // include librairies & connect to DB
2195
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
2196
    if (defined('DB_PASSWD_CLEAR') === false) {
2197
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
2198
    }
2199
    DB::$host = DB_HOST;
2200
    DB::$user = DB_USER;
2201
    DB::$password = DB_PASSWD_CLEAR;
2202
    DB::$dbName = DB_NAME;
2203
    DB::$port = DB_PORT;
2204
    DB::$encoding = DB_ENCODING;
2205
2206
    if (file_exists($tp_config_file) === false || $action === 'rebuild') {
2207
        // perform a copy
2208
        if (file_exists($tp_config_file)) {
2209
            if (!copy($tp_config_file, $tp_config_file . '.' . date('Y_m_d_His', time()))) {
2210
                return "ERROR: Could not copy file '" . $tp_config_file . "'";
2211
            }
2212
        }
2213
2214
2215
        // regenerate
2216
        $data = array();
2217
        $data[0] = "<?php\n";
2218
        $data[1] = "global \$SETTINGS;\n";
2219
        $data[2] = "\$SETTINGS = array (\n";
2220
        $rows = DB::query(
2221
            'SELECT * FROM ' . prefixTable('misc') . ' WHERE type=%s',
2222
            'admin'
2223
        );
2224
        foreach ($rows as $record) {
2225
            array_push($data, "    '" . $record['intitule'] . "' => '" . $record['valeur'] . "',\n");
2226
        }
2227
        array_push($data, ");\n");
2228
        $data = array_unique($data);
2229
        // ---
2230
    } elseif ($action === 'update' && empty($field) === false) {
2231
        $data = file($tp_config_file);
2232
        $inc = 0;
2233
        $bFound = false;
2234
        foreach ($data as $line) {
2235
            if (stristr($line, ');')) {
2236
                break;
2237
            }
2238
2239
            if (stristr($line, "'" . $field . "' => '")) {
2240
                $data[$inc] = "    '" . $field . "' => '" . filter_var($value, FILTER_SANITIZE_STRING) . "',\n";
2241
                $bFound = true;
2242
                break;
2243
            }
2244
            ++$inc;
2245
        }
2246
        if ($bFound === false) {
2247
            $data[($inc)] = "    '" . $field . "' => '" . filter_var($value, FILTER_SANITIZE_STRING) . "',\n);\n";
2248
        }
2249
    }
2250
2251
    // update file
2252
    file_put_contents($tp_config_file, implode('', isset($data) ? $data : array()));
2253
2254
    return true;
2255
}
2256
2257
2258
/**
2259
 * Permits to replace &#92; to permit correct display
2260
 *
2261
 * @param string $input Some text
2262
 *
2263
 * @return string
2264
 */
2265
function handleBackslash($input)
2266
{
2267
    return str_replace('&amp;#92;', '&#92;', $input);
2268
}
2269
2270
/*
2271
** Permits to loas settings
2272
*/
2273
function loadSettings()
2274
{
2275
    global $SETTINGS;
2276
2277
    /* LOAD CPASSMAN SETTINGS */
2278
    if (!isset($SETTINGS['loaded']) || $SETTINGS['loaded'] != 1) {
2279
        $SETTINGS['duplicate_folder'] = 0; //by default, this is set to 0;
2280
        $SETTINGS['duplicate_item'] = 0; //by default, this is set to 0;
2281
        $SETTINGS['number_of_used_pw'] = 5; //by default, this value is set to 5;
2282
        $settings = array();
2283
2284
        $rows = DB::query(
2285
            'SELECT * FROM ' . prefixTable('misc') . ' WHERE type=%s_type OR type=%s_type2',
2286
            array(
2287
                'type' => 'admin',
2288
                'type2' => 'settings',
2289
            )
2290
        );
2291
        foreach ($rows as $record) {
2292
            if ($record['type'] === 'admin') {
2293
                $SETTINGS[$record['intitule']] = $record['valeur'];
2294
            } else {
2295
                $settings[$record['intitule']] = $record['valeur'];
2296
            }
2297
        }
2298
        $SETTINGS['loaded'] = 1;
2299
        $SETTINGS['default_session_expiration_time'] = 5;
2300
    }
2301
}
2302
2303
/*
2304
** check if folder has custom fields.
2305
** Ensure that target one also has same custom fields
2306
*/
2307
function checkCFconsistency($source_id, $target_id)
2308
{
2309
    $source_cf = array();
2310
    $rows = DB::QUERY(
2311
        'SELECT id_category
2312
        FROM ' . prefixTable('categories_folders') . '
2313
        WHERE id_folder = %i',
2314
        $source_id
2315
    );
2316
    foreach ($rows as $record) {
2317
        array_push($source_cf, $record['id_category']);
2318
    }
2319
2320
    $target_cf = array();
2321
    $rows = DB::QUERY(
2322
        'SELECT id_category
2323
        FROM ' . prefixTable('categories_folders') . '
2324
        WHERE id_folder = %i',
2325
        $target_id
2326
    );
2327
    foreach ($rows as $record) {
2328
        array_push($target_cf, $record['id_category']);
2329
    }
2330
2331
    $cf_diff = array_diff($source_cf, $target_cf);
2332
    if (count($cf_diff) > 0) {
2333
        return false;
2334
    }
2335
2336
    return true;
2337
}
2338
2339
/**
2340
 * Will encrypte/decrypt a fil eusing Defuse.
2341
 *
2342
 * @param string $type        can be either encrypt or decrypt
2343
 * @param string $source_file path to source file
2344
 * @param string $target_file path to target file
2345
 * @param array  $SETTINGS    Settings
2346
 * @param string $password    A password
2347
 *
2348
 * @return string|boolean
2349
 */
2350
function prepareFileWithDefuse(
2351
    $type,
2352
    $source_file,
2353
    $target_file,
2354
    $SETTINGS,
2355
    $password = null
2356
) {
2357
    // Load AntiXSS
2358
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/voku/helper/AntiXSS.php';
2359
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/voku/helper/ASCII.php';
2360
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/voku/helper/UTF8.php';
2361
    $antiXss = new voku\helper\AntiXSS();
2362
2363
    // Protect against bad inputs
2364
    if (is_array($source_file) === true || is_array($target_file) === true) {
2365
        return 'error_cannot_be_array';
2366
    }
2367
2368
    // Sanitize
2369
    $source_file = $antiXss->xss_clean($source_file);
2370
    $target_file = $antiXss->xss_clean($target_file);
2371
2372
    if (empty($password) === true || is_null($password) === true) {
2373
        // get KEY to define password
2374
        $ascii_key = file_get_contents(SECUREPATH . '/teampass-seckey.txt');
2375
        $password = \Defuse\Crypto\Key::loadFromAsciiSafeString($ascii_key);
2376
    }
2377
2378
    $err = '';
2379
    if ($type === 'decrypt') {
2380
        // Decrypt file
2381
        $err = defuseFileDecrypt(
2382
            $source_file,
2383
            $target_file,
2384
            $SETTINGS,
2385
            /** @scrutinizer ignore-type */ $password
2386
        );
2387
        // ---
2388
    } elseif ($type === 'encrypt') {
2389
        // Encrypt file
2390
        $err = defuseFileEncrypt(
2391
            $source_file,
2392
            $target_file,
2393
            $SETTINGS,
2394
            /** @scrutinizer ignore-type */ $password
2395
        );
2396
    }
2397
2398
    // return error
2399
    return empty($err) === false ? $err : "";
2400
}
2401
2402
/**
2403
 * Encrypt a file with Defuse.
2404
 *
2405
 * @param string $source_file path to source file
2406
 * @param string $target_file path to target file
2407
 * @param array  $SETTINGS    Settings
2408
 * @param string $password    A password
2409
 *
2410
 * @return string|bool
2411
 */
2412
function defuseFileEncrypt(
2413
    $source_file,
2414
    $target_file,
2415
    $SETTINGS,
2416
    $password = null
2417
) {
2418
    // load PhpEncryption library
2419
    $path_to_encryption = '/includes/libraries/Encryption/Encryption/';
2420
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Crypto.php';
2421
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Encoding.php';
2422
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'DerivedKeys.php';
2423
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Key.php';
2424
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'KeyOrPassword.php';
2425
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'File.php';
2426
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'RuntimeTests.php';
2427
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'KeyProtectedByPassword.php';
2428
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Core.php';
2429
2430
    try {
2431
        \Defuse\Crypto\File::encryptFileWithPassword(
2432
            $source_file,
2433
            $target_file,
2434
            $password
2435
        );
2436
    } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
2437
        $err = 'wrong_key';
2438
    } catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) {
2439
        $err = $ex;
2440
    } catch (Defuse\Crypto\Exception\IOException $ex) {
2441
        $err = $ex;
2442
    }
2443
2444
    // return error
2445
    return empty($err) === false ? $err : true;
2446
}
2447
2448
/**
2449
 * Decrypt a file with Defuse.
2450
 *
2451
 * @param string $source_file path to source file
2452
 * @param string $target_file path to target file
2453
 * @param array  $SETTINGS    Settings
2454
 * @param string $password    A password
2455
 *
2456
 * @return string|bool
2457
 */
2458
function defuseFileDecrypt(
2459
    $source_file,
2460
    $target_file,
2461
    $SETTINGS,
2462
    $password = null
2463
) {
2464
    // load PhpEncryption library
2465
    $path_to_encryption = '/includes/libraries/Encryption/Encryption/';
2466
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Crypto.php';
2467
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Encoding.php';
2468
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'DerivedKeys.php';
2469
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Key.php';
2470
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'KeyOrPassword.php';
2471
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'File.php';
2472
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'RuntimeTests.php';
2473
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'KeyProtectedByPassword.php';
2474
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Core.php';
2475
2476
    try {
2477
        \Defuse\Crypto\File::decryptFileWithPassword(
2478
            $source_file,
2479
            $target_file,
2480
            $password
2481
        );
2482
    } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
2483
        $err = 'wrong_key';
2484
    } catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) {
2485
        $err = $ex;
2486
    } catch (Defuse\Crypto\Exception\IOException $ex) {
2487
        $err = $ex;
2488
    }
2489
2490
    // return error
2491
    return empty($err) === false ? $err : true;
2492
}
2493
2494
/*
2495
* NOT TO BE USED
2496
*/
2497
/**
2498
 * Undocumented function.
2499
 *
2500
 * @param string $text Text to debug
2501
 */
2502
function debugTeampass($text)
2503
{
2504
    $debugFile = fopen('D:/wamp64/www/TeamPass/debug.txt', 'r+');
2505
    if ($debugFile !== false) {
2506
        fputs($debugFile, $text);
2507
        fclose($debugFile);
2508
    }    
2509
}
2510
2511
/**
2512
 * DELETE the file with expected command depending on server type.
2513
 *
2514
 * @param string $file     Path to file
2515
 * @param array  $SETTINGS Teampass settings
2516
 */
2517
function fileDelete($file, $SETTINGS)
2518
{
2519
    // Load AntiXSS
2520
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/voku/helper/ASCII.php';
2521
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/voku/helper/UTF8.php';
2522
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/voku/helper/AntiXSS.php';
2523
    $antiXss = new voku\helper\AntiXSS();
2524
2525
    $file = $antiXss->xss_clean($file);
2526
    if (is_file($file)) {
2527
        unlink($file);
2528
    }
2529
}
2530
2531
/**
2532
 * Permits to extract the file extension.
2533
 *
2534
 * @param string $file File name
2535
 *
2536
 * @return string
2537
 */
2538
function getFileExtension($file)
2539
{
2540
    if (strpos($file, '.') === false) {
2541
        return $file;
2542
    }
2543
2544
    return substr($file, strrpos($file, '.') + 1);
2545
}
2546
2547
/**
2548
 * Performs chmod operation on subfolders.
2549
 *
2550
 * @param string $dir             Parent folder
2551
 * @param int    $dirPermissions  New permission on folders
2552
 * @param int    $filePermissions New permission on files
2553
 *
2554
 * @return bool
2555
 */
2556
function chmodRecursive($dir, $dirPermissions, $filePermissions)
2557
{
2558
    $pointer_dir = opendir($dir);
2559
    if ($pointer_dir === false) {
2560
        return false;
2561
    }  
2562
    $res = true;
2563
    while (false !== ($file = readdir($pointer_dir))) {
2564
        if (($file === '.') || ($file === '..')) {
2565
            continue;
2566
        }
2567
2568
        $fullPath = $dir . '/' . $file;
2569
2570
        if (is_dir($fullPath)) {
2571
            if ($res = @chmod($fullPath, $dirPermissions)) {
2572
                $res = @chmodRecursive($fullPath, $dirPermissions, $filePermissions);
2573
            }
2574
        } else {
2575
            $res = chmod($fullPath, $filePermissions);
2576
        }
2577
        if (!$res) {
2578
            closedir($pointer_dir);
2579
2580
            return false;
2581
        }
2582
    }
2583
    closedir($pointer_dir);
2584
    if (is_dir($dir) && $res) {
2585
        $res = @chmod($dir, $dirPermissions);
2586
    }
2587
2588
    return $res;
2589
}
2590
2591
/**
2592
 * Check if user can access to this item.
2593
 *
2594
 * @param int $item_id ID of item
2595
 */
2596
function accessToItemIsGranted($item_id, $SETTINGS)
2597
{
2598
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
2599
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
2600
2601
    // Prepare superGlobal variables
2602
    $session_groupes_visibles = $superGlobal->get('groupes_visibles', 'SESSION');
2603
    $session_list_restricted_folders_for_items = $superGlobal->get('list_restricted_folders_for_items', 'SESSION');
2604
2605
    // Load item data
2606
    $data = DB::queryFirstRow(
2607
        'SELECT id_tree
2608
        FROM ' . prefixTable('items') . '
2609
        WHERE id = %i',
2610
        $item_id
2611
    );
2612
2613
    // Check if user can access this folder
2614
    if (in_array($data['id_tree'], $session_groupes_visibles) === false) {
2615
        // Now check if this folder is restricted to user
2616
        if (isset($session_list_restricted_folders_for_items[$data['id_tree']]) === true
2617
            && in_array($item_id, $session_list_restricted_folders_for_items[$data['id_tree']]) === false
2618
        ) {
2619
            return 'ERR_FOLDER_NOT_ALLOWED';
2620
        }
2621
    }
2622
2623
    return true;
2624
}
2625
2626
/**
2627
 * Creates a unique key.
2628
 *
2629
 * @param integer $lenght Key lenght
2630
 *
2631
 * @return string
2632
 */
2633
function uniqidReal($lenght = 13)
2634
{
2635
    if (function_exists('random_bytes')) {
2636
        $bytes = random_bytes(intval(ceil($lenght / 2)));
2637
    } elseif (function_exists('openssl_random_pseudo_bytes')) {
2638
        $bytes = openssl_random_pseudo_bytes(intval(ceil($lenght / 2)));
2639
    } else {
2640
        throw new Exception('no cryptographically secure random function available');
2641
    }
2642
2643
    return substr(bin2hex($bytes), 0, $lenght);
2644
}
2645
2646
/**
2647
 * Obfuscate an email.
2648
 *
2649
 * @param string $email Email address
2650
 *
2651
 * @return string
2652
 */
2653
function obfuscateEmail($email)
2654
{
2655
    $prop = 2;
2656
    $start = '';
2657
    $end = '';
2658
    $domain = substr(strrchr($email, '@'), 1);
2659
    $mailname = str_replace($domain, '', $email);
2660
    $name_l = strlen($mailname);
2661
    $domain_l = strlen($domain);
2662
    for ($i = 0; $i <= $name_l / $prop - 1; ++$i) {
2663
        $start .= 'x';
2664
    }
2665
2666
    for ($i = 0; $i <= $domain_l / $prop - 1; ++$i) {
2667
        $end .= 'x';
2668
    }
2669
2670
    return substr_replace($mailname, $start, 2, $name_l / $prop)
2671
        . substr_replace($domain, $end, 2, $domain_l / $prop);
2672
}
2673
2674
2675
2676
2677
2678
/**
2679
 * Perform a Query.
2680
 *
2681
 * @param array  $SETTINGS Teamapss settings
2682
 * @param string $fields   Fields to use
2683
 * @param string $table    Table to use
2684
 *
2685
 * @return array
2686
 */
2687
function performDBQuery($SETTINGS, $fields, $table)
2688
{
2689
    // include librairies & connect to DB
2690
    include_once $SETTINGS['cpassman_dir'] . '/includes/config/settings.php';
2691
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
2692
    if (defined('DB_PASSWD_CLEAR') === false) {
2693
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
2694
    }
2695
    DB::$host = DB_HOST;
2696
    DB::$user = DB_USER;
2697
    DB::$password = DB_PASSWD_CLEAR;
2698
    DB::$dbName = DB_NAME;
2699
    DB::$port = DB_PORT;
2700
    DB::$encoding = DB_ENCODING;
2701
2702
    // Insert log in DB
2703
    return DB::query(
2704
        'SELECT ' . $fields . '
2705
        FROM ' . prefixTable($table)
2706
    );
2707
}
2708
2709
/**
2710
 * Undocumented function.
2711
 *
2712
 * @param int $bytes Size of file
2713
 *
2714
 * @return string
2715
 */
2716
function formatSizeUnits($bytes)
2717
{
2718
    if ($bytes >= 1073741824) {
2719
        $bytes = number_format($bytes / 1073741824, 2) . ' GB';
2720
    } elseif ($bytes >= 1048576) {
2721
        $bytes = number_format($bytes / 1048576, 2) . ' MB';
2722
    } elseif ($bytes >= 1024) {
2723
        $bytes = number_format($bytes / 1024, 2) . ' KB';
2724
    } elseif ($bytes > 1) {
2725
        $bytes = $bytes . ' bytes';
2726
    } elseif ($bytes == 1) {
2727
        $bytes = $bytes . ' byte';
2728
    } else {
2729
        $bytes = '0 bytes';
2730
    }
2731
2732
    return $bytes;
2733
}
2734
2735
/**
2736
 * Generate user pair of keys.
2737
 *
2738
 * @param string $userPwd User password
2739
 *
2740
 * @return array
2741
 */
2742
function generateUserKeys($userPwd)
2743
{
2744
    // include library
2745
    include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php';
2746
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php';
2747
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
2748
2749
    // Load classes
2750
    $rsa = new Crypt_RSA();
2751
    $cipher = new Crypt_AES();
2752
2753
    // Create the private and public key
2754
    $res = $rsa->createKey(4096);
2755
2756
    // Encrypt the privatekey
2757
    $cipher->setPassword($userPwd);
2758
    $privatekey = $cipher->encrypt($res['privatekey']);
2759
2760
    return array(
2761
        'private_key' => base64_encode($privatekey),
2762
        'public_key' => base64_encode($res['publickey']),
2763
        'private_key_clear' => base64_encode($res['privatekey']),
2764
    );
2765
}
2766
2767
/**
2768
 * Permits to decrypt the user's privatekey.
2769
 *
2770
 * @param string $userPwd        User password
2771
 * @param string $userPrivateKey User private key
2772
 *
2773
 * @return string
2774
 */
2775
function decryptPrivateKey($userPwd, $userPrivateKey)
2776
{
2777
    if (empty($userPwd) === false) {
2778
        include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
2779
2780
        // Load classes
2781
        $cipher = new Crypt_AES();
2782
2783
        // Encrypt the privatekey
2784
        $cipher->setPassword($userPwd);
2785
2786
        return base64_encode($cipher->decrypt(base64_decode($userPrivateKey)));
2787
    }
2788
}
2789
2790
/**
2791
 * Permits to encrypt the user's privatekey.
2792
 *
2793
 * @param string $userPwd        User password
2794
 * @param string $userPrivateKey User private key
2795
 *
2796
 * @return string
2797
 */
2798
function encryptPrivateKey($userPwd, $userPrivateKey)
2799
{
2800
    if (empty($userPwd) === false) {
2801
        include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
2802
2803
        // Load classes
2804
        $cipher = new Crypt_AES();
2805
2806
        // Encrypt the privatekey
2807
        $cipher->setPassword($userPwd);
2808
2809
        return base64_encode($cipher->encrypt(base64_decode($userPrivateKey)));
2810
    }
2811
}
2812
2813
2814
/**
2815
 * Encrypts a string using AES.
2816
 *
2817
 * @param string $data String to encrypt
2818
 *
2819
 * @return array
2820
 */
2821
function doDataEncryption($data)
2822
{
2823
    // Includes
2824
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
2825
2826
    // Load classes
2827
    $cipher = new Crypt_AES(CRYPT_AES_MODE_CBC);
2828
2829
    // Generate an object key
2830
    $objectKey = uniqidReal(32);
2831
2832
    // Set it as password
2833
    $cipher->setPassword($objectKey);
2834
2835
    return array(
2836
        'encrypted' => base64_encode($cipher->encrypt($data)),
2837
        'objectKey' => base64_encode($objectKey),
2838
    );
2839
}
2840
2841
/**
2842
 * Decrypts a string using AES.
2843
 *
2844
 * @param string $data Encrypted data
2845
 * @param string $key  Key to uncrypt
2846
 *
2847
 * @return string
2848
 */
2849
function doDataDecryption($data, $key)
2850
{
2851
    // Includes
2852
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
2853
2854
    // Load classes
2855
    $cipher = new Crypt_AES();
2856
2857
    // Set the object key
2858
    $cipher->setPassword(base64_decode($key));
2859
2860
    return base64_encode($cipher->decrypt(base64_decode($data)));
2861
}
2862
2863
/**
2864
 * Encrypts using RSA a string using a public key.
2865
 *
2866
 * @param string $key       Key to be encrypted
2867
 * @param string $publicKey User public key
2868
 *
2869
 * @return string
2870
 */
2871
function encryptUserObjectKey($key, $publicKey)
2872
{
2873
    // Includes
2874
    include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php';
2875
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php';
2876
2877
    // Load classes
2878
    $rsa = new Crypt_RSA();
2879
    $rsa->loadKey(base64_decode($publicKey));
2880
2881
    // Encrypt
2882
    return base64_encode($rsa->encrypt(base64_decode($key)));
2883
}
2884
2885
/**
2886
 * Decrypts using RSA an encrypted string using a private key.
2887
 *
2888
 * @param string $key        Encrypted key
2889
 * @param string $privateKey User private key
2890
 *
2891
 * @return string
2892
 */
2893
function decryptUserObjectKey($key, $privateKey)
2894
{
2895
    // Includes
2896
    include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php';
2897
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php';
2898
2899
    // Load classes
2900
    $rsa = new Crypt_RSA();
2901
    $rsa->loadKey(base64_decode($privateKey));
2902
//echo $privateKey." ;; ".$key;
2903
    // Decrypt
2904
    return base64_encode($rsa->decrypt(base64_decode($key)));
2905
}
2906
2907
/**
2908
 * Encrypts a file.
2909
 *
2910
 * @param string $fileInName File name
2911
 * @param string $fileInPath Path to file
2912
 *
2913
 * @return array
2914
 */
2915
function encryptFile($fileInName, $fileInPath)
2916
{
2917
    if (defined('FILE_BUFFER_SIZE') === false) {
2918
        define('FILE_BUFFER_SIZE', 128 * 1024);
2919
    }
2920
2921
    // Includes
2922
    include_once '../includes/config/include.php';
2923
    include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php';
2924
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php';
2925
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
2926
2927
    // Load classes
2928
    $cipher = new Crypt_AES();
2929
2930
    // Generate an object key
2931
    $objectKey = uniqidReal(32);
2932
2933
    // Set it as password
2934
    $cipher->setPassword($objectKey);
2935
2936
    // Prevent against out of memory
2937
    $cipher->enableContinuousBuffer();
2938
    //$cipher->disablePadding();
2939
2940
    // Encrypt the file content
2941
    $plaintext = file_get_contents(
2942
        filter_var($fileInPath . '/' . $fileInName, FILTER_SANITIZE_URL)
2943
    );
2944
2945
    $ciphertext = $cipher->encrypt($plaintext);
2946
2947
    // Save new file
2948
    $hash = md5($plaintext);
2949
    $fileOut = $fileInPath . '/' . TP_FILE_PREFIX . $hash;
2950
    file_put_contents($fileOut, $ciphertext);
2951
    unlink($fileInPath . '/' . $fileInName);
2952
2953
    return array(
2954
        'fileHash' => base64_encode($hash),
2955
        'objectKey' => base64_encode($objectKey),
2956
    );
2957
}
2958
2959
/**
2960
 * Decrypt a file.
2961
 *
2962
 * @param string $fileName File name
2963
 * @param string $filePath Path to file
2964
 * @param string $key      Key to use
2965
 *
2966
 * @return string
2967
 */
2968
function decryptFile($fileName, $filePath, $key)
2969
{
2970
    if (!defined('FILE_BUFFER_SIZE')) {
2971
        define('FILE_BUFFER_SIZE', 128 * 1024);
2972
    }
2973
2974
    // Includes
2975
    include_once '../includes/config/include.php';
2976
    include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php';
2977
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php';
2978
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
2979
2980
    // Get file name
2981
    $fileName = base64_decode($fileName);
2982
2983
    // Load classes
2984
    $cipher = new Crypt_AES();
2985
2986
    // Set the object key
2987
    $cipher->setPassword(base64_decode($key));
2988
2989
    // Prevent against out of memory
2990
    $cipher->enableContinuousBuffer();
2991
    $cipher->disablePadding();
2992
2993
    // Get file content
2994
    $ciphertext = file_get_contents($filePath . '/' . TP_FILE_PREFIX . $fileName);
2995
2996
    // Decrypt file content and return
2997
    return base64_encode($cipher->decrypt($ciphertext));
2998
}
2999
3000
/**
3001
 * Generate a simple password
3002
 *
3003
 * @param integer $length          Length of string
3004
 * @param boolean $symbolsincluded Allow symbols
3005
 *
3006
 * @return string
3007
 */
3008
function generateQuickPassword($length = 16, $symbolsincluded = true)
3009
{
3010
    // Generate new user password
3011
    $small_letters = range('a', 'z');
3012
    $big_letters = range('A', 'Z');
3013
    $digits = range(0, 9);
3014
    $symbols = $symbolsincluded === true ?
3015
        array('#', '_', '-', '@', '$', '+', '&') : array();
3016
3017
    $res = array_merge($small_letters, $big_letters, $digits, $symbols);
3018
    $count = count($res);
3019
    // first variant
3020
3021
    $random_string = '';
3022
    for ($i = 0; $i < $length; ++$i) {
3023
        $random_string .= $res[random_int(0, $count - 1)];
3024
    }
3025
3026
    return $random_string;
3027
}
3028
3029
/**
3030
 * Permit to store the sharekey of an object for users.
3031
 *
3032
 * @param string $object_name             Type for table selection
3033
 * @param int    $post_folder_is_personal Personal
3034
 * @param int    $post_folder_id          Folder
3035
 * @param int    $post_object_id          Object
3036
 * @param string $objectKey               Object key
3037
 * @param array  $SETTINGS                Teampass settings
3038
 *
3039
 * @return void
3040
 */
3041
function storeUsersShareKey(
3042
    $object_name,
3043
    $post_folder_is_personal,
3044
    $post_folder_id,
3045
    $post_object_id,
3046
    $objectKey,
3047
    $SETTINGS
3048
) {
3049
    // include librairies & connect to DB
3050
    include_once $SETTINGS['cpassman_dir'] . '/includes/config/settings.php';
3051
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
3052
    if (defined('DB_PASSWD_CLEAR') === false) {
3053
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
3054
    }
3055
    DB::$host = DB_HOST;
3056
    DB::$user = DB_USER;
3057
    DB::$password = DB_PASSWD_CLEAR;
3058
    DB::$dbName = DB_NAME;
3059
    DB::$port = DB_PORT;
3060
    DB::$encoding = DB_ENCODING;
3061
3062
    // Delete existing entries for this object
3063
    DB::delete(
3064
        $object_name,
3065
        'object_id = %i',
3066
        $post_object_id
3067
    );
3068
3069
    // Superglobals
3070
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
3071
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
3072
3073
    // Prepare superGlobal variables
3074
    $sessionPpersonaFolders = $superGlobal->get('personal_folders', 'SESSION');
3075
    $sessionUserId = $superGlobal->get('user_id', 'SESSION');
3076
    $sessionUserPublicKey = $superGlobal->get('public_key', 'SESSION', 'user');
3077
3078
    if (
3079
        (int) $post_folder_is_personal === 1
3080
        && in_array($post_folder_id, $sessionPpersonaFolders) === true
3081
    ) {
3082
        // If this is a personal object
3083
        // Only create the sharekey for user
3084
        DB::insert(
3085
            $object_name,
3086
            array(
3087
                'object_id' => $post_object_id,
3088
                'user_id' => $sessionUserId,
3089
                'share_key' => encryptUserObjectKey($objectKey, $sessionUserPublicKey),
3090
            )
3091
        );
3092
    } else {
3093
        // This is a public object
3094
        // Create sharekey for each user
3095
        $users = DB::query(
3096
            'SELECT id, public_key
3097
            FROM ' . prefixTable('users') . '
3098
            WHERE id NOT IN ("' . OTV_USER_ID . '","' . SSH_USER_ID . '","' . API_USER_ID . '")
3099
            AND public_key != ""'
3100
        );
3101
        foreach ($users as $user) {
3102
            // Insert in DB the new object key for this item by user
3103
            DB::insert(
3104
                $object_name,
3105
                array(
3106
                    'object_id' => $post_object_id,
3107
                    'user_id' => $user['id'],
3108
                    'share_key' => encryptUserObjectKey(
3109
                        $objectKey,
3110
                        $user['public_key']
3111
                    ),
3112
                )
3113
            );
3114
        }
3115
    }
3116
}
3117
3118
/**
3119
 * Is this string base64 encoded?
3120
 *
3121
 * @param string $str Encoded string?
3122
 *
3123
 * @return bool
3124
 */
3125
function isBase64($str)
3126
{
3127
    $str = (string) trim($str);
3128
3129
    if (!isset($str[0])) {
3130
        return false;
3131
    }
3132
3133
    $base64String = (string) base64_decode($str, true);
3134
    if ($base64String && base64_encode($base64String) === $str) {
3135
        return true;
3136
    }
3137
3138
    return false;
3139
}
3140
3141
/**
3142
 * Undocumented function
3143
 *
3144
 * @param string $field Parameter
3145
 *
3146
 * @return array|bool|resource|string
3147
 */
3148
function filterString($field)
3149
{
3150
    // Sanitize string
3151
    $field = filter_var(trim($field), FILTER_SANITIZE_STRING);
3152
    if (empty($field) === false) {
3153
        // Load AntiXSS
3154
        include_once '../includes/libraries/voku/helper/AntiXSS.php';
3155
        $antiXss = new voku\helper\AntiXSS();
3156
        // Return
3157
        return $antiXss->xss_clean($field);
3158
    }
3159
3160
    return false;
3161
}
3162
3163
3164
/**
3165
 * CHeck if provided credentials are allowed on server
3166
 *
3167
 * @param string $login    User Login
3168
 * @param string $password User Pwd
3169
 * @param array  $SETTINGS Teampass settings
3170
 *
3171
 * @return bool
3172
 */
3173
function ldapCheckUserPassword($login, $password, $SETTINGS)
3174
{
3175
    // Build ldap configuration array
3176
    $config = [
3177
        // Mandatory Configuration Options
3178
        'hosts'            => [$SETTINGS['ldap_domain_controler']],
3179
        'base_dn'          => $SETTINGS['ldap_search_base'],
3180
        'username'         => $SETTINGS['ldap_bind_dn'],
3181
        'password'         => $SETTINGS['ldap_bind_passwd'],
3182
3183
        // Optional Configuration Options
3184
        'port'             => $SETTINGS['ldap_port'],
3185
        'use_ssl'          => $SETTINGS['ldap_ssl'] === 1 ? true : false,
3186
        'use_tls'          => $SETTINGS['ldap_tls'] === 1 ? true : false,
3187
        'version'          => 3,
3188
        'timeout'          => 5,
3189
        'follow_referrals' => false,
3190
3191
        // Custom LDAP Options
3192
        'options' => [
3193
            // See: http://php.net/ldap_set_option
3194
            LDAP_OPT_X_TLS_REQUIRE_CERT => LDAP_OPT_X_TLS_HARD
3195
        ]
3196
    ];
3197
3198
    // Load expected libraries
3199
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Tightenco/Collect/Support/Traits/Macroable.php';
3200
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Tightenco/Collect/Support/Arr.php';
3201
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/LdapRecord/DetectsErrors.php';
3202
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/LdapRecord/Connection.php';
3203
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/LdapRecord/Ldap.php';
3204
3205
    $ad = new SplClassLoader('LdapRecord', '../includes/libraries');
3206
    $ad->register();
3207
    $connection = new Connection($config);
3208
3209
    // COnnect to LDAP
3210
    try {
3211
        $connection->connect();
3212
3213
    } catch (\LdapRecord\Auth\BindException $e) {
3214
        $error = $e->getDetailedError();
3215
3216
        echo "Error : ".$error->getErrorCode()." - ".$error->getErrorMessage(). "<br>".$error->getDiagnosticMessage();
3217
        return false;
3218
    }
3219
3220
    // Authenticate user
3221
    try {
3222
        $connection->auth()->attempt($SETTINGS['ldap_user_attribute']."=".$login.",".$SETTINGS['ldap_search_base'], $password, $stayAuthenticated = true);
3223
3224
    } catch (\LdapRecord\Auth\BindException $e) {
3225
        $error = $e->getDetailedError();
3226
        
3227
        echo "Error : ".$error->getErrorCode()." - ".$error->getErrorMessage(). "<br>".$error->getDiagnosticMessage();
3228
        return false;
3229
    }
3230
3231
    return true;
3232
}
3233
3234
3235
3236
3237
/**
3238
 * Removes from DB all sharekeys of this user
3239
 *
3240
 * @param integer $userId   User's id
3241
 * @param array   $SETTINGS Teampass settings
3242
 *
3243
 * @return void|bool
3244
 */
3245
function deleteUserObjetsKeys($userId, $SETTINGS)
3246
{
3247
    // include librairies & connect to DB
3248
    include_once $SETTINGS['cpassman_dir'] . '/includes/config/settings.php';
3249
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
3250
    if (defined('DB_PASSWD_CLEAR') === false) {
3251
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
3252
    }
3253
    DB::$host = DB_HOST;
3254
    DB::$user = DB_USER;
3255
    DB::$password = DB_PASSWD_CLEAR;
3256
    DB::$dbName = DB_NAME;
3257
    DB::$port = DB_PORT;
3258
    DB::$encoding = DB_ENCODING;
3259
3260
    // Remove all item sharekeys items
3261
    DB::delete(
3262
        prefixTable('sharekeys_items'),
3263
        'user_id = %i',
3264
        $userId
3265
    );
3266
3267
    // Remove all item sharekeys files
3268
    DB::delete(
3269
        prefixTable('sharekeys_files'),
3270
        'user_id = %i',
3271
        $userId
3272
    );
3273
3274
    // Remove all item sharekeys fields
3275
    DB::delete(
3276
        prefixTable('sharekeys_fields'),
3277
        'user_id = %i',
3278
        $userId
3279
    );
3280
3281
    // Remove all item sharekeys logs
3282
    DB::delete(
3283
        prefixTable('sharekeys_logs'),
3284
        'user_id = %i',
3285
        $userId
3286
    );
3287
3288
    // Remove all item sharekeys suggestions
3289
    DB::delete(
3290
        prefixTable('sharekeys_suggestions'),
3291
        'user_id = %i',
3292
        $userId
3293
    );
3294
3295
    return false;
3296
}
3297