Passed
Push — teampass_3.0 ( 84b7c5...4b45d9 )
by Nils
09:36
created

formatSizeUnits()   A

Complexity

Conditions 6
Paths 6

Size

Total Lines 17
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 6
eloc 13
c 2
b 0
f 0
nc 6
nop 1
dl 0
loc 17
rs 9.2222
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
21
if (isset($_SESSION['CPM']) === false || (int) $_SESSION['CPM'] !== 1) {
22
    die('Hacking attempt...');
23
}
24
25
// Load config if $SETTINGS not defined
26
if (isset($SETTINGS['cpassman_dir']) === false || empty($SETTINGS['cpassman_dir']) === true) {
27
    if (file_exists('../includes/config/tp.config.php')) {
28
        include_once '../includes/config/tp.config.php';
29
    } elseif (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
    } else {
34
        throw new Exception("Error file '/includes/config/tp.config.php' not exists", 1);
35
    }
36
}
37
38
header('Content-type: text/html; charset=utf-8');
39
header('Cache-Control: no-cache, must-revalidate');
40
41
/**
42
 * Convert language code to string.
43
 *
44
 * @param string $string String to get
45
 *
46
 * @return string
47
 */
48
function langHdl($string)
49
{
50
    if (empty($string) === true) {
51
        // Manage error
52
        return 'ERROR in language strings!';
53
    }
54
55
    // Load superglobal
56
    if (file_exists('../includes/libraries/protect/SuperGlobal/SuperGlobal.php')) {
57
        include_once '../includes/libraries/protect/SuperGlobal/SuperGlobal.php';
58
    } elseif (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
    } else {
63
        throw new Exception("Error file '/includes/libraries/protect/SuperGlobal/SuperGlobal.php' not exists", 1);
64
    }
65
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
66
67
    // Get language string
68
    $session_language = $superGlobal->get(trim($string), 'SESSION', 'lang');
69
70
    if (isset($session_language) === false) {
71
        // Manage error
72
        return 'ERROR in language strings!';
73
    } else {
74
        return str_replace(
75
            array('"', "'"),
76
            array('&quot;', '&apos;'),
77
            $session_language
78
        );
79
    }
80
}
81
82
//Generate N# of random bits for use as salt
83
/**
84
 * Undocumented function.
85
 *
86
 * @param int $size Length
87
 *
88
 * @return array
89
 */
90
function getBits($size)
91
{
92
    $str = '';
93
    $var_x = $size + 10;
94
    for ($var_i = 0; $var_i < $var_x; ++$var_i) {
95
        $str .= base_convert(mt_rand(1, 36), 10, 36);
96
    }
97
98
    return substr($str, 0, $size);
0 ignored issues
show
Bug Best Practice introduced by
The expression return substr($str, 0, $size) returns the type string which is incompatible with the documented return type array.
Loading history...
99
}
100
101
//generate pbkdf2 compliant hash
102
function strHashPbkdf2($var_p, $var_s, $var_c, $var_kl, $var_a = 'sha256', $var_st = 0)
103
{
104
    $var_kb = $var_st + $var_kl; // Key blocks to compute
105
    $var_dk = ''; // Derived key
106
107
    for ($block = 1; $block <= $var_kb; ++$block) { // Create key
108
        $var_ib = $var_h = hash_hmac($var_a, $var_s . pack('N', $block), $var_p, true); // Initial hash for this block
109
        for ($var_i = 1; $var_i < $var_c; ++$var_i) { // Perform block iterations
110
            $var_ib ^= ($var_h = hash_hmac($var_a, $var_h, $var_p, true)); // XOR each iterate
111
        }
112
        $var_dk .= $var_ib; // Append iterated block
113
    }
114
115
    return substr($var_dk, $var_st, $var_kl); // Return derived key of correct length
116
}
117
118
/**
119
 * stringUtf8Decode().
120
 *
121
 * utf8_decode
122
 */
123
function stringUtf8Decode($string)
124
{
125
    return str_replace(' ', '+', utf8_decode($string));
126
}
127
128
/**
129
 * encryptOld().
130
 *
131
 * crypt a string
132
 *
133
 * @param string $text
134
 */
135
function encryptOld($text, $personalSalt = '')
136
{
137
    if (empty($personalSalt) === false) {
138
        return trim(
139
            base64_encode(
140
                mcrypt_encrypt(
1 ignored issue
show
Deprecated Code introduced by
The function mcrypt_encrypt() has been deprecated: 7.1 ( Ignorable by Annotation )

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

140
                /** @scrutinizer ignore-deprecated */ mcrypt_encrypt(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
141
                    MCRYPT_RIJNDAEL_256,
142
                    $personalSalt,
143
                    $text,
144
                    MCRYPT_MODE_ECB,
145
                    mcrypt_create_iv(
0 ignored issues
show
Deprecated Code introduced by
The function mcrypt_create_iv() has been deprecated: 7.1 ( Ignorable by Annotation )

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

145
                    /** @scrutinizer ignore-deprecated */ mcrypt_create_iv(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
146
                        mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB),
0 ignored issues
show
Deprecated Code introduced by
The function mcrypt_get_iv_size() has been deprecated: 7.1 ( Ignorable by Annotation )

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

146
                        /** @scrutinizer ignore-deprecated */ mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB),

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
147
                        MCRYPT_RAND
148
                    )
149
                )
150
            )
151
        );
152
    }
153
154
    // If $personalSalt is not empty
155
    return trim(
156
        base64_encode(
157
            mcrypt_encrypt(
1 ignored issue
show
Deprecated Code introduced by
The function mcrypt_encrypt() has been deprecated: 7.1 ( Ignorable by Annotation )

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

157
            /** @scrutinizer ignore-deprecated */ mcrypt_encrypt(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
158
                MCRYPT_RIJNDAEL_256,
159
                SALT,
0 ignored issues
show
Bug introduced by
The constant SALT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
160
                $text,
161
                MCRYPT_MODE_ECB,
162
                mcrypt_create_iv(
0 ignored issues
show
Deprecated Code introduced by
The function mcrypt_create_iv() has been deprecated: 7.1 ( Ignorable by Annotation )

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

162
                /** @scrutinizer ignore-deprecated */ mcrypt_create_iv(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
163
                    mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB),
0 ignored issues
show
Deprecated Code introduced by
The function mcrypt_get_iv_size() has been deprecated: 7.1 ( Ignorable by Annotation )

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

163
                    /** @scrutinizer ignore-deprecated */ mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB),

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
164
                    MCRYPT_RAND
165
                )
166
            )
167
        )
168
    );
169
}
170
171
/**
172
 * decryptOld().
173
 *
174
 * decrypt a crypted string
175
 */
176
function decryptOld($text, $personalSalt = '')
177
{
178
    if (!empty($personalSalt)) {
179
        return trim(
180
            mcrypt_decrypt(
0 ignored issues
show
Deprecated Code introduced by
The function mcrypt_decrypt() has been deprecated: 7.1 ( Ignorable by Annotation )

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

180
            /** @scrutinizer ignore-deprecated */ mcrypt_decrypt(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
181
                MCRYPT_RIJNDAEL_256,
182
                $personalSalt,
183
                base64_decode($text),
184
                MCRYPT_MODE_ECB,
185
                mcrypt_create_iv(
0 ignored issues
show
Deprecated Code introduced by
The function mcrypt_create_iv() has been deprecated: 7.1 ( Ignorable by Annotation )

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

185
                /** @scrutinizer ignore-deprecated */ mcrypt_create_iv(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
186
                    mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB),
0 ignored issues
show
Deprecated Code introduced by
The function mcrypt_get_iv_size() has been deprecated: 7.1 ( Ignorable by Annotation )

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

186
                    /** @scrutinizer ignore-deprecated */ mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB),

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
187
                    MCRYPT_RAND
188
                )
189
            )
190
        );
191
    }
192
193
    // No personal SK
194
    return trim(
195
        mcrypt_decrypt(
0 ignored issues
show
Deprecated Code introduced by
The function mcrypt_decrypt() has been deprecated: 7.1 ( Ignorable by Annotation )

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

195
        /** @scrutinizer ignore-deprecated */ mcrypt_decrypt(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
196
            MCRYPT_RIJNDAEL_256,
197
            SALT,
0 ignored issues
show
Bug introduced by
The constant SALT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
198
            base64_decode($text),
199
            MCRYPT_MODE_ECB,
200
            mcrypt_create_iv(
0 ignored issues
show
Deprecated Code introduced by
The function mcrypt_create_iv() has been deprecated: 7.1 ( Ignorable by Annotation )

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

200
            /** @scrutinizer ignore-deprecated */ mcrypt_create_iv(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
201
                mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB),
0 ignored issues
show
Deprecated Code introduced by
The function mcrypt_get_iv_size() has been deprecated: 7.1 ( Ignorable by Annotation )

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

201
                /** @scrutinizer ignore-deprecated */ mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB),

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
202
                MCRYPT_RAND
203
            )
204
        )
205
    );
206
}
207
208
/**
209
 * encrypt().
210
 *
211
 * crypt a string
212
 *
213
 * @param string $decrypted
214
 */
215
/*function encrypt($decrypted, $personalSalt = '')
216
{
217
    global $SETTINGS;
218
219
    if (isset($SETTINGS['cpassman_dir']) === false || empty($SETTINGS['cpassman_dir']) === true) {
220
        require_once '../includes/libraries/Encryption/PBKDF2/PasswordHash.php';
221
    } else {
222
        require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Encryption/PBKDF2/PasswordHash.php';
223
    }
224
225
    if (!empty($personalSalt)) {
226
        $staticSalt = $personalSalt;
227
    } else {
228
        $staticSalt = SALT;
229
    }
230
231
    //set our salt to a variable
232
    // Get 64 random bits for the salt for pbkdf2
233
    $pbkdf2Salt = getBits(64);
234
    // generate a pbkdf2 key to use for the encryption.
235
    $key = substr(pbkdf2('sha256', $staticSalt, $pbkdf2Salt, ITCOUNT, 16 + 32, true), 32, 16);
236
    // Build $init_vect and $ivBase64.  We use a block size of 256 bits (AES compliant)
237
    // and CTR mode.  (Note: ECB mode is inadequate as IV is not used.)
238
    $init_vect = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, 'ctr'), MCRYPT_RAND);
239
240
    //base64 trim
241
    if (strlen($ivBase64 = rtrim(base64_encode($init_vect), '=')) != 43) {
242
        return false;
243
    }
244
    // Encrypt $decrypted
245
    $encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $decrypted, 'ctr', $init_vect);
246
    // MAC the encrypted text
247
    $mac = hash_hmac('sha256', $encrypted, $staticSalt);
248
    // We're done!
249
    return base64_encode($ivBase64 . $encrypted . $mac . $pbkdf2Salt);
250
}
251
/*
252
253
/**
254
 * decrypt().
255
 *
256
 * decrypt a crypted string
257
 */
258
/*function decrypt($encrypted, $personalSalt = '')
259
{
260
    global $SETTINGS;
261
262
    if (isset($SETTINGS['cpassman_dir']) === false || empty($SETTINGS['cpassman_dir']) === true) {
263
        include_once '../includes/libraries/Encryption/PBKDF2/PasswordHash.php';
264
    } else {
265
        include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Encryption/PBKDF2/PasswordHash.php';
266
    }
267
268
    if (!empty($personalSalt)) {
269
        $staticSalt = $personalSalt;
270
    } else {
271
        $staticSalt = file_get_contents(SECUREPATH . '/teampass-seckey.txt');
272
    }
273
    //base64 decode the entire payload
274
    $encrypted = base64_decode($encrypted);
275
    // get the salt
276
    $pbkdf2Salt = substr($encrypted, -64);
277
    //remove the salt from the string
278
    $encrypted = substr($encrypted, 0, -64);
279
    $key = substr(pbkdf2('sha256', $staticSalt, $pbkdf2Salt, ITCOUNT, 16 + 32, true), 32, 16);
280
    // Retrieve $init_vect which is the first 22 characters plus ==, base64_decoded.
281
    $init_vect = base64_decode(substr($encrypted, 0, 43) . '==');
282
    // Remove $init_vect from $encrypted.
283
    $encrypted = substr($encrypted, 43);
284
    // Retrieve $mac which is the last 64 characters of $encrypted.
285
    $mac = substr($encrypted, -64);
286
    // Remove the last 64 chars from encrypted (remove MAC)
287
    $encrypted = substr($encrypted, 0, -64);
288
    //verify the sha256hmac from the encrypted data before even trying to decrypt it
289
    if (hash_hmac('sha256', $encrypted, $staticSalt) != $mac) {
290
        return false;
291
    }
292
    // Decrypt the data.
293
    $decrypted = rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $encrypted, 'ctr', $init_vect), "\0\4");
294
    // Yay!
295
    return $decrypted;
296
}
297
*/
298
299
/**
300
 * genHash().
301
 *
302
 * Generate a hash for user login
303
 *
304
 * @param string $password
305
 */
306
function bCrypt($password, $cost)
307
{
308
    $salt = sprintf('$2y$%02d$', $cost);
309
    if (function_exists('openssl_random_pseudo_bytes')) {
310
        $salt .= bin2hex(openssl_random_pseudo_bytes(11));
311
    } else {
312
        $chars = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
313
        for ($i = 0; $i < 22; ++$i) {
314
            $salt .= $chars[mt_rand(0, 63)];
315
        }
316
    }
317
318
    return crypt($password, $salt);
319
}
320
321
function testHex2Bin($val)
322
{
323
    if (!@hex2bin($val)) {
324
        throw new Exception('ERROR');
325
    }
326
327
    return hex2bin($val);
328
}
329
330
/**
331
 * Defuse cryption function.
332
 *
333
 * @param string $message   what to de/crypt
334
 * @param string $ascii_key key to use
335
 * @param string $type      operation to perform
336
 * @param array  $SETTINGS  Teampass settings
337
 *
338
 * @return array
339
 */
340
function cryption($message, $ascii_key, $type, $SETTINGS)
341
{
342
    $ascii_key = (empty($ascii_key) === true) ? file_get_contents(SECUREPATH . '/teampass-seckey.txt') : $ascii_key;
0 ignored issues
show
Bug introduced by
The constant SECUREPATH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
343
    $err = false;
344
345
    // load PhpEncryption library
346
    if (isset($SETTINGS['cpassman_dir']) === false || empty($SETTINGS['cpassman_dir']) === true) {
347
        $path = '../includes/libraries/Encryption/Encryption/';
348
    } else {
349
        $path = $SETTINGS['cpassman_dir'] . '/includes/libraries/Encryption/Encryption/';
350
    }
351
352
    include_once $path . 'Crypto.php';
353
    include_once $path . 'Encoding.php';
354
    include_once $path . 'DerivedKeys.php';
355
    include_once $path . 'Key.php';
356
    include_once $path . 'KeyOrPassword.php';
357
    include_once $path . 'File.php';
358
    include_once $path . 'RuntimeTests.php';
359
    include_once $path . 'KeyProtectedByPassword.php';
360
    include_once $path . 'Core.php';
361
362
    // convert KEY
363
    $key = \Defuse\Crypto\Key::loadFromAsciiSafeString($ascii_key);
364
365
    try {
366
        if ($type === 'encrypt') {
367
            $text = \Defuse\Crypto\Crypto::encrypt($message, $key);
368
        } else if ($type === 'decrypt') {
369
            $text = \Defuse\Crypto\Crypto::decrypt($message, $key);
370
        }
371
    } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
372
        $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.';
373
    } catch (Defuse\Crypto\Exception\BadFormatException $ex) {
374
        $err = $ex;
375
    } catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) {
376
        $err = $ex;
377
    } catch (Defuse\Crypto\Exception\CryptoException $ex) {
378
        $err = $ex;
379
    } catch (Defuse\Crypto\Exception\IOException $ex) {
380
        $err = $ex;
381
    }
382
    //echo \Defuse\Crypto\Crypto::decrypt($message, $key).' ## ';
383
384
    return array(
385
        'string' => isset($text) ? $text : '',
386
        'error' => $err,
387
    );
388
}
389
390
/**
391
 * Generating a defuse key.
392
 *
393
 * @return string
394
 */
395
function defuse_generate_key()
396
{
397
    // load PhpEncryption library
398
    if (file_exists('../includes/config/tp.config.php') === true) {
399
        $path = '../includes/libraries/Encryption/Encryption/';
400
    } elseif (file_exists('./includes/config/tp.config.php') === true) {
401
        $path = './includes/libraries/Encryption/Encryption/';
402
    } else {
403
        $path = '../includes/libraries/Encryption/Encryption/';
404
    }
405
406
    include_once $path . 'Crypto.php';
407
    include_once $path . 'Encoding.php';
408
    include_once $path . 'DerivedKeys.php';
409
    include_once $path . 'Key.php';
410
    include_once $path . 'KeyOrPassword.php';
411
    include_once $path . 'File.php';
412
    include_once $path . 'RuntimeTests.php';
413
    include_once $path . 'KeyProtectedByPassword.php';
414
    include_once $path . 'Core.php';
415
416
    $key = \Defuse\Crypto\Key::createNewRandomKey();
417
    $key = $key->saveToAsciiSafeString();
418
419
    return $key;
420
}
421
422
/**
423
 * Generate a Defuse personal key.
424
 *
425
 * @param string $psk psk used
426
 *
427
 * @return string
428
 */
429
function defuse_generate_personal_key($psk)
430
{
431
    // load PhpEncryption library
432
    if (file_exists('../includes/config/tp.config.php') === true) {
433
        $path = '../includes/libraries/Encryption/Encryption/';
434
    } elseif (file_exists('./includes/config/tp.config.php') === true) {
435
        $path = './includes/libraries/Encryption/Encryption/';
436
    } else {
437
        $path = '../includes/libraries/Encryption/Encryption/';
438
    }
439
440
    include_once $path . 'Crypto.php';
441
    include_once $path . 'Encoding.php';
442
    include_once $path . 'DerivedKeys.php';
443
    include_once $path . 'Key.php';
444
    include_once $path . 'KeyOrPassword.php';
445
    include_once $path . 'File.php';
446
    include_once $path . 'RuntimeTests.php';
447
    include_once $path . 'KeyProtectedByPassword.php';
448
    include_once $path . 'Core.php';
449
450
    $protected_key = \Defuse\Crypto\KeyProtectedByPassword::createRandomPasswordProtectedKey($psk);
451
    $protected_key_encoded = $protected_key->saveToAsciiSafeString();
452
453
    return $protected_key_encoded; // save this in user table
454
}
455
456
/**
457
 * Validate persoanl key with defuse.
458
 *
459
 * @param string $psk                   the user's psk
460
 * @param string $protected_key_encoded special key
461
 *
462
 * @return string
463
 */
464
function defuse_validate_personal_key($psk, $protected_key_encoded)
465
{
466
    // load PhpEncryption library
467
    if (file_exists('../includes/config/tp.config.php') === true) {
468
        $path = '../includes/libraries/Encryption/Encryption/';
469
    } elseif (file_exists('./includes/config/tp.config.php') === true) {
470
        $path = './includes/libraries/Encryption/Encryption/';
471
    } else {
472
        $path = '../includes/libraries/Encryption/Encryption/';
473
    }
474
475
    include_once $path . 'Crypto.php';
476
    include_once $path . 'Encoding.php';
477
    include_once $path . 'DerivedKeys.php';
478
    include_once $path . 'Key.php';
479
    include_once $path . 'KeyOrPassword.php';
480
    include_once $path . 'File.php';
481
    include_once $path . 'RuntimeTests.php';
482
    include_once $path . 'KeyProtectedByPassword.php';
483
    include_once $path . 'Core.php';
484
485
    try {
486
        $protected_key = \Defuse\Crypto\KeyProtectedByPassword::loadFromAsciiSafeString($protected_key_encoded);
487
        $user_key = $protected_key->unlockKey($psk);
488
        $user_key_encoded = $user_key->saveToAsciiSafeString();
489
    } catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) {
490
        return 'Error - Major issue as the encryption is broken.';
491
    } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
492
        return 'Error - The saltkey is not the correct one.';
493
    }
494
495
    return $user_key_encoded; // store it in session once user has entered his psk
496
}
497
498
/**
499
 * Decrypt a defuse string if encrypted.
500
 *
501
 * @param string $value Encrypted string
502
 *
503
 * @return string Decrypted string
504
 */
505
function defuseReturnDecrypted($value, $SETTINGS)
506
{
507
    if (substr($value, 0, 3) === 'def') {
508
        $value = cryption($value, '', 'decrypt', $SETTINGS)['string'];
509
    }
510
511
    return $value;
512
}
513
514
/**
515
 * Trims a string depending on a specific string.
516
 *
517
 * @param string|array $chaine  what to trim
518
 * @param string       $element trim on what
519
 *
520
 * @return string
521
 */
522
function trimElement($chaine, $element)
523
{
524
    if (!empty($chaine)) {
525
        if (is_array($chaine) === true) {
526
            $chaine = implode(';', $chaine);
527
        }
528
        $chaine = trim($chaine);
529
        if (substr($chaine, 0, 1) === $element) {
530
            $chaine = substr($chaine, 1);
531
        }
532
        if (substr($chaine, strlen($chaine) - 1, 1) === $element) {
533
            $chaine = substr($chaine, 0, strlen($chaine) - 1);
534
        }
535
    }
536
537
    return $chaine;
538
}
539
540
/**
541
 * Permits to suppress all "special" characters from string.
542
 *
543
 * @param string $string  what to clean
544
 * @param bool   $special use of special chars?
545
 *
546
 * @return string
547
 */
548
function cleanString($string, $special = false)
549
{
550
    // Create temporary table for special characters escape
551
    $tabSpecialChar = array();
552
    for ($i = 0; $i <= 31; ++$i) {
553
        $tabSpecialChar[] = chr($i);
554
    }
555
    array_push($tabSpecialChar, '<br />');
556
    if ((int) $special === 1) {
557
        $tabSpecialChar = array_merge($tabSpecialChar, array('</li>', '<ul>', '<ol>'));
558
    }
559
560
    return str_replace($tabSpecialChar, "\n", $string);
561
}
562
563
/**
564
 * Erro manager for DB.
565
 *
566
 * @param array $params output from query
567
 */
568
function db_error_handler($params)
569
{
570
    echo 'Error: ' . $params['error'] . "<br>\n";
571
    echo 'Query: ' . $params['query'] . "<br>\n";
572
    throw new Exception('Error - Query', 1);
573
}
574
575
/**
576
 * [identifyUserRights description].
577
 *
578
 * @param string $groupesVisiblesUser  [description]
579
 * @param string $groupesInterditsUser [description]
580
 * @param string $isAdmin              [description]
581
 * @param string $idFonctions          [description]
582
 *
583
 * @return string [description]
584
 */
585
function identifyUserRights(
586
    $groupesVisiblesUser,
587
    $groupesInterditsUser,
588
    $isAdmin,
589
    $idFonctions,
590
    $SETTINGS
591
) {
592
    //load ClassLoader
593
    include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php';
594
595
    // Load superglobal
596
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
597
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
598
599
    //Connect to DB
600
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
601
    if (defined('DB_PASSWD_CLEAR') === false) {
602
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
0 ignored issues
show
Bug introduced by
The constant DB_PASSWD was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
603
    }
604
    DB::$host = DB_HOST;
0 ignored issues
show
Bug introduced by
The constant DB_HOST was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
605
    DB::$user = DB_USER;
0 ignored issues
show
Bug introduced by
The constant DB_USER was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
606
    DB::$password = DB_PASSWD_CLEAR;
607
    DB::$dbName = DB_NAME;
0 ignored issues
show
Bug introduced by
The constant DB_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
608
    DB::$port = DB_PORT;
0 ignored issues
show
Bug introduced by
The constant DB_PORT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
609
    DB::$encoding = DB_ENCODING;
610
611
    //Build tree
612
    $tree = new SplClassLoader('Tree\NestedTree', $SETTINGS['cpassman_dir'] . '/includes/libraries');
613
    $tree->register();
614
    $tree = new Tree\NestedTree\NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
615
616
    // Check if user is ADMINISTRATOR
617
    if ((int) $isAdmin === 1) {
618
        identAdmin(
619
            $idFonctions,
620
            $SETTINGS,
621
            $tree
0 ignored issues
show
Bug introduced by
$tree of type Tree\NestedTree\NestedTree is incompatible with the type array expected by parameter $tree of identAdmin(). ( Ignorable by Annotation )

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

621
            /** @scrutinizer ignore-type */ $tree
Loading history...
622
        );
623
    } else {
624
        identUser(
625
            $groupesVisiblesUser,
626
            $groupesInterditsUser,
627
            $idFonctions,
628
            $SETTINGS,
629
            $tree
0 ignored issues
show
Bug introduced by
$tree of type Tree\NestedTree\NestedTree is incompatible with the type array expected by parameter $tree of identUser(). ( Ignorable by Annotation )

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

629
            /** @scrutinizer ignore-type */ $tree
Loading history...
630
        );
631
    }
632
633
    // update user's timestamp
634
    DB::update(
635
        prefixTable('users'),
636
        array(
637
            'timestamp' => time(),
638
        ),
639
        'id=%i',
640
        $superGlobal->get('user_id', 'SESSION')
641
    );
642
}
643
644
/**
645
 * Identify administrator.
646
 *
647
 * @param string $idFonctions Roles of user
648
 * @param array  $SETTINGS    Teampass settings
649
 * @param array  $tree        Tree of folders
650
 */
651
function identAdmin($idFonctions, $SETTINGS, $tree)
652
{
653
    // Load superglobal
654
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
655
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
656
657
    // Init
658
    $groupesVisibles = array();
659
    $superGlobal->put('personal_folders', array(), 'SESSION');
660
    $superGlobal->put('groupes_visibles', array(), 'SESSION');
661
    $superGlobal->put('no_access_folders', array(), 'SESSION');
662
    $superGlobal->put('personal_visible_groups', array(), 'SESSION');
663
    $superGlobal->put('read_only_folders', array(), 'SESSION');
664
    $superGlobal->put('list_restricted_folders_for_items', array(), 'SESSION');
665
    $superGlobal->put('list_folders_editable_by_role', array(), 'SESSION');
666
    $superGlobal->put('list_folders_limited', array(), 'SESSION');
667
    $superGlobal->put('no_access_folders', array(), 'SESSION');
668
    $superGlobal->put('forbiden_pfs', array(), 'SESSION');
669
670
    // Get superglobals
671
    $globalsUserId = $superGlobal->get('user_id', 'SESSION');
672
    $globalsVisibleFolders = $superGlobal->get('groupes_visibles', 'SESSION');
673
    $globalsPersonalVisibleFolders = $superGlobal->get('personal_visible_groups', 'SESSION');
674
675
    // Get list of Folders
676
    $rows = DB::query('SELECT id FROM ' . prefixTable('nested_tree') . ' WHERE personal_folder = %i', 0);
677
    foreach ($rows as $record) {
678
        array_push($groupesVisibles, $record['id']);
679
    }
680
    $superGlobal->put('groupes_visibles', $groupesVisibles, 'SESSION');
681
    $superGlobal->put('all_non_personal_folders', $groupesVisibles, 'SESSION');
682
683
    // Exclude all PF
684
    $where = new WhereClause('and'); // create a WHERE statement of pieces joined by ANDs
685
    $where->add('personal_folder=%i', 1);
686
    if (
687
        isset($SETTINGS['enable_pf_feature']) === true
688
        && (int) $SETTINGS['enable_pf_feature'] === 1
689
    ) {
690
        $where->add('title=%s', $globalsUserId);
691
        $where->negateLast();
692
    }
693
    // Get ID of personal folder
694
    $persfld = DB::queryfirstrow(
695
        'SELECT id FROM ' . prefixTable('nested_tree') . ' WHERE title = %s',
696
        $globalsUserId
697
    );
698
    if (empty($persfld['id']) === false) {
699
        if (in_array($persfld['id'], $globalsVisibleFolders) === false) {
700
            array_push($globalsVisibleFolders, $persfld['id']);
701
            array_push($globalsPersonalVisibleFolders, $persfld['id']);
702
            // get all descendants
703
            $tree = new Tree\NestedTree\NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
704
            $tree->rebuild();
705
            $tst = $tree->getDescendants($persfld['id']);
706
            foreach ($tst as $t) {
707
                array_push($globalsVisibleFolders, $t->id);
708
                array_push($globalsPersonalVisibleFolders, $t->id);
709
            }
710
        }
711
    }
712
713
    // get complete list of ROLES
714
    $tmp = explode(';', $idFonctions);
715
    $rows = DB::query(
716
        'SELECT * FROM ' . prefixTable('roles_title') . '
717
        ORDER BY title ASC'
718
    );
719
    foreach ($rows as $record) {
720
        if (!empty($record['id']) && !in_array($record['id'], $tmp)) {
721
            array_push($tmp, $record['id']);
722
        }
723
    }
724
    $superGlobal->put('fonction_id', implode(';', $tmp), 'SESSION');
725
    $superGlobal->put('is_admin', 1, 'SESSION');
726
727
    // Check if admin has created Folders and Roles
728
    DB::query('SELECT * FROM ' . prefixTable('nested_tree') . '');
729
    $superGlobal->put('nb_folders', DB::count(), 'SESSION');
730
    DB::query('SELECT * FROM ' . prefixTable('roles_title'));
731
    $superGlobal->put('nb_roles', DB::count(), 'SESSION');
732
}
733
734
/**
735
 * Permits to convert an element to array.
736
 *
737
 * @param string|array $element Any value to be returned as array
738
 *
739
 * @return array
740
 */
741
function convertToArray($element)
742
{
743
    if (is_string($element) === true) {
744
        if (empty($element) === true) {
745
            return array();
746
        } else {
747
            return explode(
748
                ';',
749
                trimElement($element, ';')
750
            );
751
        }
752
    } else {
753
        return $element;
754
    }
755
}
756
757
/**
758
 * Defines the rights the user has.
759
 *
760
 * @param string|array $allowedFolders  Allowed folders
761
 * @param string|array $noAccessFolders Not allowed folders
762
 * @param string|array $userRoles       Roles of user
763
 * @param array        $SETTINGS        Teampass settings
764
 * @param array        $tree            Tree of folders
765
 */
766
function identUser(
767
    $allowedFolders,
768
    $noAccessFolders,
769
    $userRoles,
770
    $SETTINGS,
771
    $tree
772
) {
773
    // Load superglobal
774
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
775
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
776
777
    // Init
778
    $superGlobal->put('groupes_visibles', array(), 'SESSION');
779
    $superGlobal->put('personal_folders', array(), 'SESSION');
780
    $superGlobal->put('no_access_folders', array(), 'SESSION');
781
    $superGlobal->put('personal_visible_groups', array(), 'SESSION');
782
    $superGlobal->put('read_only_folders', array(), 'SESSION');
783
    $superGlobal->put('fonction_id', $userRoles, 'SESSION');
784
    $superGlobal->put('is_admin', 0, 'SESSION');
785
786
    // init
787
    $personalFolders = array();
788
    $readOnlyFolders = array();
789
    $noAccessPersonalFolders = array();
790
    $restrictedFoldersForItems = array();
791
    $foldersLimited = array();
792
    $foldersLimitedFull = array();
793
    $allowedFoldersByRoles = array();
794
795
    // Get superglobals
796
    $globalsUserId = $superGlobal->get('user_id', 'SESSION');
797
    $globalsPersonalFolders = $superGlobal->get('personal_folder', 'SESSION');
798
799
    // Ensure consistency in array format
800
    $noAccessFolders = convertToArray($noAccessFolders);
801
    $userRoles = convertToArray($userRoles);
802
    $allowedFolders = convertToArray($allowedFolders);
803
804
    // Get list of folders depending on Roles
805
    $rows = DB::query(
806
        'SELECT *
807
        FROM ' . prefixTable('roles_values') . '
808
        WHERE role_id IN %li AND type IN %ls',
809
        $userRoles,
810
        array('W', 'ND', 'NE', 'NDNE', 'R')
811
    );
812
    foreach ($rows as $record) {
813
        if ($record['type'] === 'R') {
814
            array_push($readOnlyFolders, $record['folder_id']);
815
        } elseif (in_array($record['folder_id'], $allowedFolders) === false) {
816
            array_push($allowedFoldersByRoles, $record['folder_id']);
817
        }
818
    }
819
    $allowedFoldersByRoles = array_unique($allowedFoldersByRoles);
820
    $readOnlyFolders = array_unique($readOnlyFolders);
821
822
    // Clean arrays
823
    foreach ($allowedFoldersByRoles as $value) {
824
        if (($key = array_search($value, $readOnlyFolders)) !== false) {
825
            unset($readOnlyFolders[$key]);
826
        }
827
    }
828
829
    // Does this user is allowed to see other items
830
    $inc = 0;
831
    $rows = DB::query(
832
        'SELECT id, id_tree FROM ' . prefixTable('items') . '
833
        WHERE restricted_to LIKE %ss AND inactif = %s',
834
        $globalsUserId . ';',
835
        '0'
836
    );
837
    foreach ($rows as $record) {
838
        // Exclude restriction on item if folder is fully accessible
839
        if (in_array($record['id_tree'], $allowedFolders) === false) {
840
            $restrictedFoldersForItems[$record['id_tree']][$inc] = $record['id'];
841
            ++$inc;
842
        }
843
    }
844
845
    // Check for the users roles if some specific rights exist on items
846
    $rows = DB::query(
847
        'SELECT i.id_tree, r.item_id
848
        FROM ' . prefixTable('items') . ' as i
849
        INNER JOIN ' . prefixTable('restriction_to_roles') . ' as r ON (r.item_id=i.id)
850
        WHERE r.role_id IN %li
851
        ORDER BY i.id_tree ASC',
852
        $userRoles
853
    );
854
    $inc = 0;
855
    foreach ($rows as $record) {
856
        if (isset($record['id_tree'])) {
857
            $foldersLimited[$record['id_tree']][$inc] = $record['item_id'];
858
            array_push($foldersLimitedFull, $record['item_id']);
859
            ++$inc;
860
        }
861
    }
862
863
    // Get list of Personal Folders
864
    if (
865
        isset($SETTINGS['enable_pf_feature']) === true && (int) $SETTINGS['enable_pf_feature'] === 1
866
        && isset($globalsPersonalFolders) === true && (int) $globalsPersonalFolders === 1
867
    ) {
868
        $persoFld = DB::queryfirstrow(
869
            'SELECT id
870
            FROM ' . prefixTable('nested_tree') . '
871
            WHERE title = %s AND personal_folder = %i',
872
            $globalsUserId,
873
            1
874
        );
875
        if (empty($persoFld['id']) === false) {
876
            if (in_array($persoFld['id'], $allowedFolders) === false) {
877
                array_push($personalFolders, $persoFld['id']);
878
                array_push($allowedFolders, $persoFld['id']);
879
880
                // get all descendants
881
                $ids = $tree->getChildren($persoFld['id'], false);
882
                foreach ($ids as $ident) {
883
                    if ((int) $ident->personal_folder === 1) {
884
                        array_push($allowedFolders, $ident->id);
885
                        array_push($personalFolders, $ident->id);
886
                    }
887
                }
888
            }
889
        }
890
    }
891
892
    // Exclude all other PF
893
    $where = new WhereClause('and');
894
    $where->add('personal_folder=%i', 1);
895
    if (
896
        isset($SETTINGS['enable_pf_feature']) === true && (int) $SETTINGS['enable_pf_feature'] === 1
897
        && isset($globalsPersonalFolders) === true && (int) $globalsPersonalFolders === 1
898
    ) {
899
        $where->add('title=%s', $globalsUserId);
900
        $where->negateLast();
901
    }
902
    $persoFlds = DB::query(
903
        'SELECT id
904
        FROM ' . prefixTable('nested_tree') . '
905
        WHERE %l',
906
        $where
907
    );
908
    foreach ($persoFlds as $persoFldId) {
909
        array_push($noAccessPersonalFolders, $persoFldId['id']);
910
    }
911
912
    // All folders visibles
913
    $allowedFolders = array_merge(
914
        $foldersLimitedFull,
915
        $allowedFoldersByRoles,
916
        $restrictedFoldersForItems,
917
        $readOnlyFolders
918
    );
919
920
    // Exclude from allowed folders all the specific user forbidden folders
921
    if (count($noAccessFolders) > 0) {
922
        $allowedFolders = array_diff($allowedFolders, $noAccessFolders);
923
    }
924
925
    // Return data
926
    $superGlobal->put('all_non_personal_folders', $allowedFolders, 'SESSION');
927
    $superGlobal->put('groupes_visibles', array_merge($allowedFolders, $personalFolders), 'SESSION');
928
    $superGlobal->put('read_only_folders', $readOnlyFolders, 'SESSION');
929
    $superGlobal->put('no_access_folders', $noAccessFolders, 'SESSION');
930
    $superGlobal->put('personal_folders', $personalFolders, 'SESSION');
931
    $superGlobal->put('list_folders_limited', $foldersLimited, 'SESSION');
932
    $superGlobal->put('list_folders_editable_by_role', $allowedFoldersByRoles, 'SESSION');
933
    $superGlobal->put('list_restricted_folders_for_items', $restrictedFoldersForItems, 'SESSION');
934
    $superGlobal->put('forbiden_pfs', $noAccessPersonalFolders, 'SESSION');
935
    $superGlobal->put(
936
        'all_folders_including_no_access',
937
        array_merge(
938
            $allowedFolders,
939
            $personalFolders,
940
            $noAccessFolders,
941
            $readOnlyFolders
942
        ),
943
        'SESSION'
944
    );
945
946
    // Folders and Roles numbers
947
    DB::queryfirstrow('SELECT id FROM ' . prefixTable('nested_tree') . '');
948
    $superGlobal->put('nb_folders', DB::count(), 'SESSION');
949
    DB::queryfirstrow('SELECT id FROM ' . prefixTable('roles_title'));
950
    $superGlobal->put('nb_roles', DB::count(), 'SESSION');
951
952
    // check if change proposals on User's items
953
    if (isset($SETTINGS['enable_suggestion']) === true && (int) $SETTINGS['enable_suggestion'] === 1) {
954
        DB::query(
955
            'SELECT *
956
            FROM ' . prefixTable('items_change') . ' AS c
957
            LEFT JOIN ' . prefixTable('log_items') . ' AS i ON (c.item_id = i.id_item)
958
            WHERE i.action = %s AND i.id_user = %i',
959
            'at_creation',
960
            $globalsUserId
961
        );
962
        $superGlobal->put('nb_item_change_proposals', DB::count(), 'SESSION');
963
    } else {
964
        $superGlobal->put('nb_item_change_proposals', 0, 'SESSION');
965
    }
966
967
    return true;
968
}
969
970
/**
971
 * Update the CACHE table.
972
 *
973
 * @param string $action   What to do
974
 * @param array  $SETTINGS Teampass settings
975
 * @param string $ident    Ident format
976
 */
977
function updateCacheTable($action, $SETTINGS, $ident = null)
978
{
979
    if ($action === 'reload') {
980
        // Rebuild full cache table
981
        cacheTableRefresh($SETTINGS);
982
    } elseif ($action === 'update_value' && is_null($ident) === false) {
983
        // UPDATE an item
984
        cacheTableUpdate($SETTINGS, $ident);
985
    } elseif ($action === 'add_value' && is_null($ident) === false) {
986
        // ADD an item
987
        cacheTableAdd($SETTINGS, $ident);
988
    } elseif ($action === 'delete_value' && is_null($ident) === false) {
989
        // DELETE an item
990
        DB::delete(prefixTable('cache'), 'id = %i', $ident);
991
    }
992
}
993
994
/**
995
 * Cache table - refresh.
996
 *
997
 * @param array $SETTINGS Teampass settings
998
 */
999
function cacheTableRefresh($SETTINGS)
1000
{
1001
    include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php';
1002
1003
    //Connect to DB
1004
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
1005
    if (defined('DB_PASSWD_CLEAR') === false) {
1006
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
0 ignored issues
show
Bug introduced by
The constant DB_PASSWD was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1007
    }
1008
    DB::$host = DB_HOST;
0 ignored issues
show
Bug introduced by
The constant DB_HOST was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1009
    DB::$user = DB_USER;
0 ignored issues
show
Bug introduced by
The constant DB_USER was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1010
    DB::$password = DB_PASSWD_CLEAR;
1011
    DB::$dbName = DB_NAME;
0 ignored issues
show
Bug introduced by
The constant DB_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1012
    DB::$port = DB_PORT;
0 ignored issues
show
Bug introduced by
The constant DB_PORT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1013
    DB::$encoding = DB_ENCODING;
1014
1015
    //Load Tree
1016
    $tree = new SplClassLoader('Tree\NestedTree', '../includes/libraries');
1017
    $tree->register();
1018
    $tree = new Tree\NestedTree\NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
1019
1020
    // truncate table
1021
    DB::query('TRUNCATE TABLE ' . prefixTable('cache'));
1022
1023
    // reload date
1024
    $rows = DB::query(
1025
        'SELECT *
1026
        FROM ' . prefixTable('items') . ' as i
1027
        INNER JOIN ' . prefixTable('log_items') . ' as l ON (l.id_item = i.id)
1028
        AND l.action = %s
1029
        AND i.inactif = %i',
1030
        'at_creation',
1031
        0
1032
    );
1033
    foreach ($rows as $record) {
1034
        if (empty($record['id_tree']) === false) {
1035
            // Get all TAGS
1036
            $tags = '';
1037
            $itemTags = DB::query(
1038
                'SELECT tag
1039
                FROM ' . prefixTable('tags') . '
1040
                WHERE item_id = %i AND tag != ""',
1041
                $record['id']
1042
            );
1043
            foreach ($itemTags as $itemTag) {
1044
                $tags .= $itemTag['tag'] . ' ';
1045
            }
1046
1047
            // Get renewal period
1048
            $resNT = DB::queryfirstrow(
1049
                'SELECT renewal_period
1050
                FROM ' . prefixTable('nested_tree') . '
1051
                WHERE id = %i',
1052
                $record['id_tree']
1053
            );
1054
1055
            // form id_tree to full foldername
1056
            $folder = array();
1057
            $arbo = $tree->getPath($record['id_tree'], true);
1058
            foreach ($arbo as $elem) {
1059
                // Check if title is the ID of a user
1060
                if (is_numeric($elem->title) === true) {
1061
                    // Is this a User id?
1062
                    $user = DB::queryfirstrow(
1063
                        'SELECT id, login
1064
                        FROM ' . prefixTable('users') . '
1065
                        WHERE id = %i',
1066
                        $elem->title
1067
                    );
1068
                    if (count($user) > 0) {
1069
                        $elem->title = $user['login'];
1070
                    }
1071
                }
1072
                // Build path
1073
                array_push($folder, stripslashes($elem->title));
1074
            }
1075
            // store data
1076
            DB::insert(
1077
                prefixTable('cache'),
1078
                array(
1079
                    'id' => $record['id'],
1080
                    'label' => $record['label'],
1081
                    'description' => isset($record['description']) ? $record['description'] : '',
1082
                    'url' => (isset($record['url']) && !empty($record['url'])) ? $record['url'] : '0',
1083
                    'tags' => $tags,
1084
                    'id_tree' => $record['id_tree'],
1085
                    'perso' => $record['perso'],
1086
                    'restricted_to' => (isset($record['restricted_to']) && !empty($record['restricted_to'])) ? $record['restricted_to'] : '0',
1087
                    'login' => isset($record['login']) ? $record['login'] : '',
1088
                    'folder' => implode(' > ', $folder),
1089
                    'author' => $record['id_user'],
1090
                    'renewal_period' => isset($resNT['renewal_period']) ? $resNT['renewal_period'] : '0',
1091
                    'timestamp' => $record['date'],
1092
                )
1093
            );
1094
        }
1095
    }
1096
}
1097
1098
/**
1099
 * Cache table - update existing value.
1100
 *
1101
 * @param array  $SETTINGS Teampass settings
1102
 * @param string $ident    Ident format
1103
 */
1104
function cacheTableUpdate($SETTINGS, $ident = null)
1105
{
1106
    include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php';
1107
1108
    // Load superglobal
1109
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
1110
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
1111
1112
    //Connect to DB
1113
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
1114
    if (defined('DB_PASSWD_CLEAR') === false) {
1115
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
0 ignored issues
show
Bug introduced by
The constant DB_PASSWD was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1116
    }
1117
    DB::$host = DB_HOST;
0 ignored issues
show
Bug introduced by
The constant DB_HOST was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1118
    DB::$user = DB_USER;
0 ignored issues
show
Bug introduced by
The constant DB_USER was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1119
    DB::$password = DB_PASSWD_CLEAR;
1120
    DB::$dbName = DB_NAME;
0 ignored issues
show
Bug introduced by
The constant DB_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1121
    DB::$port = DB_PORT;
0 ignored issues
show
Bug introduced by
The constant DB_PORT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1122
    DB::$encoding = DB_ENCODING;
1123
1124
    //Load Tree
1125
    $tree = new SplClassLoader('Tree\NestedTree', '../includes/libraries');
1126
    $tree->register();
1127
    $tree = new Tree\NestedTree\NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
1128
1129
    // get new value from db
1130
    $data = DB::queryfirstrow(
1131
        'SELECT label, description, id_tree, perso, restricted_to, login, url
1132
        FROM ' . prefixTable('items') . '
1133
        WHERE id=%i',
1134
        $ident
1135
    );
1136
    // Get all TAGS
1137
    $tags = '';
1138
    $itemTags = DB::query(
1139
        'SELECT tag
1140
        FROM ' . prefixTable('tags') . '
1141
        WHERE item_id = %i AND tag != ""',
1142
        $ident
1143
    );
1144
    foreach ($itemTags as $itemTag) {
1145
        $tags .= $itemTag['tag'] . ' ';
1146
    }
1147
    // form id_tree to full foldername
1148
    $folder = array();
1149
    $arbo = $tree->getPath($data['id_tree'], true);
1150
    foreach ($arbo as $elem) {
1151
        // Check if title is the ID of a user
1152
        if (is_numeric($elem->title) === true) {
1153
            // Is this a User id?
1154
            $user = DB::queryfirstrow(
1155
                'SELECT id, login
1156
                FROM ' . prefixTable('users') . '
1157
                WHERE id = %i',
1158
                $elem->title
1159
            );
1160
            if (count($user) > 0) {
1161
                $elem->title = $user['login'];
1162
            }
1163
        }
1164
        // Build path
1165
        array_push($folder, stripslashes($elem->title));
1166
    }
1167
    // finaly update
1168
    DB::update(
1169
        prefixTable('cache'),
1170
        array(
1171
            'label' => $data['label'],
1172
            'description' => $data['description'],
1173
            'tags' => $tags,
1174
            'url' => (isset($data['url']) && !empty($data['url'])) ? $data['url'] : '0',
1175
            'id_tree' => $data['id_tree'],
1176
            'perso' => $data['perso'],
1177
            'restricted_to' => (isset($data['restricted_to']) && !empty($data['restricted_to'])) ? $data['restricted_to'] : '0',
1178
            'login' => isset($data['login']) ? $data['login'] : '',
1179
            'folder' => implode(' » ', $folder),
1180
            'author' => $superGlobal->get('user_id', 'SESSION'),
1181
        ),
1182
        'id = %i',
1183
        $ident
1184
    );
1185
}
1186
1187
/**
1188
 * Cache table - add new value.
1189
 *
1190
 * @param array  $SETTINGS Teampass settings
1191
 * @param string $ident    Ident format
1192
 */
1193
function cacheTableAdd($SETTINGS, $ident = null)
1194
{
1195
    include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php';
1196
1197
    // Load superglobal
1198
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
1199
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
1200
1201
    // Get superglobals
1202
    $globalsUserId = $superGlobal->get('user_id', 'SESSION');
1203
1204
    //Connect to DB
1205
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
1206
    if (defined('DB_PASSWD_CLEAR') === false) {
1207
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
0 ignored issues
show
Bug introduced by
The constant DB_PASSWD was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1208
    }
1209
    DB::$host = DB_HOST;
0 ignored issues
show
Bug introduced by
The constant DB_HOST was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1210
    DB::$user = DB_USER;
0 ignored issues
show
Bug introduced by
The constant DB_USER was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1211
    DB::$password = DB_PASSWD_CLEAR;
1212
    DB::$dbName = DB_NAME;
0 ignored issues
show
Bug introduced by
The constant DB_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1213
    DB::$port = DB_PORT;
0 ignored issues
show
Bug introduced by
The constant DB_PORT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1214
    DB::$encoding = DB_ENCODING;
1215
1216
    //Load Tree
1217
    $tree = new SplClassLoader('Tree\NestedTree', '../includes/libraries');
1218
    $tree->register();
1219
    $tree = new Tree\NestedTree\NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
1220
1221
    // get new value from db
1222
    $data = DB::queryFirstRow(
1223
        'SELECT i.label, i.description, i.id_tree as id_tree, i.perso, i.restricted_to, i.id, i.login, i.url, l.date
1224
        FROM ' . prefixTable('items') . ' as i
1225
        INNER JOIN ' . prefixTable('log_items') . ' as l ON (l.id_item = i.id)
1226
        WHERE i.id = %i
1227
        AND l.action = %s',
1228
        $ident,
1229
        'at_creation'
1230
    );
1231
    // Get all TAGS
1232
    $tags = '';
1233
    $itemTags = DB::query(
1234
        'SELECT tag
1235
        FROM ' . prefixTable('tags') . '
1236
        WHERE item_id = %i AND tag != ""',
1237
        $ident
1238
    );
1239
    foreach ($itemTags as $itemTag) {
1240
        $tags .= $itemTag['tag'] . ' ';
1241
    }
1242
    // form id_tree to full foldername
1243
    $folder = array();
1244
    $arbo = $tree->getPath($data['id_tree'], true);
1245
    foreach ($arbo as $elem) {
1246
        // Check if title is the ID of a user
1247
        if (is_numeric($elem->title) === true) {
1248
            // Is this a User id?
1249
            $user = DB::queryfirstrow(
1250
                'SELECT id, login
1251
                FROM ' . prefixTable('users') . '
1252
                WHERE id = %i',
1253
                $elem->title
1254
            );
1255
            if (count($user) > 0) {
1256
                $elem->title = $user['login'];
1257
            }
1258
        }
1259
        // Build path
1260
        array_push($folder, stripslashes($elem->title));
1261
    }
1262
    // finaly update
1263
    DB::insert(
1264
        prefixTable('cache'),
1265
        array(
1266
            'id' => $data['id'],
1267
            'label' => $data['label'],
1268
            'description' => $data['description'],
1269
            'tags' => (isset($tags) && empty($tags) === false) ? $tags : 'None',
1270
            'url' => (isset($data['url']) && !empty($data['url'])) ? $data['url'] : '0',
1271
            'id_tree' => $data['id_tree'],
1272
            'perso' => (isset($data['perso']) && empty($data['perso']) === false && $data['perso'] !== 'None') ? $data['perso'] : '0',
1273
            'restricted_to' => (isset($data['restricted_to']) && empty($data['restricted_to']) === false) ? $data['restricted_to'] : '0',
1274
            'login' => isset($data['login']) ? $data['login'] : '',
1275
            'folder' => implode(' » ', $folder),
1276
            'author' => $globalsUserId,
1277
            'timestamp' => $data['date'],
1278
        )
1279
    );
1280
}
1281
1282
/**
1283
 * Do statistics.
1284
 *
1285
 * @param array $SETTINGS Teampass settings
1286
 *
1287
 * @return array
1288
 */
1289
function getStatisticsData($SETTINGS)
1290
{
1291
    DB::query(
1292
        'SELECT id FROM ' . prefixTable('nested_tree') . ' WHERE personal_folder = %i',
1293
        0
1294
    );
1295
    $counter_folders = DB::count();
1296
1297
    DB::query(
1298
        'SELECT id FROM ' . prefixTable('nested_tree') . ' WHERE personal_folder = %i',
1299
        1
1300
    );
1301
    $counter_folders_perso = DB::count();
1302
1303
    DB::query(
1304
        'SELECT id FROM ' . prefixTable('items') . ' WHERE perso = %i',
1305
        0
1306
    );
1307
    $counter_items = DB::count();
1308
1309
    DB::query(
1310
        'SELECT id FROM ' . prefixTable('items') . ' WHERE perso = %i',
1311
        1
1312
    );
1313
    $counter_items_perso = DB::count();
1314
1315
    DB::query(
1316
        'SELECT id FROM ' . prefixTable('users') . ''
1317
    );
1318
    $counter_users = DB::count();
1319
1320
    DB::query(
1321
        'SELECT id FROM ' . prefixTable('users') . ' WHERE admin = %i',
1322
        1
1323
    );
1324
    $admins = DB::count();
1325
1326
    DB::query(
1327
        'SELECT id FROM ' . prefixTable('users') . ' WHERE gestionnaire = %i',
1328
        1
1329
    );
1330
    $managers = DB::count();
1331
1332
    DB::query(
1333
        'SELECT id FROM ' . prefixTable('users') . ' WHERE read_only = %i',
1334
        1
1335
    );
1336
    $readOnly = DB::count();
1337
1338
    // list the languages
1339
    $usedLang = [];
1340
    $tp_languages = DB::query(
1341
        'SELECT name FROM ' . prefixTable('languages')
1342
    );
1343
    foreach ($tp_languages as $tp_language) {
1344
        DB::query(
1345
            'SELECT * FROM ' . prefixTable('users') . ' WHERE user_language = %s',
1346
            $tp_language['name']
1347
        );
1348
        $usedLang[$tp_language['name']] = round((DB::count() * 100 / $counter_users), 0);
1349
    }
1350
1351
    // get list of ips
1352
    $usedIp = [];
1353
    $tp_ips = DB::query(
1354
        'SELECT user_ip FROM ' . prefixTable('users')
1355
    );
1356
    foreach ($tp_ips as $ip) {
1357
        if (array_key_exists($ip['user_ip'], $usedIp)) {
1358
            $usedIp[$ip['user_ip']] = $usedIp[$ip['user_ip']] + 1;
1359
        } elseif (!empty($ip['user_ip']) && $ip['user_ip'] !== 'none') {
1360
            $usedIp[$ip['user_ip']] = 1;
1361
        }
1362
    }
1363
1364
    return array(
1365
        'error' => '',
1366
        'stat_phpversion' => phpversion(),
1367
        'stat_folders' => $counter_folders,
1368
        'stat_folders_shared' => intval($counter_folders) - intval($counter_folders_perso),
1369
        'stat_items' => $counter_items,
1370
        'stat_items_shared' => intval($counter_items) - intval($counter_items_perso),
1371
        'stat_users' => $counter_users,
1372
        'stat_admins' => $admins,
1373
        'stat_managers' => $managers,
1374
        'stat_ro' => $readOnly,
1375
        'stat_kb' => $SETTINGS['enable_kb'],
1376
        'stat_pf' => $SETTINGS['enable_pf_feature'],
1377
        'stat_fav' => $SETTINGS['enable_favourites'],
1378
        'stat_teampassversion' => TP_VERSION_FULL,
1379
        'stat_ldap' => $SETTINGS['ldap_mode'],
1380
        'stat_agses' => $SETTINGS['agses_authentication_enabled'],
1381
        'stat_duo' => $SETTINGS['duo'],
1382
        'stat_suggestion' => $SETTINGS['enable_suggestion'],
1383
        'stat_api' => $SETTINGS['api'],
1384
        'stat_customfields' => $SETTINGS['item_extra_fields'],
1385
        'stat_syslog' => $SETTINGS['syslog_enable'],
1386
        'stat_2fa' => $SETTINGS['google_authentication'],
1387
        'stat_stricthttps' => $SETTINGS['enable_sts'],
1388
        'stat_mysqlversion' => DB::serverVersion(),
1389
        'stat_languages' => $usedLang,
1390
        'stat_country' => $usedIp,
1391
    );
1392
}
1393
1394
/**
1395
 * Permits to send an email.
1396
 *
1397
 * @param string $subject     email subject
1398
 * @param string $textMail    email message
1399
 * @param string $email       email
1400
 * @param array  $SETTINGS    settings
1401
 * @param string $textMailAlt email message alt
1402
 * @param bool   $silent      no errors
1403
 *
1404
 * @return string some json info
1405
 */
1406
function sendEmail(
1407
    $subject,
1408
    $textMail,
1409
    $email,
1410
    $SETTINGS,
1411
    $textMailAlt = null,
1412
    $silent = true
1413
) {
1414
    // CAse where email not defined
1415
    if ($email === 'none') {
1416
        return '"error":"" , "message":"' . langHdl('forgot_my_pw_email_sent') . '"';
1417
    }
1418
1419
    // Load settings
1420
    include_once $SETTINGS['cpassman_dir'] . '/includes/config/settings.php';
1421
1422
    // Load superglobal
1423
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
1424
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
1425
1426
    // Get user language
1427
    $session_user_language = $superGlobal->get('user_language', 'SESSION');
1428
    $user_language = isset($session_user_language) ? $session_user_language : 'english';
1429
    include_once $SETTINGS['cpassman_dir'] . '/includes/language/' . $user_language . '.php';
1430
1431
    // Load library
1432
    include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php';
1433
1434
    // load PHPMailer
1435
    $mail = new SplClassLoader('PHPMailer\PHPMailer', '../includes/libraries');
1436
    $mail->register();
1437
    $mail = new PHPMailer\PHPMailer\PHPMailer(true);
1438
    try {
1439
        // send to user
1440
        $mail->setLanguage('en', $SETTINGS['cpassman_dir'] . '/includes/libraries/PHPMailer/PHPMailer/language/');
1441
        $mail->SMTPDebug = 0; //value 1 can be used to debug - 4 for debuging connections
1442
        $mail->Port = $SETTINGS['email_port']; //COULD BE USED
1443
        $mail->CharSet = 'utf-8';
1444
        $mail->SMTPSecure = ($SETTINGS['email_security'] === 'tls'
1445
            || $SETTINGS['email_security'] === 'ssl') ? $SETTINGS['email_security'] : '';
1446
        $mail->SMTPAutoTLS = ($SETTINGS['email_security'] === 'tls'
1447
            || $SETTINGS['email_security'] === 'ssl') ? true : false;
1448
        $mail->SMTPOptions = array(
1449
            'ssl' => array(
1450
                'verify_peer' => false,
1451
                'verify_peer_name' => false,
1452
                'allow_self_signed' => true,
1453
            ),
1454
        );
1455
        $mail->isSmtp(); // send via SMTP
1456
        $mail->Host = $SETTINGS['email_smtp_server']; // SMTP servers
1457
        $mail->SMTPAuth = (int) $SETTINGS['email_smtp_auth'] === 1 ? true : false; // turn on SMTP authentication
1458
        $mail->Username = $SETTINGS['email_auth_username']; // SMTP username
1459
        $mail->Password = $SETTINGS['email_auth_pwd']; // SMTP password
1460
        $mail->From = $SETTINGS['email_from'];
1461
        $mail->FromName = $SETTINGS['email_from_name'];
1462
1463
        // Prepare for each person
1464
        foreach (array_filter(explode(',', $email)) as $dest) {
1465
            if (empty($dest) === false) {
1466
                $mail->addAddress($dest);
1467
            }
1468
        }
1469
1470
        // Prepare HTML
1471
        $text_html = emailBody($textMail);
1472
1473
        $mail->WordWrap = 80; // set word wrap
1474
        $mail->isHtml(true); // send as HTML
1475
        $mail->Subject = $subject;
1476
        $mail->Body = $text_html;
1477
        $mail->AltBody = (is_null($textMailAlt) === false) ? $textMailAlt : '';
1478
        $silent = false;
1479
        // send email
1480
        if ($mail->send()) {
1481
            if ($silent === false) {
0 ignored issues
show
introduced by
The condition $silent === false is always true.
Loading history...
1482
                return json_encode(
1483
                    array(
1484
                        'error' => false,
1485
                        'message' => langHdl('forgot_my_pw_email_sent'),
1486
                    )
1487
                );
1488
            }
1489
        } elseif ($silent === false) {
0 ignored issues
show
introduced by
The condition $silent === false is always true.
Loading history...
1490
            return json_encode(
1491
                array(
1492
                    'error' => true,
1493
                    'message' => str_replace(array("\n", "\t", "\r"), '', $mail->ErrorInfo),
1494
                )
1495
            );
1496
        }
1497
    } catch (Exception $e) {
1498
        if ($silent === false) {
1499
            return json_encode(
1500
                array(
1501
                    'error' => true,
1502
                    'message' => str_replace(array("\n", "\t", "\r"), '', $mail->ErrorInfo),
1503
                )
1504
            );
1505
        }
1506
    }
1507
}
1508
1509
/**
1510
 * Returns the email body.
1511
 *
1512
 * @param string $textMail Text for the email
1513
 *
1514
 * @return string
1515
 */
1516
function emailBody($textMail)
1517
{
1518
    return '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.=
1519
    w3.org/TR/html4/loose.dtd"><html>
1520
    <head><title>Email Template</title>
1521
    <style type="text/css">
1522
    body { background-color: #f0f0f0; padding: 10px 0; margin:0 0 10px =0; }
1523
    </style></head>
1524
    <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">
1525
    <table border="0" width="100%" height="100%" cellpadding="0" cellspacing="0" bgcolor="#f0f0f0" style="border-spacing: 0;">
1526
    <tr><td style="border-collapse: collapse;"><br>
1527
        <table border="0" width="100%" cellpadding="0" cellspacing="0" bgcolor="#17357c" style="border-spacing: 0; margin-bottom: 25px;">
1528
        <tr><td style="border-collapse: collapse; padding: 11px 20px;">
1529
            <div style="max-width:150px; max-height:34px; color:#f0f0f0; font-weight:bold;">Teampass</div>
1530
        </td></tr></table></td>
1531
    </tr>
1532
    <tr><td align="center" valign="top" bgcolor="#f0f0f0" style="border-collapse: collapse; background-color: #f0f0f0;">
1533
        <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;">
1534
        <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;">
1535
        <br><div style="float:right;">' .
1536
        $textMail .
1537
        '<br><br></td></tr></table>
1538
    </td></tr></table>
1539
    <br></body></html>';
1540
}
1541
1542
/**
1543
 * Generate a Key.
1544
 *
1545
 * @return string
1546
 */
1547
function generateKey()
1548
{
1549
    return substr(md5(rand() . rand()), 0, 15);
1550
}
1551
1552
/**
1553
 * Convert date to timestamp.
1554
 *
1555
 * @param string $date     The date
1556
 * @param array  $SETTINGS Teampass settings
1557
 *
1558
 * @return string
1559
 */
1560
function dateToStamp($date, $SETTINGS)
1561
{
1562
    $date = date_parse_from_format($SETTINGS['date_format'], $date);
1563
    if ((int) $date['warning_count'] === 0 && (int) $date['error_count'] === 0) {
1564
        return mktime(23, 59, 59, $date['month'], $date['day'], $date['year']);
1565
    } else {
1566
        return '';
1567
    }
1568
}
1569
1570
/**
1571
 * Is this a date.
1572
 *
1573
 * @param string $date Date
1574
 *
1575
 * @return bool
1576
 */
1577
function isDate($date)
1578
{
1579
    return strtotime($date) !== false;
1580
}
1581
1582
/**
1583
 * Check if isUTF8().
1584
 *
1585
 * @return int is the string in UTF8 format
1586
 */
1587
function isUTF8($string)
1588
{
1589
    if (is_array($string) === true) {
1590
        $string = $string['string'];
1591
    }
1592
1593
    return preg_match(
1594
        '%^(?:
1595
        [\x09\x0A\x0D\x20-\x7E] # ASCII
1596
        | [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
1597
        | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
1598
        | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
1599
        | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
1600
        | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
1601
        | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
1602
        | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
1603
        )*$%xs',
1604
        $string
1605
    );
1606
}
1607
1608
/**
1609
 * Prepare an array to UTF8 format before JSON_encode.
1610
 *
1611
 * @param array $array Array of values
1612
 *
1613
 * @return array
1614
 */
1615
function utf8Converter($array)
1616
{
1617
    array_walk_recursive(
1618
        $array,
1619
        function (&$item) {
1620
            if (mb_detect_encoding($item, 'utf-8', true) === false) {
1621
                $item = utf8_encode($item);
1622
            }
1623
        }
1624
    );
1625
1626
    return $array;
1627
}
1628
1629
/**
1630
 * Permits to prepare data to be exchanged.
1631
 *
1632
 * @param array|string $data Text
1633
 * @param string       $type Parameter
1634
 * @param string       $key  Optional key
1635
 *
1636
 * @return string|array
1637
 */
1638
function prepareExchangedData($data, $type, $key = null)
1639
{
1640
    if (file_exists('../includes/config/tp.config.php')) {
1641
        include '../includes/config/tp.config.php';
1642
    } elseif (file_exists('./includes/config/tp.config.php')) {
1643
        include './includes/config/tp.config.php';
1644
    } elseif (file_exists('../../includes/config/tp.config.php')) {
1645
        include '../../includes/config/tp.config.php';
1646
    } else {
1647
        throw new Exception("Error file '/includes/config/tp.config.php' not exists", 1);
1648
    }
1649
1650
    // Load superglobal
1651
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $SETTINGS seems to be never defined.
Loading history...
1652
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
1653
1654
    // Get superglobals
1655
    if ($key !== null) {
1656
        $superGlobal->put('key', $key, 'SESSION');
1657
        $globalsKey = $key;
1658
    } else {
1659
        $globalsKey = $superGlobal->get('key', 'SESSION');
1660
    }
1661
1662
    //load ClassLoader
1663
    include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php';
1664
    //Load AES
1665
    $aes = new SplClassLoader('Encryption\Crypt', $SETTINGS['cpassman_dir'] . '/includes/libraries');
1666
    $aes->register();
1667
1668
    if ($type === 'encode' && is_array($data) === true) {
1669
        // Ensure UTF8 format
1670
        $data = utf8Converter($data);
1671
        // Now encode
1672
        if (isset($SETTINGS['encryptClientServer'])
1 ignored issue
show
Comprehensibility Best Practice introduced by
The variable $SETTINGS seems to never exist and therefore isset should always be false.
Loading history...
1673
            && $SETTINGS['encryptClientServer'] === '0'
1674
        ) {
1675
            return json_encode(
1676
                $data,
1677
                JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
1678
            );
1679
        } else {
1680
            return Encryption\Crypt\aesctr::encrypt(
1681
                json_encode(
1682
                    $data,
1683
                    JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
1684
                ),
1685
                $globalsKey,
1686
                256
1687
            );
1688
        }
1689
    } elseif ($type === 'decode' && is_array($data) === false) {
1690
        if (isset($SETTINGS['encryptClientServer'])
1691
            && $SETTINGS['encryptClientServer'] === '0'
1692
        ) {
1693
            return json_decode(
1694
                $data,
0 ignored issues
show
Bug introduced by
$data of type array is incompatible with the type string expected by parameter $json of json_decode(). ( Ignorable by Annotation )

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

1694
                /** @scrutinizer ignore-type */ $data,
Loading history...
1695
                true
1696
            );
1697
        } else {
1698
            return json_decode(
1699
                Encryption\Crypt\aesctr::decrypt(
1700
                    $data,
0 ignored issues
show
Bug introduced by
$data of type array is incompatible with the type Encryption\Crypt\source expected by parameter $ciphertext of Encryption\Crypt\aesctr::decrypt(). ( Ignorable by Annotation )

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

1700
                    /** @scrutinizer ignore-type */ $data,
Loading history...
1701
                    $globalsKey,
1702
                    256
1703
                ),
1704
                true
1705
            );
1706
        }
1707
    }
1708
}
1709
1710
/**
1711
 * Create a thumbnail.
1712
 *
1713
 * @param string $src           Source
1714
 * @param string $dest          Destination
1715
 * @param float  $desired_width Size of width
1716
 */
1717
function makeThumbnail($src, $dest, $desired_width)
1718
{
1719
    /* read the source image */
1720
    if(is_file($src) === true && mime_content_type($src) === 'image/png'){
1721
        $source_image = imagecreatefrompng($src);
1722
    } else {
1723
        return "Error: Not a valid PNG file! It's type is ".mime_content_type($src);
1724
    }
1725
    
1726
    // Get height and width
1727
    $width = imagesx($source_image);
0 ignored issues
show
Bug introduced by
It seems like $source_image can also be of type false; however, parameter $image of imagesx() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

1727
    $width = imagesx(/** @scrutinizer ignore-type */ $source_image);
Loading history...
1728
    $height = imagesy($source_image);
0 ignored issues
show
Bug introduced by
It seems like $source_image can also be of type false; however, parameter $image of imagesy() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

1728
    $height = imagesy(/** @scrutinizer ignore-type */ $source_image);
Loading history...
1729
1730
    /* find the "desired height" of this thumbnail, relative to the desired width  */
1731
    $desired_height = floor($height * ($desired_width / $width));
1732
1733
    /* create a new, "virtual" image */
1734
    $virtual_image = imagecreatetruecolor($desired_width, $desired_height);
0 ignored issues
show
Bug introduced by
$desired_width of type double is incompatible with the type integer expected by parameter $width of imagecreatetruecolor(). ( Ignorable by Annotation )

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

1734
    $virtual_image = imagecreatetruecolor(/** @scrutinizer ignore-type */ $desired_width, $desired_height);
Loading history...
Bug introduced by
$desired_height of type double is incompatible with the type integer expected by parameter $height of imagecreatetruecolor(). ( Ignorable by Annotation )

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

1734
    $virtual_image = imagecreatetruecolor($desired_width, /** @scrutinizer ignore-type */ $desired_height);
Loading history...
1735
1736
    /* copy source image at a resized size */
1737
    imagecopyresampled($virtual_image, $source_image, 0, 0, 0, 0, $desired_width, $desired_height, $width, $height);
0 ignored issues
show
Bug introduced by
$desired_height of type double is incompatible with the type integer expected by parameter $dst_h of imagecopyresampled(). ( Ignorable by Annotation )

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

1737
    imagecopyresampled($virtual_image, $source_image, 0, 0, 0, 0, $desired_width, /** @scrutinizer ignore-type */ $desired_height, $width, $height);
Loading history...
Bug introduced by
It seems like $virtual_image can also be of type false; however, parameter $dst_image of imagecopyresampled() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

1737
    imagecopyresampled(/** @scrutinizer ignore-type */ $virtual_image, $source_image, 0, 0, 0, 0, $desired_width, $desired_height, $width, $height);
Loading history...
Bug introduced by
$desired_width of type double is incompatible with the type integer expected by parameter $dst_w of imagecopyresampled(). ( Ignorable by Annotation )

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

1737
    imagecopyresampled($virtual_image, $source_image, 0, 0, 0, 0, /** @scrutinizer ignore-type */ $desired_width, $desired_height, $width, $height);
Loading history...
Bug introduced by
It seems like $source_image can also be of type false; however, parameter $src_image of imagecopyresampled() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

1737
    imagecopyresampled($virtual_image, /** @scrutinizer ignore-type */ $source_image, 0, 0, 0, 0, $desired_width, $desired_height, $width, $height);
Loading history...
1738
1739
    /* create the physical thumbnail image to its destination */
1740
    imagejpeg($virtual_image, $dest);
0 ignored issues
show
Bug introduced by
It seems like $virtual_image can also be of type false; however, parameter $image of imagejpeg() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

1740
    imagejpeg(/** @scrutinizer ignore-type */ $virtual_image, $dest);
Loading history...
1741
}
1742
1743
/**
1744
 * Check table prefix in SQL query.
1745
 *
1746
 * @param string $table Table name
1747
 *
1748
 * @return string
1749
 */
1750
function prefixTable($table)
1751
{
1752
    $safeTable = htmlspecialchars(DB_PREFIX . $table);
0 ignored issues
show
Bug introduced by
The constant DB_PREFIX was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1753
    if (!empty($safeTable)) {
1754
        // sanitize string
1755
        return $safeTable;
1756
    } else {
1757
        // stop error no table
1758
        return 'table_not_exists';
1759
    }
1760
}
1761
1762
1763
/**
1764
 * GenerateCryptKey
1765
 *
1766
 * @param int     $size      Length
1767
 * @param boolean $secure    Secure
1768
 * @param boolean $numerals  Numerics
1769
 * @param boolean $uppercase Uppercase letters
1770
 * @param boolean $symbols   Symbols
1771
 * @param boolean $lowercase Lowercase
1772
 * @param array   $SETTINGS  SETTINGS
1773
 * @return void
1774
 */
1775
function GenerateCryptKey(
1776
    $size = null,
1777
    $secure = false,
1778
    $numerals = false,
1779
    $uppercase = false,
1780
    $symbols = false,
1781
    $lowercase = false,
1782
    $SETTINGS
1783
) {
1784
    include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php';
1785
    $generator = new SplClassLoader('PasswordGenerator\Generator', $SETTINGS['cpassman_dir'] . '/includes/libraries');
1786
    $generator->register();
1787
    $generator = new PasswordGenerator\Generator\ComputerPasswordGenerator();
1788
1789
    // Is PHP7 being used?
1790
    if (version_compare(PHP_VERSION, '7.0.0', '>=')) {
1791
        $php7generator = new SplClassLoader('PasswordGenerator\RandomGenerator', $SETTINGS['cpassman_dir'] . '/includes/libraries');
1792
        $php7generator->register();
1793
        $generator->setRandomGenerator(new PasswordGenerator\RandomGenerator\Php7RandomGenerator());
1794
    }
1795
1796
    // Manage size
1797
    $generator->setLength(($size === null) ? 10 : (int) $size);
1798
1799
    if ($secure === true) {
1800
        $generator->setSymbols(true);
1801
        $generator->setLowercase(true);
1802
        $generator->setUppercase(true);
1803
        $generator->setNumbers(true);
1804
    } else {
1805
        $generator->setLowercase($lowercase);
1806
        $generator->setUppercase($uppercase);
1807
        $generator->setNumbers($numerals);
1808
        $generator->setSymbols($symbols);
1809
    }
1810
1811
    return $generator->generatePasswords()[0];
1812
}
1813
1814
/*
1815
* Send sysLOG message
1816
* @param string $message
1817
* @param string $host
1818
*/
1819
function send_syslog($message, $host, $port, $component = 'teampass')
1820
{
1821
    $sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
1822
    $syslog_message = '<123>' . date('M d H:i:s ') . $component . ': ' . $message;
1823
    socket_sendto($sock, $syslog_message, strlen($syslog_message), 0, $host, $port);
1824
    socket_close($sock);
1825
}
1826
1827
/**
1828
 * Permits to log events into DB
1829
 *
1830
 * @param array  $SETTINGS Teampass settings
1831
 * @param string $type     Type
1832
 * @param string $label    Label
1833
 * @param string $who      Who
1834
 * @param string $login    Login
1835
 * @param string $field_1  Field
1836
 *
1837
 * @return void
1838
 */
1839
function logEvents($SETTINGS, $type, $label, $who, $login = null, $field_1 = null)
1840
{
1841
    if (empty($who)) {
1842
        $who = getClientIpServer();
1843
    }
1844
1845
    // include librairies & connect to DB
1846
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
1847
    if (defined('DB_PASSWD_CLEAR') === false) {
1848
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
0 ignored issues
show
Bug introduced by
The constant DB_PASSWD was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1849
    }
1850
    DB::$host = DB_HOST;
0 ignored issues
show
Bug introduced by
The constant DB_HOST was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1851
    DB::$user = DB_USER;
0 ignored issues
show
Bug introduced by
The constant DB_USER was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1852
    DB::$password = DB_PASSWD_CLEAR;
1853
    DB::$dbName = DB_NAME;
0 ignored issues
show
Bug introduced by
The constant DB_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1854
    DB::$port = DB_PORT;
0 ignored issues
show
Bug introduced by
The constant DB_PORT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1855
    DB::$encoding = DB_ENCODING;
1856
1857
    DB::insert(
1858
        prefixTable('log_system'),
1859
        array(
1860
            'type' => $type,
1861
            'date' => time(),
1862
            'label' => $label,
1863
            'qui' => $who,
1864
            'field_1' => $field_1 === null ? '' : $field_1,
1865
        )
1866
    );
1867
1868
    // If SYSLOG
1869
    if (isset($SETTINGS['syslog_enable']) === true && (int) $SETTINGS['syslog_enable'] === 1) {
1870
        if ($type === 'user_mngt') {
1871
            send_syslog(
1872
                'action=' . str_replace('at_', '', $label) . ' attribute=user user=' . $who . ' userid="' . $login . '" change="' . $field_1 . '" ',
1873
                $SETTINGS['syslog_host'],
1874
                $SETTINGS['syslog_port'],
1875
                'teampass'
1876
            );
1877
        } else {
1878
            send_syslog(
1879
                'action=' . $type . ' attribute=' . $label . ' user=' . $who . ' userid="' . $login . '" ',
1880
                $SETTINGS['syslog_host'],
1881
                $SETTINGS['syslog_port'],
1882
                'teampass'
1883
            );
1884
        }
1885
    }
1886
}
1887
1888
/**
1889
 * Log events.
1890
 *
1891
 * @param array  $SETTINGS        Teampass settings
1892
 * @param int    $item_id         Item id
1893
 * @param string $item_label      Item label
1894
 * @param int    $id_user         User id
1895
 * @param string $action          Code for reason
1896
 * @param string $login           User login
1897
 * @param string $raison          Code for reason
1898
 * @param string $encryption_type Encryption on
1899
 */
1900
function logItems(
1901
    $SETTINGS,
1902
    $item_id,
1903
    $item_label,
1904
    $id_user,
1905
    $action,
1906
    $login = null,
1907
    $raison = null,
1908
    $encryption_type = null
1909
) {
1910
    // include librairies & connect to DB
1911
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
1912
    if (defined('DB_PASSWD_CLEAR') === false) {
1913
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
0 ignored issues
show
Bug introduced by
The constant DB_PASSWD was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1914
    }
1915
    DB::$host = DB_HOST;
0 ignored issues
show
Bug introduced by
The constant DB_HOST was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1916
    DB::$user = DB_USER;
0 ignored issues
show
Bug introduced by
The constant DB_USER was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1917
    DB::$password = DB_PASSWD_CLEAR;
1918
    DB::$dbName = DB_NAME;
0 ignored issues
show
Bug introduced by
The constant DB_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1919
    DB::$port = DB_PORT;
0 ignored issues
show
Bug introduced by
The constant DB_PORT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1920
    DB::$encoding = DB_ENCODING;
1921
1922
    // Insert log in DB
1923
    DB::insert(
1924
        prefixTable('log_items'),
1925
        array(
1926
            'id_item' => $item_id,
1927
            'date' => time(),
1928
            'id_user' => $id_user,
1929
            'action' => $action,
1930
            'raison' => $raison,
1931
            'raison_iv' => '',
1932
            'encryption_type' => is_null($encryption_type) === true ? TP_ENCRYPTION_NAME : $encryption_type,
1933
        )
1934
    );
1935
    // Timestamp the last change
1936
    if ($action === 'at_creation' || $action === 'at_modifiation' || $action === 'at_delete' || $action === 'at_import') {
1937
        DB::update(
1938
            prefixTable('misc'),
1939
            array(
1940
                'valeur' => time(),
1941
            ),
1942
            'type = %s AND intitule = %s',
1943
            'timestamp',
1944
            'last_item_change'
1945
        );
1946
    }
1947
1948
    // SYSLOG
1949
    if (isset($SETTINGS['syslog_enable']) === true && $SETTINGS['syslog_enable'] === '1') {
1950
        // Extract reason
1951
        $attribute = explode(' : ', $raison);
1952
1953
        // Get item info if not known
1954
        if (empty($item_label) === true) {
1955
            $dataItem = DB::queryfirstrow(
1956
                'SELECT id, id_tree, label
1957
                FROM ' . prefixTable('items') . '
1958
                WHERE id = %i',
1959
                $item_id
1960
            );
1961
1962
            $item_label = $dataItem['label'];
1963
        }
1964
1965
        send_syslog(
1966
            'action=' . str_replace('at_', '', $action) . ' attribute=' . str_replace('at_', '', $attribute[0]) . ' itemno=' . $item_id . ' user=' . addslashes($login) . ' itemname="' . addslashes($item_label) . '"',
1967
            $SETTINGS['syslog_host'],
1968
            $SETTINGS['syslog_port'],
1969
            'teampass'
1970
        );
1971
    }
1972
1973
    // send notification if enabled
1974
    notifyOnChange($item_id, $action, $SETTINGS);
1975
}
1976
1977
/**
1978
 * If enabled, then notify admin/manager.
1979
 *
1980
 * @param int    $item_id  Item id
1981
 * @param string $action   Action to do
1982
 * @param array  $SETTINGS Teampass settings
1983
 */
1984
function notifyOnChange($item_id, $action, $SETTINGS)
1985
{
1986
    if (
1987
        isset($SETTINGS['enable_email_notification_on_item_shown']) === true
1988
        && (int) $SETTINGS['enable_email_notification_on_item_shown'] === 1
1989
        && $action === 'at_shown'
1990
    ) {
1991
        // Load superglobal
1992
        include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
1993
        $superGlobal = new protect\SuperGlobal\SuperGlobal();
1994
1995
        // Get superglobals
1996
        $globalsLastname = $superGlobal->get('lastname', 'SESSION');
1997
        $globalsName = $superGlobal->get('name', 'SESSION');
1998
        $globalsNotifiedEmails = $superGlobal->get('listNotificationEmails', 'SESSION');
1999
2000
        // Get info about item
2001
        $dataItem = DB::queryfirstrow(
2002
            'SELECT id, id_tree, label
2003
            FROM ' . prefixTable('items') . '
2004
            WHERE id = %i',
2005
            $item_id
2006
        );
2007
        $item_label = $dataItem['label'];
2008
2009
        // send back infos
2010
        DB::insert(
2011
            prefixTable('emails'),
2012
            array(
2013
                'timestamp' => time(),
2014
                'subject' => langHdl('email_on_open_notification_subject'),
2015
                'body' => str_replace(
2016
                    array('#tp_user#', '#tp_item#', '#tp_link#'),
2017
                    array(
2018
                        addslashes($globalsName . ' ' . $globalsLastname),
2019
                        addslashes($item_label),
2020
                        $SETTINGS['cpassman_url'] . '/index.php?page=items&group=' . $dataItem['id_tree'] . '&id=' . $item_id,
2021
                    ),
2022
                    langHdl('email_on_open_notification_mail')
2023
                ),
2024
                'receivers' => $globalsNotifiedEmails,
2025
                'status' => '',
2026
            )
2027
        );
2028
    }
2029
}
2030
2031
/**
2032
 * Prepare notification email to subscribers.
2033
 *
2034
 * @param int    $item_id  Item id
2035
 * @param string $label    Item label
2036
 * @param array  $changes  List of changes
2037
 * @param array  $SETTINGS Teampass settings
2038
 */
2039
function notifyChangesToSubscribers($item_id, $label, $changes, $SETTINGS)
2040
{
2041
    // Load superglobal
2042
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
2043
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
2044
2045
    // Get superglobals
2046
    $globalsUserId = $superGlobal->get('user_id', 'SESSION');
2047
    $globalsLastname = $superGlobal->get('lastname', 'SESSION');
2048
    $globalsName = $superGlobal->get('name', 'SESSION');
2049
2050
    // send email to user that what to be notified
2051
    $notification = DB::queryOneColumn(
2052
        'email',
2053
        'SELECT *
2054
        FROM ' . prefixTable('notification') . ' AS n
2055
        INNER JOIN ' . prefixTable('users') . ' AS u ON (n.user_id = u.id)
2056
        WHERE n.item_id = %i AND n.user_id != %i',
2057
        $item_id,
2058
        $globalsUserId
2059
    );
2060
2061
    if (DB::count() > 0) {
2062
        // Prepare path
2063
        $path = geItemReadablePath($item_id, '', $SETTINGS);
2064
2065
        // Get list of changes
2066
        $htmlChanges = '<ul>';
2067
        foreach ($changes as $change) {
2068
            $htmlChanges .= '<li>' . $change . '</li>';
2069
        }
2070
        $htmlChanges .= '</ul>';
2071
2072
        // send email
2073
        DB::insert(
2074
            prefixTable('emails'),
2075
            array(
2076
                'timestamp' => time(),
2077
                'subject' => langHdl('email_subject_item_updated'),
2078
                'body' => str_replace(
2079
                    array('#item_label#', '#folder_name#', '#item_id#', '#url#', '#name#', '#lastname#', '#changes#'),
2080
                    array($label, $path, $item_id, $SETTINGS['cpassman_url'], $globalsName, $globalsLastname, $htmlChanges),
2081
                    langHdl('email_body_item_updated')
2082
                ),
2083
                'receivers' => implode(',', $notification),
2084
                'status' => '',
2085
            )
2086
        );
2087
    }
2088
}
2089
2090
/**
2091
 * Returns the Item + path.
2092
 *
2093
 * @param int    $id_tree  Node id
2094
 * @param string $label    Label
2095
 * @param array  $SETTINGS TP settings
2096
 *
2097
 * @return string
2098
 */
2099
function geItemReadablePath($id_tree, $label, $SETTINGS)
2100
{
2101
    // Class loader
2102
    include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php';
2103
2104
    //Load Tree
2105
    $tree = new SplClassLoader('Tree\NestedTree', '../includes/libraries');
2106
    $tree->register();
2107
    $tree = new Tree\NestedTree\NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
2108
2109
    $arbo = $tree->getPath($id_tree, true);
2110
    $path = '';
2111
    foreach ($arbo as $elem) {
2112
        if (empty($path) === true) {
2113
            $path = htmlspecialchars(stripslashes(htmlspecialchars_decode($elem->title, ENT_QUOTES)), ENT_QUOTES) . ' ';
2114
        } else {
2115
            $path .= '&#8594; ' . htmlspecialchars(stripslashes(htmlspecialchars_decode($elem->title, ENT_QUOTES)), ENT_QUOTES);
2116
        }
2117
    }
2118
2119
    // Build text to show user
2120
    if (empty($label) === false) {
2121
        return empty($path) === true ? addslashes($label) : addslashes($label) . ' (' . $path . ')';
2122
    } else {
2123
        return empty($path) === true ? '' : $path;
2124
    }
2125
}
2126
2127
/**
2128
 * Get the client ip address.
2129
 *
2130
 * @return string IP address
2131
 */
2132
function getClientIpServer()
2133
{
2134
    if (getenv('HTTP_CLIENT_IP')) {
2135
        $ipaddress = getenv('HTTP_CLIENT_IP');
2136
    } elseif (getenv('HTTP_X_FORWARDED_FOR')) {
2137
        $ipaddress = getenv('HTTP_X_FORWARDED_FOR');
2138
    } elseif (getenv('HTTP_X_FORWARDED')) {
2139
        $ipaddress = getenv('HTTP_X_FORWARDED');
2140
    } elseif (getenv('HTTP_FORWARDED_FOR')) {
2141
        $ipaddress = getenv('HTTP_FORWARDED_FOR');
2142
    } elseif (getenv('HTTP_FORWARDED')) {
2143
        $ipaddress = getenv('HTTP_FORWARDED');
2144
    } elseif (getenv('REMOTE_ADDR')) {
2145
        $ipaddress = getenv('REMOTE_ADDR');
2146
    } else {
2147
        $ipaddress = 'UNKNOWN';
2148
    }
2149
2150
    return $ipaddress;
2151
}
2152
2153
/**
2154
 * Escape all HTML, JavaScript, and CSS.
2155
 *
2156
 * @param string $input    The input string
2157
 * @param string $encoding Which character encoding are we using?
2158
 *
2159
 * @return string
2160
 */
2161
function noHTML($input, $encoding = 'UTF-8')
2162
{
2163
    return htmlspecialchars($input, ENT_QUOTES | ENT_XHTML, $encoding, false);
2164
}
2165
2166
/**
2167
 * 
2168
 * Permits to handle the Teampass config file
2169
 * $action accepts "rebuild" and "update"
2170
 *
2171
 * @param string $action   Action to perform
2172
 * @param array  $SETTINGS Teampass settings
2173
 * @param string $field    Field to refresh
2174
 * @param string $value    Value to set
2175
 *
2176
 * @return void
2177
 */
2178
function handleConfigFile($action, $SETTINGS, $field = null, $value = null)
2179
{
2180
    $tp_config_file = $SETTINGS['cpassman_dir'] . '/includes/config/tp.config.php';
2181
2182
    // include librairies & connect to DB
2183
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
2184
    if (defined('DB_PASSWD_CLEAR') === false) {
2185
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
0 ignored issues
show
Bug introduced by
The constant DB_PASSWD was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2186
    }
2187
    DB::$host = DB_HOST;
0 ignored issues
show
Bug introduced by
The constant DB_HOST was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2188
    DB::$user = DB_USER;
0 ignored issues
show
Bug introduced by
The constant DB_USER was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2189
    DB::$password = DB_PASSWD_CLEAR;
2190
    DB::$dbName = DB_NAME;
0 ignored issues
show
Bug introduced by
The constant DB_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2191
    DB::$port = DB_PORT;
0 ignored issues
show
Bug introduced by
The constant DB_PORT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2192
    DB::$encoding = DB_ENCODING;
2193
2194
    if (file_exists($tp_config_file) === false || $action === 'rebuild') {
2195
        // perform a copy
2196
        if (file_exists($tp_config_file)) {
2197
            if (!copy($tp_config_file, $tp_config_file . '.' . date('Y_m_d_His', time()))) {
2198
                return "ERROR: Could not copy file '" . $tp_config_file . "'";
0 ignored issues
show
Bug Best Practice introduced by
The expression return 'ERROR: Could not.... $tp_config_file . ''' returns the type string which is incompatible with the documented return type void.
Loading history...
2199
            }
2200
        }
2201
2202
2203
        // regenerate
2204
        $data = array();
2205
        $data[0] = "<?php\n";
2206
        $data[1] = "global \$SETTINGS;\n";
2207
        $data[2] = "\$SETTINGS = array (\n";
2208
        $rows = DB::query(
2209
            'SELECT * FROM ' . prefixTable('misc') . ' WHERE type=%s',
2210
            'admin'
2211
        );
2212
        foreach ($rows as $record) {
2213
            array_push($data, "    '" . $record['intitule'] . "' => '" . $record['valeur'] . "',\n");
2214
        }
2215
        array_push($data, ");\n");
2216
        $data = array_unique($data);
2217
        // ---
2218
    } elseif ($action === 'update' && empty($field) === false) {
2219
        $data = file($tp_config_file);
2220
        $inc = 0;
2221
        $bFound = false;
2222
        foreach ($data as $line) {
2223
            if (stristr($line, ');')) {
2224
                break;
2225
            }
2226
2227
            if (stristr($line, "'" . $field . "' => '")) {
2228
                $data[$inc] = "    '" . $field . "' => '" . filter_var($value, FILTER_SANITIZE_STRING) . "',\n";
2229
                $bFound = true;
2230
                break;
2231
            }
2232
            ++$inc;
2233
        }
2234
        if ($bFound === false) {
2235
            $data[($inc)] = "    '" . $field . "' => '" . filter_var($value, FILTER_SANITIZE_STRING) . "',\n);\n";
2236
        }
2237
    }
2238
2239
    // update file
2240
    file_put_contents($tp_config_file, implode('', isset($data) ? $data : array()));
2241
2242
    return true;
0 ignored issues
show
Bug Best Practice introduced by
The expression return true returns the type true which is incompatible with the documented return type void.
Loading history...
2243
}
2244
2245
2246
/**
2247
 * Permits to replace &#92; to permit correct display
2248
 *
2249
 * @param string $input Some text
2250
 *
2251
 * @return string
2252
 */
2253
function handleBackslash($input)
2254
{
2255
    return str_replace('&amp;#92;', '&#92;', $input);
2256
}
2257
2258
/*
2259
** Permits to loas settings
2260
*/
2261
function loadSettings()
2262
{
2263
    global $SETTINGS;
2264
2265
    /* LOAD CPASSMAN SETTINGS */
2266
    if (!isset($SETTINGS['loaded']) || $SETTINGS['loaded'] != 1) {
2267
        $SETTINGS['duplicate_folder'] = 0; //by default, this is set to 0;
2268
        $SETTINGS['duplicate_item'] = 0; //by default, this is set to 0;
2269
        $SETTINGS['number_of_used_pw'] = 5; //by default, this value is set to 5;
2270
        $settings = array();
2271
2272
        $rows = DB::query(
2273
            'SELECT * FROM ' . prefixTable('misc') . ' WHERE type=%s_type OR type=%s_type2',
2274
            array(
2275
                'type' => 'admin',
2276
                'type2' => 'settings',
2277
            )
2278
        );
2279
        foreach ($rows as $record) {
2280
            if ($record['type'] === 'admin') {
2281
                $SETTINGS[$record['intitule']] = $record['valeur'];
2282
            } else {
2283
                $settings[$record['intitule']] = $record['valeur'];
2284
            }
2285
        }
2286
        $SETTINGS['loaded'] = 1;
2287
        $SETTINGS['default_session_expiration_time'] = 5;
2288
    }
2289
}
2290
2291
/*
2292
** check if folder has custom fields.
2293
** Ensure that target one also has same custom fields
2294
*/
2295
function checkCFconsistency($source_id, $target_id)
2296
{
2297
    $source_cf = array();
2298
    $rows = DB::QUERY(
2299
        'SELECT id_category
2300
        FROM ' . prefixTable('categories_folders') . '
2301
        WHERE id_folder = %i',
2302
        $source_id
2303
    );
2304
    foreach ($rows as $record) {
2305
        array_push($source_cf, $record['id_category']);
2306
    }
2307
2308
    $target_cf = array();
2309
    $rows = DB::QUERY(
2310
        'SELECT id_category
2311
        FROM ' . prefixTable('categories_folders') . '
2312
        WHERE id_folder = %i',
2313
        $target_id
2314
    );
2315
    foreach ($rows as $record) {
2316
        array_push($target_cf, $record['id_category']);
2317
    }
2318
2319
    $cf_diff = array_diff($source_cf, $target_cf);
2320
    if (count($cf_diff) > 0) {
2321
        return false;
2322
    }
2323
2324
    return true;
2325
}
2326
2327
/**
2328
 * Will encrypte/decrypt a fil eusing Defuse.
2329
 *
2330
 * @param string $type        can be either encrypt or decrypt
2331
 * @param string $source_file path to source file
2332
 * @param string $target_file path to target file
2333
 * @param array  $SETTINGS    Settings
2334
 * @param string $password    A password
2335
 *
2336
 * @return string|bool
2337
 */
2338
function prepareFileWithDefuse(
2339
    $type,
2340
    $source_file,
2341
    $target_file,
2342
    $SETTINGS,
2343
    $password = null
2344
) {
2345
    // Load AntiXSS
2346
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/voku/helper/AntiXSS.php';
2347
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/voku/helper/ASCII.php';
2348
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/voku/helper/UTF8.php';
2349
    $antiXss = new voku\helper\AntiXSS();
2350
2351
    // Protect against bad inputs
2352
    if (is_array($source_file) === true || is_array($target_file) === true) {
1 ignored issue
show
introduced by
The condition is_array($target_file) === true is always false.
Loading history...
2353
        return 'error_cannot_be_array';
2354
    }
2355
2356
    // Sanitize
2357
    $source_file = $antiXss->xss_clean($source_file);
2358
    $target_file = $antiXss->xss_clean($target_file);
2359
2360
    if (empty($password) === true || is_null($password) === true) {
2361
        // get KEY to define password
2362
        $ascii_key = file_get_contents(SECUREPATH . '/teampass-seckey.txt');
0 ignored issues
show
Bug introduced by
The constant SECUREPATH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2363
        $password = \Defuse\Crypto\Key::loadFromAsciiSafeString($ascii_key);
2364
    }
2365
2366
    $err = '';
2367
    if ($type === 'decrypt') {
2368
        // Decrypt file
2369
        $err = defuseFileDecrypt(
2370
            $source_file,
2371
            $target_file,
2372
            $SETTINGS,
2373
            $password
0 ignored issues
show
Bug introduced by
It seems like $password can also be of type Defuse\Crypto\Key; however, parameter $password of defuseFileDecrypt() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

2373
            /** @scrutinizer ignore-type */ $password
Loading history...
2374
        );
2375
        // ---
2376
    } elseif ($type === 'encrypt') {
2377
        // Encrypt file
2378
        $err = defuseFileEncrypt(
2379
            $source_file,
2380
            $target_file,
2381
            $SETTINGS,
2382
            $password
0 ignored issues
show
Bug introduced by
It seems like $password can also be of type Defuse\Crypto\Key; however, parameter $password of defuseFileEncrypt() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

2382
            /** @scrutinizer ignore-type */ $password
Loading history...
2383
        );
2384
    }
2385
2386
    // return error
2387
    return empty($err) === false ? $err : true;
2388
}
2389
2390
/**
2391
 * Encrypt a file with Defuse.
2392
 *
2393
 * @param string $source_file path to source file
2394
 * @param string $target_file path to target file
2395
 * @param array  $SETTINGS    Settings
2396
 * @param string $password    A password
2397
 *
2398
 * @return string|bool
2399
 */
2400
function defuseFileEncrypt(
2401
    $source_file,
2402
    $target_file,
2403
    $SETTINGS,
2404
    $password = null
2405
) {
2406
    // load PhpEncryption library
2407
    $path_to_encryption = '/includes/libraries/Encryption/Encryption/';
2408
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Crypto.php';
2409
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Encoding.php';
2410
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'DerivedKeys.php';
2411
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Key.php';
2412
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'KeyOrPassword.php';
2413
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'File.php';
2414
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'RuntimeTests.php';
2415
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'KeyProtectedByPassword.php';
2416
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Core.php';
2417
2418
    try {
2419
        \Defuse\Crypto\File::encryptFileWithPassword(
2420
            $source_file,
2421
            $target_file,
2422
            $password
2423
        );
2424
    } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
2425
        $err = 'wrong_key';
2426
    } catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) {
2427
        $err = $ex;
2428
    } catch (Defuse\Crypto\Exception\IOException $ex) {
2429
        $err = $ex;
2430
    }
2431
2432
    // return error
2433
    return empty($err) === false ? $err : true;
2434
}
2435
2436
/**
2437
 * Decrypt a file with Defuse.
2438
 *
2439
 * @param string $source_file path to source file
2440
 * @param string $target_file path to target file
2441
 * @param array  $SETTINGS    Settings
2442
 * @param string $password    A password
2443
 *
2444
 * @return string|bool
2445
 */
2446
function defuseFileDecrypt(
2447
    $source_file,
2448
    $target_file,
2449
    $SETTINGS,
2450
    $password = null
2451
) {
2452
    // load PhpEncryption library
2453
    $path_to_encryption = '/includes/libraries/Encryption/Encryption/';
2454
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Crypto.php';
2455
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Encoding.php';
2456
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'DerivedKeys.php';
2457
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Key.php';
2458
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'KeyOrPassword.php';
2459
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'File.php';
2460
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'RuntimeTests.php';
2461
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'KeyProtectedByPassword.php';
2462
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Core.php';
2463
2464
    try {
2465
        \Defuse\Crypto\File::decryptFileWithPassword(
2466
            $source_file,
2467
            $target_file,
2468
            $password
2469
        );
2470
    } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
2471
        $err = 'wrong_key';
2472
    } catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) {
2473
        $err = $ex;
2474
    } catch (Defuse\Crypto\Exception\IOException $ex) {
2475
        $err = $ex;
2476
    }
2477
2478
    // return error
2479
    return empty($err) === false ? $err : true;
2480
}
2481
2482
/*
2483
* NOT TO BE USED
2484
*/
2485
/**
2486
 * Undocumented function.
2487
 *
2488
 * @param string $text Text to debug
2489
 */
2490
function debugTeampass($text)
2491
{
2492
    $debugFile = fopen('D:/wamp64/www/TeamPass/debug.txt', 'r+');
2493
    fputs($debugFile, $text);
0 ignored issues
show
Bug introduced by
It seems like $debugFile can also be of type false; however, parameter $handle of fputs() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

2493
    fputs(/** @scrutinizer ignore-type */ $debugFile, $text);
Loading history...
2494
    fclose($debugFile);
0 ignored issues
show
Bug introduced by
It seems like $debugFile can also be of type false; however, parameter $handle of fclose() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

2494
    fclose(/** @scrutinizer ignore-type */ $debugFile);
Loading history...
2495
}
2496
2497
/**
2498
 * DELETE the file with expected command depending on server type.
2499
 *
2500
 * @param string $file     Path to file
2501
 * @param array  $SETTINGS Teampass settings
2502
 */
2503
function fileDelete($file, $SETTINGS)
2504
{
2505
    // Load AntiXSS
2506
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/voku/helper/ASCII.php';
2507
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/voku/helper/UTF8.php';
2508
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/voku/helper/AntiXSS.php';
2509
    $antiXss = new voku\helper\AntiXSS();
2510
2511
    $file = $antiXss->xss_clean($file);
2512
    if (is_file($file)) {
2513
        unlink($file);
2514
    }
2515
}
2516
2517
/**
2518
 * Permits to extract the file extension.
2519
 *
2520
 * @param string $file File name
2521
 *
2522
 * @return string
2523
 */
2524
function getFileExtension($file)
2525
{
2526
    if (strpos($file, '.') === false) {
2527
        return $file;
2528
    }
2529
2530
    return substr($file, strrpos($file, '.') + 1);
2531
}
2532
2533
/**
2534
 * Performs chmod operation on subfolders.
2535
 *
2536
 * @param string $dir             Parent folder
2537
 * @param int    $dirPermissions  New permission on folders
2538
 * @param int    $filePermissions New permission on files
2539
 *
2540
 * @return bool
2541
 */
2542
function chmodRecursive($dir, $dirPermissions, $filePermissions)
2543
{
2544
    $pointer_dir = opendir($dir);
2545
    $res = true;
2546
    while (false !== ($file = readdir($pointer_dir))) {
0 ignored issues
show
Bug introduced by
It seems like $pointer_dir can also be of type false; however, parameter $dir_handle of readdir() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

2546
    while (false !== ($file = readdir(/** @scrutinizer ignore-type */ $pointer_dir))) {
Loading history...
2547
        if (($file === '.') || ($file === '..')) {
2548
            continue;
2549
        }
2550
2551
        $fullPath = $dir . '/' . $file;
2552
2553
        if (is_dir($fullPath)) {
2554
            if ($res = @chmod($fullPath, $dirPermissions)) {
2555
                $res = @chmodRecursive($fullPath, $dirPermissions, $filePermissions);
2556
            }
2557
        } else {
2558
            $res = chmod($fullPath, $filePermissions);
2559
        }
2560
        if (!$res) {
2561
            closedir($pointer_dir);
0 ignored issues
show
Bug introduced by
It seems like $pointer_dir can also be of type false; however, parameter $dir_handle of closedir() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

2561
            closedir(/** @scrutinizer ignore-type */ $pointer_dir);
Loading history...
2562
2563
            return false;
2564
        }
2565
    }
2566
    closedir($pointer_dir);
2567
    if (is_dir($dir) && $res) {
2568
        $res = @chmod($dir, $dirPermissions);
2569
    }
2570
2571
    return $res;
2572
}
2573
2574
/**
2575
 * Check if user can access to this item.
2576
 *
2577
 * @param int $item_id ID of item
2578
 */
2579
function accessToItemIsGranted($item_id, $SETTINGS)
2580
{
2581
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
2582
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
2583
2584
    // Prepare superGlobal variables
2585
    $session_groupes_visibles = $superGlobal->get('groupes_visibles', 'SESSION');
2586
    $session_list_restricted_folders_for_items = $superGlobal->get('list_restricted_folders_for_items', 'SESSION');
2587
2588
    // Load item data
2589
    $data = DB::queryFirstRow(
2590
        'SELECT id_tree
2591
        FROM ' . prefixTable('items') . '
2592
        WHERE id = %i',
2593
        $item_id
2594
    );
2595
2596
    // Check if user can access this folder
2597
    if (in_array($data['id_tree'], $session_groupes_visibles) === false) {
2598
        // Now check if this folder is restricted to user
2599
        if (isset($session_list_restricted_folders_for_items[$data['id_tree']]) === true
2600
            && in_array($item_id, $session_list_restricted_folders_for_items[$data['id_tree']]) === false
2601
        ) {
2602
            return 'ERR_FOLDER_NOT_ALLOWED';
2603
        }
2604
    }
2605
2606
    return true;
2607
}
2608
2609
/**
2610
 * Creates a unique key.
2611
 *
2612
 * @param float $lenght Key lenght
2613
 *
2614
 * @return string
2615
 */
2616
function uniqidReal($lenght = 13)
2617
{
2618
    // uniqid gives 13 chars, but you could adjust it to your needs.
2619
    if (function_exists('random_bytes')) {
2620
        $bytes = random_bytes(ceil($lenght / 2));
0 ignored issues
show
Bug introduced by
ceil($lenght / 2) of type double is incompatible with the type integer expected by parameter $length of random_bytes(). ( Ignorable by Annotation )

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

2620
        $bytes = random_bytes(/** @scrutinizer ignore-type */ ceil($lenght / 2));
Loading history...
2621
    } elseif (function_exists('openssl_random_pseudo_bytes')) {
2622
        $bytes = openssl_random_pseudo_bytes(ceil($lenght / 2));
0 ignored issues
show
Bug introduced by
ceil($lenght / 2) of type double is incompatible with the type integer expected by parameter $length of openssl_random_pseudo_bytes(). ( Ignorable by Annotation )

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

2622
        $bytes = openssl_random_pseudo_bytes(/** @scrutinizer ignore-type */ ceil($lenght / 2));
Loading history...
2623
    } else {
2624
        throw new Exception('no cryptographically secure random function available');
2625
    }
2626
2627
    return substr(bin2hex($bytes), 0, $lenght);
0 ignored issues
show
Bug introduced by
It seems like $lenght can also be of type double; however, parameter $length of substr() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

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

2627
    return substr(bin2hex($bytes), 0, /** @scrutinizer ignore-type */ $lenght);
Loading history...
2628
}
2629
2630
/**
2631
 * Obfuscate an email.
2632
 *
2633
 * @param string $email Email address
2634
 *
2635
 * @return string
2636
 */
2637
function obfuscateEmail($email)
2638
{
2639
    $prop = 2;
2640
    $start = '';
2641
    $end = '';
2642
    $domain = substr(strrchr($email, '@'), 1);
2643
    $mailname = str_replace($domain, '', $email);
2644
    $name_l = strlen($mailname);
2645
    $domain_l = strlen($domain);
2646
    for ($i = 0; $i <= $name_l / $prop - 1; ++$i) {
2647
        $start .= 'x';
2648
    }
2649
2650
    for ($i = 0; $i <= $domain_l / $prop - 1; ++$i) {
2651
        $end .= 'x';
2652
    }
2653
2654
    return substr_replace($mailname, $start, 2, $name_l / $prop)
2655
        . substr_replace($domain, $end, 2, $domain_l / $prop);
2656
}
2657
2658
/**
2659
 * Permits to get LDAP information about a user.
2660
 *
2661
 * @param string $username User name
2662
 * @param string $password User password
2663
 * @param array  $SETTINGS Settings
2664
 *
2665
 * @return string
2666
 */
2667
function connectLDAP($username, $password, $SETTINGS)
2668
{
2669
    $ldapInfo = '';
2670
2671
    // Prepare LDAP connection if set up
2672
2673
    if ($SETTINGS['ldap_type'] === 'posix-search') {
2674
        $ldapInfo = ldapPosixSearch(
2675
            $username,
2676
            $password,
2677
            $SETTINGS
2678
        );
2679
    } else {
2680
        $ldapInfo = ldapPosixAndWindows(
2681
            $username,
2682
            $password,
2683
            $SETTINGS
2684
        );
2685
    }
2686
2687
    return json_encode($ldapInfo);
2688
}
2689
2690
/**
2691
 * Undocumented function.
2692
 *
2693
 * @param string $username Username
2694
 * @param string $password Password
2695
 * @param array  $SETTINGS Settings
2696
 *
2697
 * @return array
2698
 */
2699
function ldapPosixSearch($username, $password, $SETTINGS)
2700
{
2701
    $ldapURIs = '';
2702
    $user_email = '';
2703
    $user_found = false;
2704
    $user_lastname = '';
2705
    $user_name = '';
2706
    $ldapConnection = false;
2707
2708
    foreach (explode(',', $SETTINGS['ldap_domain_controler']) as $domainControler) {
2709
        if ($SETTINGS['ldap_ssl'] == 1) {
2710
            $ldapURIs .= 'ldaps://' . $domainControler . ':' . $SETTINGS['ldap_port'] . ' ';
2711
        } else {
2712
            $ldapURIs .= 'ldap://' . $domainControler . ':' . $SETTINGS['ldap_port'] . ' ';
2713
        }
2714
    }
2715
    $ldapconn = ldap_connect($ldapURIs);
2716
2717
    if ($SETTINGS['ldap_tls']) {
2718
        ldap_start_tls($ldapconn);
2719
    }
2720
    ldap_set_option($ldapconn, LDAP_OPT_PROTOCOL_VERSION, 3);
2721
    ldap_set_option($ldapconn, LDAP_OPT_REFERRALS, 0);
2722
2723
    // Is LDAP connection ready?
2724
    if ($ldapconn !== false) {
2725
        // Should we bind the connection?
2726
        if (
2727
            empty($SETTINGS['ldap_bind_dn']) === false
2728
            && empty($SETTINGS['ldap_bind_passwd']) === false
2729
        ) {
2730
            $ldapbind = ldap_bind($ldapconn, $SETTINGS['ldap_bind_dn'], $SETTINGS['ldap_bind_passwd']);
2731
        } else {
2732
            $ldapbind = false;
2733
        }
2734
        if ((empty($SETTINGS['ldap_bind_dn']) === true && empty($SETTINGS['ldap_bind_passwd']) === true)
2735
            || $ldapbind === true
2736
        ) {
2737
            $filter = '(&(' . $SETTINGS['ldap_user_attribute'] . '=' . $username . ')(objectClass=' . $SETTINGS['ldap_object_class'] . '))';
2738
            $result = ldap_search(
2739
                $ldapconn,
2740
                $SETTINGS['ldap_search_base'],
2741
                $filter,
2742
                array('dn', 'mail', 'givenname', 'sn', 'samaccountname')
2743
            );
2744
2745
            // Check if user was found in AD
2746
            if (ldap_count_entries($ldapconn, $result) > 0) {
2747
                // Get user's info and especially the DN
2748
                $result = ldap_get_entries($ldapconn, $result);
2749
                $user_dn = $result[0]['dn'];
2750
                $user_email = $result[0]['mail'][0];
2751
                $user_lastname = $result[0]['sn'][0];
2752
                $user_name = isset($result[0]['givenname'][0]) === true ? $result[0]['givenname'][0] : '';
2753
                $user_found = true;
2754
2755
                // Should we restrain the search in specified user groups
2756
                $GroupRestrictionEnabled = false;
2757
                if (
2758
                    isset($SETTINGS['ldap_usergroup']) === true
2759
                    && empty($SETTINGS['ldap_usergroup']) === false
2760
                ) {
2761
                    // New way to check User's group membership
2762
                    $filter_group = 'memberUid=' . $username;
2763
                    $result_group = ldap_search(
2764
                        $ldapconn,
2765
                        $SETTINGS['ldap_search_base'],
2766
                        $filter_group,
2767
                        array('dn', 'samaccountname')
2768
                    );
2769
2770
                    if ($result_group) {
0 ignored issues
show
introduced by
$result_group is of type resource, thus it always evaluated to false.
Loading history...
2771
                        $entries = ldap_get_entries($ldapconn, $result_group);
2772
2773
                        if ($entries['count'] > 0) {
2774
                            // Now check if group fits
2775
                            for ($i = 0; $i < $entries['count']; ++$i) {
2776
                                $parsr = ldap_explode_dn($entries[$i]['dn'], 0);
2777
                                if (str_replace(array('CN=', 'cn='), '', $parsr[0]) === $SETTINGS['ldap_usergroup']) {
2778
                                    $GroupRestrictionEnabled = true;
2779
                                    break;
2780
                                }
2781
                            }
2782
                        }
2783
                    }
2784
                }
2785
2786
                // Is user in the LDAP?
2787
                if (
2788
                    $GroupRestrictionEnabled === true
2789
                    || ($GroupRestrictionEnabled === false
2790
                        && (isset($SETTINGS['ldap_usergroup']) === false
2791
                            || (isset($SETTINGS['ldap_usergroup']) === true
2792
                                && empty($SETTINGS['ldap_usergroup']) === true)))
2793
                ) {
2794
                    // Try to auth inside LDAP
2795
                    $ldapbind = ldap_bind($ldapconn, $user_dn, $password);
2796
                    if ($ldapbind === true) {
2797
                        $ldapConnection = true;
2798
                    }
2799
                }
2800
            }
2801
        }
2802
    }
2803
2804
    return array(
2805
        'lastname' => $user_lastname,
2806
        'name' => $user_name,
2807
        'email' => $user_email,
2808
        'auth_success' => $ldapConnection,
2809
        'user_found' => $user_found,
2810
    );
2811
}
2812
2813
/**
2814
 * Undocumented function.
2815
 *
2816
 * @param string $username Username
2817
 * @param string $password Password
2818
 * @param array  $SETTINGS Settings
2819
 *
2820
 * @return array
2821
 */
2822
function ldapPosixAndWindows($username, $password, $SETTINGS)
2823
{
2824
    $user_email = '';
2825
    $user_found = false;
2826
    $user_lastname = '';
2827
    $user_name = '';
2828
    $ldapConnection = false;
2829
    $ldap_suffix = '';
2830
2831
    //Multiple Domain Names
2832
    if (strpos(html_entity_decode($username), '\\') === true) {
2833
        $ldap_suffix = '@' . substr(html_entity_decode($username), 0, strpos(html_entity_decode($username), '\\'));
0 ignored issues
show
Unused Code introduced by
The assignment to $ldap_suffix is dead and can be removed.
Loading history...
2834
        $username = substr(html_entity_decode($username), strpos(html_entity_decode($username), '\\') + 1);
2835
    }
2836
    //load ClassLoader
2837
    include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php';
2838
2839
    $adldap = new SplClassLoader('adLDAP', '../includes/libraries/LDAP');
2840
    $adldap->register();
2841
2842
    // Posix style LDAP handles user searches a bit differently
2843
    if ($SETTINGS['ldap_type'] === 'posix') {
2844
        $ldap_suffix = ',' . $SETTINGS['ldap_suffix'] . ',' . $SETTINGS['ldap_domain_dn'];
2845
    } else {
2846
        // case where $SETTINGS['ldap_type'] equals 'windows'
2847
        //Multiple Domain Names
2848
        $ldap_suffix = $SETTINGS['ldap_suffix'];
2849
    }
2850
2851
    // Ensure no double commas exist in ldap_suffix
2852
    $ldap_suffix = str_replace(',,', ',', $ldap_suffix);
2853
2854
    // Create LDAP connection
2855
    $adldap = new adLDAP\adLDAP(
2856
        array(
2857
            'base_dn' => $SETTINGS['ldap_domain_dn'],
2858
            'account_suffix' => $ldap_suffix,
2859
            'domain_controllers' => explode(',', $SETTINGS['ldap_domain_controler']),
2860
            'ad_port' => $SETTINGS['ldap_port'],
2861
            'use_ssl' => $SETTINGS['ldap_ssl'],
2862
            'use_tls' => $SETTINGS['ldap_tls'],
2863
        )
2864
    );
2865
2866
    // OpenLDAP expects an attribute=value pair
2867
    if ($SETTINGS['ldap_type'] === 'posix') {
2868
        $auth_username = $SETTINGS['ldap_user_attribute'] . '=' . $username;
2869
    } else {
2870
        $auth_username = $username;
2871
    }
2872
2873
    // Authenticate the user
2874
    if ($adldap->authenticate($auth_username, html_entity_decode($password))) {
2875
        // Get user info
2876
        $result = $adldap->user()->info($auth_username, array('mail', 'givenname', 'sn'));
2877
        $user_email = $result[0]['mail'][0];
2878
        $user_lastname = $result[0]['sn'][0];
2879
        $user_name = $result[0]['givenname'][0];
2880
        $user_found = true;
2881
2882
        // Is user in allowed group
2883
        if (isset($SETTINGS['ldap_allowed_usergroup']) === true
2884
            && empty($SETTINGS['ldap_allowed_usergroup']) === false
2885
        ) {
2886
            if ($adldap->user()->inGroup($auth_username, $SETTINGS['ldap_allowed_usergroup']) === true) {
2887
                $ldapConnection = true;
2888
            }
2889
        } else {
2890
            $ldapConnection = true;
2891
        }
2892
    }
2893
2894
    return array(
2895
        'lastname' => $user_lastname,
2896
        'name' => $user_name,
2897
        'email' => $user_email,
2898
        'auth_success' => $ldapConnection,
2899
        'user_found' => $user_found,
2900
    );
2901
}
2902
2903
2904
/**
2905
 * Perform a Query.
2906
 *
2907
 * @param array  $SETTINGS Teamapss settings
2908
 * @param string $fields   Fields to use
2909
 * @param string $table    Table to use
2910
 *
2911
 * @return array
2912
 */
2913
function performDBQuery($SETTINGS, $fields, $table)
2914
{
2915
    // include librairies & connect to DB
2916
    include_once $SETTINGS['cpassman_dir'] . '/includes/config/settings.php';
2917
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
2918
    if (defined('DB_PASSWD_CLEAR') === false) {
2919
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
0 ignored issues
show
Bug introduced by
The constant DB_PASSWD was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2920
    }
2921
    DB::$host = DB_HOST;
0 ignored issues
show
Bug introduced by
The constant DB_HOST was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2922
    DB::$user = DB_USER;
0 ignored issues
show
Bug introduced by
The constant DB_USER was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2923
    DB::$password = DB_PASSWD_CLEAR;
2924
    DB::$dbName = DB_NAME;
0 ignored issues
show
Bug introduced by
The constant DB_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2925
    DB::$port = DB_PORT;
0 ignored issues
show
Bug introduced by
The constant DB_PORT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2926
    DB::$encoding = DB_ENCODING;
2927
2928
    // Insert log in DB
2929
    return DB::query(
2930
        'SELECT ' . $fields . '
2931
        FROM ' . prefixTable($table)
2932
    );
2933
}
2934
2935
/**
2936
 * Undocumented function.
2937
 *
2938
 * @param int $bytes Size of file
2939
 *
2940
 * @return string
2941
 */
2942
function formatSizeUnits($bytes)
2943
{
2944
    if ($bytes >= 1073741824) {
2945
        $bytes = number_format($bytes / 1073741824, 2) . ' GB';
2946
    } elseif ($bytes >= 1048576) {
2947
        $bytes = number_format($bytes / 1048576, 2) . ' MB';
2948
    } elseif ($bytes >= 1024) {
2949
        $bytes = number_format($bytes / 1024, 2) . ' KB';
2950
    } elseif ($bytes > 1) {
2951
        $bytes = $bytes . ' bytes';
2952
    } elseif ($bytes == 1) {
2953
        $bytes = $bytes . ' byte';
2954
    } else {
2955
        $bytes = '0 bytes';
2956
    }
2957
2958
    return $bytes;
2959
}
2960
2961
/**
2962
 * Generate user pair of keys.
2963
 *
2964
 * @param string $userPwd User password
2965
 *
2966
 * @return array
2967
 */
2968
function generateUserKeys($userPwd)
2969
{
2970
    // include library
2971
    include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php';
2972
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php';
2973
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
2974
2975
    // Load classes
2976
    $rsa = new Crypt_RSA();
2977
    $cipher = new Crypt_AES();
2978
2979
    // Create the private and public key
2980
    $res = $rsa->createKey(4096);
2981
2982
    // Encrypt the privatekey
2983
    $cipher->setPassword($userPwd);
2984
    $privatekey = $cipher->encrypt($res['privatekey']);
2985
2986
    return array(
2987
        'private_key' => base64_encode($privatekey),
2988
        'public_key' => base64_encode($res['publickey']),
2989
        'private_key_clear' => base64_encode($res['privatekey']),
2990
    );
2991
}
2992
2993
/**
2994
 * Permits to decrypt the user's privatekey.
2995
 *
2996
 * @param string $userPwd        User password
2997
 * @param string $userPrivateKey User private key
2998
 *
2999
 * @return string
3000
 */
3001
function decryptPrivateKey($userPwd, $userPrivateKey)
3002
{
3003
    if (empty($userPwd) === false) {
3004
        include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
3005
3006
        // Load classes
3007
        $cipher = new Crypt_AES();
3008
3009
        // Encrypt the privatekey
3010
        $cipher->setPassword($userPwd);
3011
3012
        return base64_encode($cipher->decrypt(base64_decode($userPrivateKey)));
3013
    }
3014
}
3015
3016
/**
3017
 * Permits to encrypt the user's privatekey.
3018
 *
3019
 * @param string $userPwd        User password
3020
 * @param string $userPrivateKey User private key
3021
 *
3022
 * @return string
3023
 */
3024
function encryptPrivateKey($userPwd, $userPrivateKey)
3025
{
3026
    if (empty($userPwd) === false) {
3027
        include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
3028
3029
        // Load classes
3030
        $cipher = new Crypt_AES();
3031
3032
        // Encrypt the privatekey
3033
        $cipher->setPassword($userPwd);
3034
3035
        return base64_encode($cipher->encrypt(base64_decode($userPrivateKey)));
3036
    }
3037
}
3038
3039
3040
/**
3041
 * Encrypts a string using AES.
3042
 *
3043
 * @param string $data String to encrypt
3044
 *
3045
 * @return array
3046
 */
3047
function doDataEncryption($data)
3048
{
3049
    // Includes
3050
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
3051
3052
    // Load classes
3053
    $cipher = new Crypt_AES(CRYPT_AES_MODE_CBC);
3054
3055
    // Generate an object key
3056
    $objectKey = uniqidReal(32);
3057
3058
    // Set it as password
3059
    $cipher->setPassword($objectKey);
3060
3061
    return array(
3062
        'encrypted' => base64_encode($cipher->encrypt($data)),
3063
        'objectKey' => base64_encode($objectKey),
3064
    );
3065
}
3066
3067
/**
3068
 * Decrypts a string using AES.
3069
 *
3070
 * @param string $data Encrypted data
3071
 * @param string $key  Key to uncrypt
3072
 *
3073
 * @return string
3074
 */
3075
function doDataDecryption($data, $key)
3076
{
3077
    // Includes
3078
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
3079
3080
    // Load classes
3081
    $cipher = new Crypt_AES();
3082
3083
    // Set the object key
3084
    $cipher->setPassword(base64_decode($key));
3085
3086
    return base64_encode($cipher->decrypt(base64_decode($data)));
3087
}
3088
3089
/**
3090
 * Encrypts using RSA a string using a public key.
3091
 *
3092
 * @param string $key       Key to be encrypted
3093
 * @param string $publicKey User public key
3094
 *
3095
 * @return string
3096
 */
3097
function encryptUserObjectKey($key, $publicKey)
3098
{
3099
    // Includes
3100
    include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php';
3101
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php';
3102
3103
    // Load classes
3104
    $rsa = new Crypt_RSA();
3105
    $rsa->loadKey(base64_decode($publicKey));
3106
3107
    // Encrypt
3108
    return base64_encode($rsa->encrypt(base64_decode($key)));
3109
}
3110
3111
/**
3112
 * Decrypts using RSA an encrypted string using a private key.
3113
 *
3114
 * @param string $key        Encrypted key
3115
 * @param string $privateKey User private key
3116
 *
3117
 * @return string
3118
 */
3119
function decryptUserObjectKey($key, $privateKey)
3120
{
3121
    // Includes
3122
    include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php';
3123
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php';
3124
3125
    // Load classes
3126
    $rsa = new Crypt_RSA();
3127
    $rsa->loadKey(base64_decode($privateKey));
3128
//echo $privateKey." ;; ".$key;
3129
    // Decrypt
3130
    return base64_encode($rsa->decrypt(base64_decode($key)));
3131
}
3132
3133
/**
3134
 * Encrypts a file.
3135
 *
3136
 * @param string $fileInName File name
3137
 * @param string $fileInPath Path to file
3138
 *
3139
 * @return array
3140
 */
3141
function encryptFile($fileInName, $fileInPath)
3142
{
3143
    if (defined('FILE_BUFFER_SIZE') === false) {
3144
        define('FILE_BUFFER_SIZE', 128 * 1024);
3145
    }
3146
3147
    // Includes
3148
    include_once '../includes/config/include.php';
3149
    include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php';
3150
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php';
3151
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
3152
3153
    // Load classes
3154
    $cipher = new Crypt_AES();
3155
3156
    // Generate an object key
3157
    $objectKey = uniqidReal(32);
3158
3159
    // Set it as password
3160
    $cipher->setPassword($objectKey);
3161
3162
    // Prevent against out of memory
3163
    $cipher->enableContinuousBuffer();
3164
    //$cipher->disablePadding();
3165
3166
    // Encrypt the file content
3167
    $plaintext = file_get_contents(
3168
        filter_var($fileInPath . '/' . $fileInName, FILTER_SANITIZE_URL)
3169
    );
3170
3171
    $ciphertext = $cipher->encrypt($plaintext);
3172
3173
    // Save new file
3174
    $hash = md5($plaintext);
3175
    $fileOut = $fileInPath . '/' . TP_FILE_PREFIX . $hash;
3176
    file_put_contents($fileOut, $ciphertext);
1 ignored issue
show
Security File Manipulation introduced by
$ciphertext can contain request data and is used in file manipulation context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read tainted data from array, and $_SERVER['PHP_AUTH_PW'] is assigned to $passwordClear
    in sources/identify.php on line 368
  2. generateUserKeys() is called
    in sources/identify.php on line 909
  3. Enters via parameter $userPwd
    in sources/main.functions.php on line 2968
  4. Crypt_Base::setPassword() is called
    in sources/main.functions.php on line 2983
  5. Enters via parameter $password
    in includes/libraries/Encryption/phpseclib/Crypt/Base.php on line 645
  6. $password . $salt is assigned to $t
    in includes/libraries/Encryption/phpseclib/Crypt/Base.php on line 681
  7. Data is passed through substr(), and substr($t, 0, $dkLen) is assigned to $key
    in includes/libraries/Encryption/phpseclib/Crypt/Base.php on line 685
  8. Data is passed through substr(), and Crypt_Base::setIV() is called
    in includes/libraries/Encryption/phpseclib/Crypt/Base.php on line 688
  9. Enters via parameter $iv
    in includes/libraries/Encryption/phpseclib/Crypt/Base.php on line 556
  10. $iv is assigned to property Crypt_AES::$iv
    in includes/libraries/Encryption/phpseclib/Crypt/Base.php on line 562
  11. Read from property Crypt_AES::$iv, and Data is passed through substr(), and Data is passed through str_pad(), and $this->decryptIV = str_pad(substr($this->iv, 0, $this->block_size), $this->block_size, '') is assigned to property Crypt_AES::$encryptIV
    in includes/libraries/Encryption/phpseclib/Crypt/Base.php on line 1944
  12. Read from property Crypt_AES::$encryptIV, and Data is passed through _openssl_ctr_process(), and $this->_openssl_ctr_process($plaintext, $this->encryptIV, $this->enbuffer) is returned
    in includes/libraries/Encryption/phpseclib/Crypt/Base.php on line 769
  13. $cipher->encrypt($plaintext) is assigned to $ciphertext
    in sources/main.functions.php on line 3171

General Strategies to prevent injection

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

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

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

$sanitized = (integer) $tainted;
Loading history...
3177
    unlink($fileInPath . '/' . $fileInName);
3178
3179
    return array(
3180
        'fileHash' => base64_encode($hash),
3181
        'objectKey' => base64_encode($objectKey),
3182
    );
3183
}
3184
3185
/**
3186
 * Decrypt a file.
3187
 *
3188
 * @param string $fileName File name
3189
 * @param string $filePath Path to file
3190
 * @param string $key      Key to use
3191
 *
3192
 * @return string
3193
 */
3194
function decryptFile($fileName, $filePath, $key)
3195
{
3196
    if (!defined('FILE_BUFFER_SIZE')) {
3197
        define('FILE_BUFFER_SIZE', 128 * 1024);
3198
    }
3199
3200
    // Includes
3201
    include_once '../includes/config/include.php';
3202
    include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php';
3203
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php';
3204
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
3205
3206
    // Get file name
3207
    $fileName = base64_decode($fileName);
3208
3209
    // Load classes
3210
    $cipher = new Crypt_AES();
3211
3212
    // Set the object key
3213
    $cipher->setPassword(base64_decode($key));
3214
3215
    // Prevent against out of memory
3216
    $cipher->enableContinuousBuffer();
3217
    $cipher->disablePadding();
3218
3219
    // Get file content
3220
    $ciphertext = file_get_contents($filePath . '/' . TP_FILE_PREFIX . $fileName);
3221
3222
    // Decrypt file content and return
3223
    return base64_encode($cipher->decrypt($ciphertext));
3224
}
3225
3226
/**
3227
 * Generate a simple password
3228
 *
3229
 * @param integer $length          Length of string
3230
 * @param boolean $symbolsincluded Allow symbols
3231
 *
3232
 * @return string
3233
 */
3234
function generateQuickPassword($length = 16, $symbolsincluded = true)
3235
{
3236
    // Generate new user password
3237
    $small_letters = range('a', 'z');
3238
    $big_letters = range('A', 'Z');
3239
    $digits = range(0, 9);
3240
    $symbols = $symbolsincluded === true ?
3241
        array('#', '_', '-', '@', '$', '+', '&') : array();
3242
3243
    $res = array_merge($small_letters, $big_letters, $digits, $symbols);
3244
    $count = count($res);
3245
    // first variant
3246
3247
    $random_string = '';
3248
    for ($i = 0; $i < $length; ++$i) {
3249
        $random_string .= $res[random_int(0, $count - 1)];
3250
    }
3251
3252
    return $random_string;
3253
}
3254
3255
/**
3256
 * Permit to store the sharekey of an object for users.
3257
 *
3258
 * @param string $object_name             Type for table selection
3259
 * @param int    $post_folder_is_personal Personal
3260
 * @param int    $post_folder_id          Folder
3261
 * @param int    $post_object_id          Object
3262
 * @param string $objectKey               Object key
3263
 * @param array  $SETTINGS                Teampass settings
3264
 *
3265
 * @return void
3266
 */
3267
function storeUsersShareKey(
3268
    $object_name,
3269
    $post_folder_is_personal,
3270
    $post_folder_id,
3271
    $post_object_id,
3272
    $objectKey,
3273
    $SETTINGS
3274
) {
3275
    // include librairies & connect to DB
3276
    include_once $SETTINGS['cpassman_dir'] . '/includes/config/settings.php';
3277
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
3278
    if (defined('DB_PASSWD_CLEAR') === false) {
3279
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
0 ignored issues
show
Bug introduced by
The constant DB_PASSWD was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3280
    }
3281
    DB::$host = DB_HOST;
0 ignored issues
show
Bug introduced by
The constant DB_HOST was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3282
    DB::$user = DB_USER;
0 ignored issues
show
Bug introduced by
The constant DB_USER was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3283
    DB::$password = DB_PASSWD_CLEAR;
3284
    DB::$dbName = DB_NAME;
0 ignored issues
show
Bug introduced by
The constant DB_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3285
    DB::$port = DB_PORT;
0 ignored issues
show
Bug introduced by
The constant DB_PORT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3286
    DB::$encoding = DB_ENCODING;
3287
3288
    // Delete existing entries for this object
3289
    DB::delete(
3290
        $object_name,
3291
        'object_id = %i',
3292
        $post_object_id
3293
    );
3294
3295
    // Superglobals
3296
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
3297
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
3298
3299
    // Prepare superGlobal variables
3300
    $sessionPpersonaFolders = $superGlobal->get('personal_folders', 'SESSION');
3301
    $sessionUserId = $superGlobal->get('user_id', 'SESSION');
3302
    $sessionUserPublicKey = $superGlobal->get('public_key', 'SESSION', 'user');
3303
3304
    if (
3305
        (int) $post_folder_is_personal === 1
3306
        && in_array($post_folder_id, $sessionPpersonaFolders) === true
3307
    ) {
3308
        // If this is a personal object
3309
        // Only create the sharekey for user
3310
        DB::insert(
3311
            $object_name,
3312
            array(
3313
                'object_id' => $post_object_id,
3314
                'user_id' => $sessionUserId,
3315
                'share_key' => encryptUserObjectKey($objectKey, $sessionUserPublicKey),
3316
            )
3317
        );
3318
    } else {
3319
        // This is a public object
3320
        // Create sharekey for each user
3321
        $users = DB::query(
3322
            'SELECT id, public_key
3323
            FROM ' . prefixTable('users') . '
3324
            WHERE id NOT IN ("' . OTV_USER_ID . '","' . SSH_USER_ID . '","' . API_USER_ID . '")
3325
            AND public_key != ""'
3326
        );
3327
        foreach ($users as $user) {
3328
            // Insert in DB the new object key for this item by user
3329
            DB::insert(
3330
                $object_name,
3331
                array(
3332
                    'object_id' => $post_object_id,
3333
                    'user_id' => $user['id'],
3334
                    'share_key' => encryptUserObjectKey(
3335
                        $objectKey,
3336
                        $user['public_key']
3337
                    ),
3338
                )
3339
            );
3340
        }
3341
    }
3342
}
3343
3344
/**
3345
 * Is this string base64 encoded?
3346
 *
3347
 * @param string $str Encoded string?
3348
 *
3349
 * @return bool
3350
 */
3351
function isBase64($str)
3352
{
3353
    $str = (string) trim($str);
3354
3355
    if (!isset($str[0])) {
3356
        return false;
3357
    }
3358
3359
    $base64String = (string) base64_decode($str, true);
3360
    if ($base64String && base64_encode($base64String) === $str) {
3361
        return true;
3362
    }
3363
3364
    return false;
3365
}
3366
3367
/**
3368
 * Undocumented function
3369
 *
3370
 * @param string $field Parameter
3371
 *
3372
 * @return string|boolean
3373
 */
3374
function filterString($field)
3375
{
3376
    // Sanitize string
3377
    $field = filter_var(trim($field), FILTER_SANITIZE_STRING);
3378
    if (empty($field) === false) {
3379
        // Load AntiXSS
3380
        include_once '../includes/libraries/voku/helper/AntiXSS.php';
3381
        $antiXss = new voku\helper\AntiXSS();
3382
        // Return
3383
        return $antiXss->xss_clean($field);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $antiXss->xss_clean($field) also could return the type array which is incompatible with the documented return type boolean|string.
Loading history...
3384
    }
3385
3386
    return false;
3387
}
3388
3389
/**
3390
 * Connect to LDAP server
3391
 *
3392
 * @param array $SETTINGS Teampass settings
3393
 *
3394
 * @return boolean||array
0 ignored issues
show
Documentation Bug introduced by
The doc comment boolean||array at position 2 could not be parsed: Unknown type name '|' at position 2 in boolean||array.
Loading history...
3395
 */
3396
function ldapConnect($SETTINGS)
3397
{
3398
    // return if ldap not enabled
3399
    if ((int) $SETTINGS['ldap_mode'] === 0) {
3400
        return false;
3401
    }
3402
3403
    if ($SETTINGS['ldap_type'] === 'posix-search') {
3404
        // LDAP with posix search
3405
        $ldapURIs = '';
3406
        foreach (explode(',', $SETTINGS['ldap_domain_controler']) as $domainControler) {
3407
            if ($SETTINGS['ldap_ssl'] == 1) {
3408
                $ldapURIs .= 'ldaps://' . $domainControler . ':' . $SETTINGS['ldap_port'] . ' ';
3409
            } else {
3410
                $ldapURIs .= 'ldap://' . $domainControler . ':' . $SETTINGS['ldap_port'] . ' ';
3411
            }
3412
        }
3413
        // Connect
3414
        $ldapconn = ldap_connect($ldapURIs);
3415
3416
        // Case of LDAP over TLS
3417
        if ($SETTINGS['ldap_tls']) {
3418
            ldap_start_tls($ldapconn);
3419
        }
3420
3421
        // Set options
3422
        ldap_set_option($ldapconn, LDAP_OPT_PROTOCOL_VERSION, 3);
3423
        ldap_set_option($ldapconn, LDAP_OPT_REFERRALS, 0);
3424
3425
        // Is LDAP connection ready?
3426
        if ($ldapconn !== false) {
3427
            // Should we bind the connection?
3428
            if ($SETTINGS['ldap_bind_dn'] !== '' && $SETTINGS['ldap_bind_passwd'] !== '') {
3429
                if (ldap_bind($ldapconn, $SETTINGS['ldap_bind_dn'], $SETTINGS['ldap_bind_passwd'])) {
3430
                    return $ldapconn;
3431
                };
3432
            }
3433
        }
3434
3435
        return false;
3436
    } else { }
3437
}
3438
3439
/**
3440
 * CHeck if provided credentials are allowed on server
3441
 *
3442
 * @param string $login    User Login
3443
 * @param string $password User Pwd
3444
 * @param array  $SETTINGS Teampass settings
3445
 *
3446
 * @return boolean||array
0 ignored issues
show
Documentation Bug introduced by
The doc comment boolean||array at position 2 could not be parsed: Unknown type name '|' at position 2 in boolean||array.
Loading history...
3447
 */
3448
function ldapCheckUserPassword($login, $password, $SETTINGS)
3449
{
3450
    $ldapconn = ldapConnect($SETTINGS);
3451
    if ($ldapconn) {
0 ignored issues
show
introduced by
$ldapconn is of type false|null|resource, thus it always evaluated to false.
Loading history...
3452
        if ($SETTINGS['ldap_type'] === 'posix-search') {
3453
            // LDAP with posix search
3454
            $filter = '(&(' . $SETTINGS['ldap_user_attribute'] . '=' . $login . ')(objectClass=' . $SETTINGS['ldap_object_class'] . '))';
3455
            $result = ldap_search(
3456
                $ldapconn,
3457
                $SETTINGS['ldap_search_base'],
3458
                $filter,
3459
                array('dn', 'mail', 'givenname', 'sn', 'samaccountname', 'shadowexpire', 'memberof')
3460
            );
3461
3462
            // Check if user was found in AD
3463
            if (ldap_count_entries($ldapconn, $result) > 0) {
3464
                // Get user's info and especially the DN
3465
                $userLDAPInfo = ldap_get_entries($ldapconn, $result);
3466
3467
                // Try to auth inside LDAP
3468
                if (ldap_bind($ldapconn, $userLDAPInfo[0]['dn'], $password) === true) {
3469
                    return true;
3470
                }
3471
            }
3472
        } else {
3473
            $adldap = new SplClassLoader('adLDAP', $SETTINGS['cpassman_dir'] . '/includes/libraries/LDAP');
3474
            $adldap->register();
3475
            $ldap_suffix = '';
3476
3477
            // Posix style LDAP handles user searches a bit differently
3478
            if ($SETTINGS['ldap_type'] === 'posix') {
3479
                $ldap_suffix = ',' . $SETTINGS['ldap_suffix'] . ',' . $SETTINGS['ldap_domain_dn'];
3480
            } elseif ($SETTINGS['ldap_type'] === 'windows') {
3481
                //Multiple Domain Names
3482
                $ldap_suffix = $SETTINGS['ldap_suffix'];
3483
            }
3484
3485
            // Ensure no double commas exist in ldap_suffix
3486
            $ldap_suffix = str_replace(',,', ',', $ldap_suffix);
3487
3488
            // Create LDAP connection
3489
            $adldap = new adLDAP\adLDAP(
3490
                array(
3491
                    'base_dn' => $SETTINGS['ldap_domain_dn'],
3492
                    'account_suffix' => $ldap_suffix,
3493
                    'domain_controllers' => explode(',', $SETTINGS['ldap_domain_controler']),
3494
                    'ad_port' => $SETTINGS['ldap_port'],
3495
                    'use_ssl' => $SETTINGS['ldap_ssl'],
3496
                    'use_tls' => $SETTINGS['ldap_tls'],
3497
                )
3498
            );
3499
3500
            // OpenLDAP expects an attribute=value pair
3501
            if ($SETTINGS['ldap_type'] === 'posix') {
3502
                $login = $SETTINGS['ldap_user_attribute'] . '=' . $login;
3503
            }
3504
3505
            // Authenticate the user
3506
            if ($adldap->authenticate($login, html_entity_decode($password))) {
3507
                return true;
3508
            }
3509
        }
3510
    }
3511
    return false;
3512
}
3513
3514
3515
/**
3516
 * Removes from DB all sharekeys of this user
3517
 *
3518
 * @param integer $userId   User's id
3519
 * @param array   $SETTINGS Teampass settings
3520
 *
3521
 * @return void
3522
 */
3523
function deleteUserObjetsKeys($userId, $SETTINGS)
3524
{
3525
    // include librairies & connect to DB
3526
    include_once $SETTINGS['cpassman_dir'] . '/includes/config/settings.php';
3527
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
3528
    if (defined('DB_PASSWD_CLEAR') === false) {
3529
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
0 ignored issues
show
Bug introduced by
The constant DB_PASSWD was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3530
    }
3531
    DB::$host = DB_HOST;
0 ignored issues
show
Bug introduced by
The constant DB_HOST was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3532
    DB::$user = DB_USER;
0 ignored issues
show
Bug introduced by
The constant DB_USER was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3533
    DB::$password = DB_PASSWD_CLEAR;
3534
    DB::$dbName = DB_NAME;
0 ignored issues
show
Bug introduced by
The constant DB_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3535
    DB::$port = DB_PORT;
0 ignored issues
show
Bug introduced by
The constant DB_PORT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3536
    DB::$encoding = DB_ENCODING;
3537
3538
    // Remove all item sharekeys items
3539
    DB::delete(
3540
        prefixTable('sharekeys_items'),
3541
        'user_id = %i',
3542
        $userId
3543
    );
3544
3545
    // Remove all item sharekeys files
3546
    DB::delete(
3547
        prefixTable('sharekeys_files'),
3548
        'user_id = %i',
3549
        $userId
3550
    );
3551
3552
    // Remove all item sharekeys fields
3553
    DB::delete(
3554
        prefixTable('sharekeys_fields'),
3555
        'user_id = %i',
3556
        $userId
3557
    );
3558
3559
    // Remove all item sharekeys logs
3560
    DB::delete(
3561
        prefixTable('sharekeys_logs'),
3562
        'user_id = %i',
3563
        $userId
3564
    );
3565
3566
    // Remove all item sharekeys suggestions
3567
    DB::delete(
3568
        prefixTable('sharekeys_suggestions'),
3569
        'user_id = %i',
3570
        $userId
3571
    );
3572
3573
    return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type void.
Loading history...
3574
}
3575