Passed
Push — teampass_3.0 ( b97e88...b1369b )
by Nils
05:44
created

ldapCheckUserPassword()   B

Complexity

Conditions 9
Paths 16

Size

Total Lines 64
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 9
eloc 35
c 1
b 0
f 0
nc 16
nop 3
dl 0
loc 64
rs 8.0555

How to fix   Long Method   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
/**
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
344
    // load PhpEncryption library
345
    if (isset($SETTINGS['cpassman_dir']) === false || empty($SETTINGS['cpassman_dir']) === true) {
346
        $path = '../includes/libraries/Encryption/Encryption/';
347
    } else {
348
        $path = $SETTINGS['cpassman_dir'] . '/includes/libraries/Encryption/Encryption/';
349
    }
350
351
    include_once $path . 'Crypto.php';
352
    include_once $path . 'Encoding.php';
353
    include_once $path . 'DerivedKeys.php';
354
    include_once $path . 'Key.php';
355
    include_once $path . 'KeyOrPassword.php';
356
    include_once $path . 'File.php';
357
    include_once $path . 'RuntimeTests.php';
358
    include_once $path . 'KeyProtectedByPassword.php';
359
    include_once $path . 'Core.php';
360
361
    // convert KEY
362
    $key = \Defuse\Crypto\Key::loadFromAsciiSafeString($ascii_key);
363
364
    try {
365
        if ($type === 'encrypt') {
366
            $text = \Defuse\Crypto\Crypto::encrypt($message, $key);
367
        } else if ($type === 'decrypt') {
368
            $text = \Defuse\Crypto\Crypto::decrypt($message, $key);
369
        }
370
    } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
371
        $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.';
372
    } catch (Defuse\Crypto\Exception\BadFormatException $ex) {
373
        $err = $ex;
374
    } catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) {
375
        $err = $ex;
376
    } catch (Defuse\Crypto\Exception\CryptoException $ex) {
377
        $err = $ex;
378
    } catch (Defuse\Crypto\Exception\IOException $ex) {
379
        $err = $ex;
380
    }
381
    //echo \Defuse\Crypto\Crypto::decrypt($message, $key).' ## ';
382
383
    return array(
384
        'string' => isset($text) ? $text : '',
385
        'error' => $err,
386
    );
387
}
388
389
/**
390
 * Generating a defuse key.
391
 *
392
 * @return string
393
 */
394
function defuse_generate_key()
395
{
396
    // load PhpEncryption library
397
    if (file_exists('../includes/config/tp.config.php') === true) {
398
        $path = '../includes/libraries/Encryption/Encryption/';
399
    } elseif (file_exists('./includes/config/tp.config.php') === true) {
400
        $path = './includes/libraries/Encryption/Encryption/';
401
    } else {
402
        $path = '../includes/libraries/Encryption/Encryption/';
403
    }
404
405
    include_once $path . 'Crypto.php';
406
    include_once $path . 'Encoding.php';
407
    include_once $path . 'DerivedKeys.php';
408
    include_once $path . 'Key.php';
409
    include_once $path . 'KeyOrPassword.php';
410
    include_once $path . 'File.php';
411
    include_once $path . 'RuntimeTests.php';
412
    include_once $path . 'KeyProtectedByPassword.php';
413
    include_once $path . 'Core.php';
414
415
    $key = \Defuse\Crypto\Key::createNewRandomKey();
416
    $key = $key->saveToAsciiSafeString();
417
418
    return $key;
419
}
420
421
/**
422
 * Generate a Defuse personal key.
423
 *
424
 * @param string $psk psk used
425
 *
426
 * @return string
427
 */
428
function defuse_generate_personal_key($psk)
429
{
430
    // load PhpEncryption library
431
    if (file_exists('../includes/config/tp.config.php') === true) {
432
        $path = '../includes/libraries/Encryption/Encryption/';
433
    } elseif (file_exists('./includes/config/tp.config.php') === true) {
434
        $path = './includes/libraries/Encryption/Encryption/';
435
    } else {
436
        $path = '../includes/libraries/Encryption/Encryption/';
437
    }
438
439
    include_once $path . 'Crypto.php';
440
    include_once $path . 'Encoding.php';
441
    include_once $path . 'DerivedKeys.php';
442
    include_once $path . 'Key.php';
443
    include_once $path . 'KeyOrPassword.php';
444
    include_once $path . 'File.php';
445
    include_once $path . 'RuntimeTests.php';
446
    include_once $path . 'KeyProtectedByPassword.php';
447
    include_once $path . 'Core.php';
448
449
    $protected_key = \Defuse\Crypto\KeyProtectedByPassword::createRandomPasswordProtectedKey($psk);
450
    $protected_key_encoded = $protected_key->saveToAsciiSafeString();
451
452
    return $protected_key_encoded; // save this in user table
453
}
454
455
/**
456
 * Validate persoanl key with defuse.
457
 *
458
 * @param string $psk                   the user's psk
459
 * @param string $protected_key_encoded special key
460
 *
461
 * @return string
462
 */
463
function defuse_validate_personal_key($psk, $protected_key_encoded)
464
{
465
    // load PhpEncryption library
466
    if (file_exists('../includes/config/tp.config.php') === true) {
467
        $path = '../includes/libraries/Encryption/Encryption/';
468
    } elseif (file_exists('./includes/config/tp.config.php') === true) {
469
        $path = './includes/libraries/Encryption/Encryption/';
470
    } else {
471
        $path = '../includes/libraries/Encryption/Encryption/';
472
    }
473
474
    include_once $path . 'Crypto.php';
475
    include_once $path . 'Encoding.php';
476
    include_once $path . 'DerivedKeys.php';
477
    include_once $path . 'Key.php';
478
    include_once $path . 'KeyOrPassword.php';
479
    include_once $path . 'File.php';
480
    include_once $path . 'RuntimeTests.php';
481
    include_once $path . 'KeyProtectedByPassword.php';
482
    include_once $path . 'Core.php';
483
484
    try {
485
        $protected_key = \Defuse\Crypto\KeyProtectedByPassword::loadFromAsciiSafeString($protected_key_encoded);
486
        $user_key = $protected_key->unlockKey($psk);
487
        $user_key_encoded = $user_key->saveToAsciiSafeString();
488
    } catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) {
489
        return 'Error - Major issue as the encryption is broken.';
490
    } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
491
        return 'Error - The saltkey is not the correct one.';
492
    }
493
494
    return $user_key_encoded; // store it in session once user has entered his psk
495
}
496
497
/**
498
 * Decrypt a defuse string if encrypted.
499
 *
500
 * @param string $value Encrypted string
501
 *
502
 * @return string Decrypted string
503
 */
504
function defuseReturnDecrypted($value, $SETTINGS)
505
{
506
    if (substr($value, 0, 3) === 'def') {
507
        $value = cryption($value, '', 'decrypt', $SETTINGS)['string'];
508
    }
509
510
    return $value;
511
}
512
513
/**
514
 * Trims a string depending on a specific string.
515
 *
516
 * @param string|array $chaine  what to trim
517
 * @param string       $element trim on what
518
 *
519
 * @return string
520
 */
521
function trimElement($chaine, $element)
522
{
523
    if (!empty($chaine)) {
524
        if (is_array($chaine) === true) {
525
            $chaine = implode(';', $chaine);
526
        }
527
        $chaine = trim($chaine);
528
        if (substr($chaine, 0, 1) === $element) {
529
            $chaine = substr($chaine, 1);
530
        }
531
        if (substr($chaine, strlen($chaine) - 1, 1) === $element) {
532
            $chaine = substr($chaine, 0, strlen($chaine) - 1);
533
        }
534
    }
535
536
    return $chaine;
537
}
538
539
/**
540
 * Permits to suppress all "special" characters from string.
541
 *
542
 * @param string $string  what to clean
543
 * @param bool   $special use of special chars?
544
 *
545
 * @return string
546
 */
547
function cleanString($string, $special = false)
548
{
549
    // Create temporary table for special characters escape
550
    $tabSpecialChar = array();
551
    for ($i = 0; $i <= 31; ++$i) {
552
        $tabSpecialChar[] = chr($i);
553
    }
554
    array_push($tabSpecialChar, '<br />');
555
    if ((int) $special === 1) {
556
        $tabSpecialChar = array_merge($tabSpecialChar, array('</li>', '<ul>', '<ol>'));
557
    }
558
559
    return str_replace($tabSpecialChar, "\n", $string);
560
}
561
562
/**
563
 * Erro manager for DB.
564
 *
565
 * @param array $params output from query
566
 */
567
function db_error_handler($params)
568
{
569
    echo 'Error: ' . $params['error'] . "<br>\n";
570
    echo 'Query: ' . $params['query'] . "<br>\n";
571
    throw new Exception('Error - Query', 1);
572
}
573
574
/**
575
 * [identifyUserRights description].
576
 *
577
 * @param string $groupesVisiblesUser  [description]
578
 * @param string $groupesInterditsUser [description]
579
 * @param string $isAdmin              [description]
580
 * @param string $idFonctions          [description]
581
 *
582
 * @return string [description]
583
 */
584
function identifyUserRights(
585
    $groupesVisiblesUser,
586
    $groupesInterditsUser,
587
    $isAdmin,
588
    $idFonctions,
589
    $SETTINGS
590
) {
591
    //load ClassLoader
592
    include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php';
593
594
    // Load superglobal
595
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
596
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
597
598
    //Connect to DB
599
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
600
    if (defined('DB_PASSWD_CLEAR') === false) {
601
        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...
602
    }
603
    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...
604
    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...
605
    DB::$password = DB_PASSWD_CLEAR;
606
    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...
607
    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...
608
    DB::$encoding = DB_ENCODING;
609
610
    //Build tree
611
    $tree = new SplClassLoader('Tree\NestedTree', $SETTINGS['cpassman_dir'] . '/includes/libraries');
612
    $tree->register();
613
    $tree = new Tree\NestedTree\NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
614
615
    // Check if user is ADMINISTRATOR
616
    if ((int) $isAdmin === 1) {
617
        identAdmin(
618
            $idFonctions,
619
            $SETTINGS,
620
            $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

620
            /** @scrutinizer ignore-type */ $tree
Loading history...
621
        );
622
    } else {
623
        identUser(
624
            $groupesVisiblesUser,
625
            $groupesInterditsUser,
626
            $idFonctions,
627
            $SETTINGS,
628
            $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

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

1696
                /** @scrutinizer ignore-type */ $data,
Loading history...
1697
                true
1698
            );
1699
        } else {
1700
            return json_decode(
1701
                Encryption\Crypt\aesctr::decrypt(
1702
                    $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

1702
                    /** @scrutinizer ignore-type */ $data,
Loading history...
1703
                    $globalsKey,
1704
                    256
1705
                ),
1706
                true
1707
            );
1708
        }
1709
    }
1710
}
1711
1712
/**
1713
 * Create a thumbnail.
1714
 *
1715
 * @param string $src           Source
1716
 * @param string $dest          Destination
1717
 * @param float  $desired_width Size of width
1718
 */
1719
function makeThumbnail($src, $dest, $desired_width)
1720
{
1721
    /* read the source image */
1722
    $source_image = imagecreatefrompng($src);
1723
    $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

1723
    $width = imagesx(/** @scrutinizer ignore-type */ $source_image);
Loading history...
1724
    $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

1724
    $height = imagesy(/** @scrutinizer ignore-type */ $source_image);
Loading history...
1725
1726
    /* find the "desired height" of this thumbnail, relative to the desired width  */
1727
    $desired_height = floor($height * ($desired_width / $width));
1728
1729
    /* create a new, "virtual" image */
1730
    $virtual_image = imagecreatetruecolor($desired_width, $desired_height);
0 ignored issues
show
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

1730
    $virtual_image = imagecreatetruecolor($desired_width, /** @scrutinizer ignore-type */ $desired_height);
Loading history...
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

1730
    $virtual_image = imagecreatetruecolor(/** @scrutinizer ignore-type */ $desired_width, $desired_height);
Loading history...
1731
1732
    /* copy source image at a resized size */
1733
    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

1733
    imagecopyresampled($virtual_image, $source_image, 0, 0, 0, 0, $desired_width, /** @scrutinizer ignore-type */ $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

1733
    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 $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

1733
    imagecopyresampled(/** @scrutinizer ignore-type */ $virtual_image, $source_image, 0, 0, 0, 0, $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

1733
    imagecopyresampled($virtual_image, /** @scrutinizer ignore-type */ $source_image, 0, 0, 0, 0, $desired_width, $desired_height, $width, $height);
Loading history...
1734
1735
    /* create the physical thumbnail image to its destination */
1736
    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

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

2367
            /** @scrutinizer ignore-type */ $password
Loading history...
2368
        );
2369
        // ---
2370
    } elseif ($type === 'encrypt') {
2371
        // Encrypt file
2372
        $err = defuseFileEncrypt(
2373
            $source_file,
2374
            $target_file,
2375
            $SETTINGS,
2376
            $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

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

2487
    fputs(/** @scrutinizer ignore-type */ $debugFile, $text);
Loading history...
2488
    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

2488
    fclose(/** @scrutinizer ignore-type */ $debugFile);
Loading history...
2489
}
2490
2491
/**
2492
 * DELETE the file with expected command depending on server type.
2493
 *
2494
 * @param string $file     Path to file
2495
 * @param array  $SETTINGS Teampass settings
2496
 */
2497
function fileDelete($file, $SETTINGS)
2498
{
2499
    // Load AntiXSS
2500
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/AntiXSS/AntiXSS.php';
2501
    $antiXss = new voku\helper\AntiXSS();
2502
2503
    $file = $antiXss->xss_clean($file);
2504
    if (is_file($file)) {
2505
        unlink($file);
2506
    }
2507
}
2508
2509
/**
2510
 * Permits to extract the file extension.
2511
 *
2512
 * @param string $file File name
2513
 *
2514
 * @return string
2515
 */
2516
function getFileExtension($file)
2517
{
2518
    if (strpos($file, '.') === false) {
2519
        return $file;
2520
    }
2521
2522
    return substr($file, strrpos($file, '.') + 1);
2523
}
2524
2525
/**
2526
 * Performs chmod operation on subfolders.
2527
 *
2528
 * @param string $dir             Parent folder
2529
 * @param int    $dirPermissions  New permission on folders
2530
 * @param int    $filePermissions New permission on files
2531
 *
2532
 * @return bool
2533
 */
2534
function chmodRecursive($dir, $dirPermissions, $filePermissions)
2535
{
2536
    $pointer_dir = opendir($dir);
2537
    $res = true;
2538
    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

2538
    while (false !== ($file = readdir(/** @scrutinizer ignore-type */ $pointer_dir))) {
Loading history...
2539
        if (($file === '.') || ($file === '..')) {
2540
            continue;
2541
        }
2542
2543
        $fullPath = $dir . '/' . $file;
2544
2545
        if (is_dir($fullPath)) {
2546
            if ($res = @chmod($fullPath, $dirPermissions)) {
2547
                $res = @chmodRecursive($fullPath, $dirPermissions, $filePermissions);
2548
            }
2549
        } else {
2550
            $res = chmod($fullPath, $filePermissions);
2551
        }
2552
        if (!$res) {
2553
            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

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

2613
        $bytes = random_bytes(/** @scrutinizer ignore-type */ ceil($lenght / 2));
Loading history...
2614
    } elseif (function_exists('openssl_random_pseudo_bytes')) {
2615
        $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

2615
        $bytes = openssl_random_pseudo_bytes(/** @scrutinizer ignore-type */ ceil($lenght / 2));
Loading history...
2616
    } else {
2617
        throw new Exception('no cryptographically secure random function available');
2618
    }
2619
2620
    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

2620
    return substr(bin2hex($bytes), 0, /** @scrutinizer ignore-type */ $lenght);
Loading history...
2621
}
2622
2623
/**
2624
 * Obfuscate an email.
2625
 *
2626
 * @param string $email Email address
2627
 *
2628
 * @return string
2629
 */
2630
function obfuscateEmail($email)
2631
{
2632
    $prop = 2;
2633
    $start = '';
2634
    $end = '';
2635
    $domain = substr(strrchr($email, '@'), 1);
2636
    $mailname = str_replace($domain, '', $email);
2637
    $name_l = strlen($mailname);
2638
    $domain_l = strlen($domain);
2639
    for ($i = 0; $i <= $name_l / $prop - 1; ++$i) {
2640
        $start .= 'x';
2641
    }
2642
2643
    for ($i = 0; $i <= $domain_l / $prop - 1; ++$i) {
2644
        $end .= 'x';
2645
    }
2646
2647
    return substr_replace($mailname, $start, 2, $name_l / $prop)
2648
        . substr_replace($domain, $end, 2, $domain_l / $prop);
2649
}
2650
2651
/**
2652
 * Permits to get LDAP information about a user.
2653
 *
2654
 * @param string $username User name
2655
 * @param string $password User password
2656
 * @param array  $SETTINGS Settings
2657
 *
2658
 * @return string
2659
 */
2660
function connectLDAP($username, $password, $SETTINGS)
2661
{
2662
    $ldapInfo = '';
2663
2664
    // Prepare LDAP connection if set up
2665
2666
    if ($SETTINGS['ldap_type'] === 'posix-search') {
2667
        $ldapInfo = ldapPosixSearch(
2668
            $username,
2669
            $password,
2670
            $SETTINGS
2671
        );
2672
    } else {
2673
        $ldapInfo = ldapPosixAndWindows(
2674
            $username,
2675
            $password,
2676
            $SETTINGS
2677
        );
2678
    }
2679
2680
    return json_encode($ldapInfo);
2681
}
2682
2683
/**
2684
 * Undocumented function.
2685
 *
2686
 * @param string $username Username
2687
 * @param string $password Password
2688
 * @param array  $SETTINGS Settings
2689
 *
2690
 * @return array
2691
 */
2692
function ldapPosixSearch($username, $password, $SETTINGS)
2693
{
2694
    $ldapURIs = '';
2695
    $user_email = '';
2696
    $user_found = false;
2697
    $user_lastname = '';
2698
    $user_name = '';
2699
    $ldapConnection = false;
2700
2701
    foreach (explode(',', $SETTINGS['ldap_domain_controler']) as $domainControler) {
2702
        if ($SETTINGS['ldap_ssl'] == 1) {
2703
            $ldapURIs .= 'ldaps://' . $domainControler . ':' . $SETTINGS['ldap_port'] . ' ';
2704
        } else {
2705
            $ldapURIs .= 'ldap://' . $domainControler . ':' . $SETTINGS['ldap_port'] . ' ';
2706
        }
2707
    }
2708
    $ldapconn = ldap_connect($ldapURIs);
2709
2710
    if ($SETTINGS['ldap_tls']) {
2711
        ldap_start_tls($ldapconn);
2712
    }
2713
    ldap_set_option($ldapconn, LDAP_OPT_PROTOCOL_VERSION, 3);
2714
    ldap_set_option($ldapconn, LDAP_OPT_REFERRALS, 0);
2715
2716
    // Is LDAP connection ready?
2717
    if ($ldapconn !== false) {
2718
        // Should we bind the connection?
2719
        if (
2720
            empty($SETTINGS['ldap_bind_dn']) === false
2721
            && empty($SETTINGS['ldap_bind_passwd']) === false
2722
        ) {
2723
            $ldapbind = ldap_bind($ldapconn, $SETTINGS['ldap_bind_dn'], $SETTINGS['ldap_bind_passwd']);
2724
        } else {
2725
            $ldapbind = false;
2726
        }
2727
        if ((empty($SETTINGS['ldap_bind_dn']) === true && empty($SETTINGS['ldap_bind_passwd']) === true)
2728
            || $ldapbind === true
2729
        ) {
2730
            $filter = '(&(' . $SETTINGS['ldap_user_attribute'] . '=' . $username . ')(objectClass=' . $SETTINGS['ldap_object_class'] . '))';
2731
            $result = ldap_search(
2732
                $ldapconn,
2733
                $SETTINGS['ldap_search_base'],
2734
                $filter,
2735
                array('dn', 'mail', 'givenname', 'sn', 'samaccountname')
2736
            );
2737
2738
            // Check if user was found in AD
2739
            if (ldap_count_entries($ldapconn, $result) > 0) {
2740
                // Get user's info and especially the DN
2741
                $result = ldap_get_entries($ldapconn, $result);
2742
                $user_dn = $result[0]['dn'];
2743
                $user_email = $result[0]['mail'][0];
2744
                $user_lastname = $result[0]['sn'][0];
2745
                $user_name = isset($result[0]['givenname'][0]) === true ? $result[0]['givenname'][0] : '';
2746
                $user_found = true;
2747
2748
                // Should we restrain the search in specified user groups
2749
                $GroupRestrictionEnabled = false;
2750
                if (
2751
                    isset($SETTINGS['ldap_usergroup']) === true
2752
                    && empty($SETTINGS['ldap_usergroup']) === false
2753
                ) {
2754
                    // New way to check User's group membership
2755
                    $filter_group = 'memberUid=' . $username;
2756
                    $result_group = ldap_search(
2757
                        $ldapconn,
2758
                        $SETTINGS['ldap_search_base'],
2759
                        $filter_group,
2760
                        array('dn', 'samaccountname')
2761
                    );
2762
2763
                    if ($result_group) {
0 ignored issues
show
introduced by
$result_group is of type resource, thus it always evaluated to false.
Loading history...
2764
                        $entries = ldap_get_entries($ldapconn, $result_group);
2765
2766
                        if ($entries['count'] > 0) {
2767
                            // Now check if group fits
2768
                            for ($i = 0; $i < $entries['count']; ++$i) {
2769
                                $parsr = ldap_explode_dn($entries[$i]['dn'], 0);
2770
                                if (str_replace(array('CN=', 'cn='), '', $parsr[0]) === $SETTINGS['ldap_usergroup']) {
2771
                                    $GroupRestrictionEnabled = true;
2772
                                    break;
2773
                                }
2774
                            }
2775
                        }
2776
                    }
2777
                }
2778
2779
                // Is user in the LDAP?
2780
                if (
2781
                    $GroupRestrictionEnabled === true
2782
                    || ($GroupRestrictionEnabled === false
2783
                        && (isset($SETTINGS['ldap_usergroup']) === false
2784
                            || (isset($SETTINGS['ldap_usergroup']) === true
2785
                                && empty($SETTINGS['ldap_usergroup']) === true)))
2786
                ) {
2787
                    // Try to auth inside LDAP
2788
                    $ldapbind = ldap_bind($ldapconn, $user_dn, $password);
2789
                    if ($ldapbind === true) {
2790
                        $ldapConnection = true;
2791
                    }
2792
                }
2793
            }
2794
        }
2795
    }
2796
2797
    return array(
2798
        'lastname' => $user_lastname,
2799
        'name' => $user_name,
2800
        'email' => $user_email,
2801
        'auth_success' => $ldapConnection,
2802
        'user_found' => $user_found,
2803
    );
2804
}
2805
2806
/**
2807
 * Undocumented function.
2808
 *
2809
 * @param string $username Username
2810
 * @param string $password Password
2811
 * @param array  $SETTINGS Settings
2812
 *
2813
 * @return array
2814
 */
2815
function ldapPosixAndWindows($username, $password, $SETTINGS)
2816
{
2817
    $user_email = '';
2818
    $user_found = false;
2819
    $user_lastname = '';
2820
    $user_name = '';
2821
    $ldapConnection = false;
2822
    $ldap_suffix = '';
2823
2824
    //Multiple Domain Names
2825
    if (strpos(html_entity_decode($username), '\\') === true) {
2826
        $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...
2827
        $username = substr(html_entity_decode($username), strpos(html_entity_decode($username), '\\') + 1);
2828
    }
2829
    //load ClassLoader
2830
    include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php';
2831
2832
    $adldap = new SplClassLoader('adLDAP', '../includes/libraries/LDAP');
2833
    $adldap->register();
2834
2835
    // Posix style LDAP handles user searches a bit differently
2836
    if ($SETTINGS['ldap_type'] === 'posix') {
2837
        $ldap_suffix = ',' . $SETTINGS['ldap_suffix'] . ',' . $SETTINGS['ldap_domain_dn'];
2838
    } else {
2839
        // case where $SETTINGS['ldap_type'] equals 'windows'
2840
        //Multiple Domain Names
2841
        $ldap_suffix = $SETTINGS['ldap_suffix'];
2842
    }
2843
2844
    // Ensure no double commas exist in ldap_suffix
2845
    $ldap_suffix = str_replace(',,', ',', $ldap_suffix);
2846
2847
    // Create LDAP connection
2848
    $adldap = new adLDAP\adLDAP(
2849
        array(
2850
            'base_dn' => $SETTINGS['ldap_domain_dn'],
2851
            'account_suffix' => $ldap_suffix,
2852
            'domain_controllers' => explode(',', $SETTINGS['ldap_domain_controler']),
2853
            'ad_port' => $SETTINGS['ldap_port'],
2854
            'use_ssl' => $SETTINGS['ldap_ssl'],
2855
            'use_tls' => $SETTINGS['ldap_tls'],
2856
        )
2857
    );
2858
2859
    // OpenLDAP expects an attribute=value pair
2860
    if ($SETTINGS['ldap_type'] === 'posix') {
2861
        $auth_username = $SETTINGS['ldap_user_attribute'] . '=' . $username;
2862
    } else {
2863
        $auth_username = $username;
2864
    }
2865
2866
    // Authenticate the user
2867
    if ($adldap->authenticate($auth_username, html_entity_decode($password))) {
2868
        // Get user info
2869
        $result = $adldap->user()->info($auth_username, array('mail', 'givenname', 'sn'));
2870
        $user_email = $result[0]['mail'][0];
2871
        $user_lastname = $result[0]['sn'][0];
2872
        $user_name = $result[0]['givenname'][0];
2873
        $user_found = true;
2874
2875
        // Is user in allowed group
2876
        if (
2877
            isset($SETTINGS['ldap_allowed_usergroup']) === true
2878
            && empty($SETTINGS['ldap_allowed_usergroup']) === false
2879
        ) {
2880
            if ($adldap->user()->inGroup($auth_username, $SETTINGS['ldap_allowed_usergroup']) === true) {
2881
                $ldapConnection = true;
2882
            }
2883
        } else {
2884
            $ldapConnection = true;
2885
        }
2886
    }
2887
2888
    return array(
2889
        'lastname' => $user_lastname,
2890
        'name' => $user_name,
2891
        'email' => $user_email,
2892
        'auth_success' => $ldapConnection,
2893
        'user_found' => $user_found,
2894
    );
2895
}
2896
2897
2898
/**
2899
 * Perform a Query.
2900
 *
2901
 * @param array  $SETTINGS Teamapss settings
2902
 * @param string $fields   Fields to use
2903
 * @param string $table    Table to use
2904
 *
2905
 * @return array
2906
 */
2907
function performDBQuery($SETTINGS, $fields, $table)
2908
{
2909
    // include librairies & connect to DB
2910
    include_once $SETTINGS['cpassman_dir'] . '/includes/config/settings.php';
2911
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
2912
    if (defined('DB_PASSWD_CLEAR') === false) {
2913
        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...
2914
    }
2915
    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...
2916
    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...
2917
    DB::$password = DB_PASSWD_CLEAR;
2918
    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...
2919
    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...
2920
    DB::$encoding = DB_ENCODING;
2921
2922
    // Insert log in DB
2923
    return DB::query(
2924
        'SELECT ' . $fields . '
2925
        FROM ' . prefixTable($table)
2926
    );
2927
}
2928
2929
/**
2930
 * Undocumented function.
2931
 *
2932
 * @param int $bytes Size of file
2933
 *
2934
 * @return string
2935
 */
2936
function formatSizeUnits($bytes)
2937
{
2938
    if ($bytes >= 1073741824) {
2939
        $bytes = number_format($bytes / 1073741824, 2) . ' GB';
2940
    } elseif ($bytes >= 1048576) {
2941
        $bytes = number_format($bytes / 1048576, 2) . ' MB';
2942
    } elseif ($bytes >= 1024) {
2943
        $bytes = number_format($bytes / 1024, 2) . ' KB';
2944
    } elseif ($bytes > 1) {
2945
        $bytes = $bytes . ' bytes';
2946
    } elseif ($bytes == 1) {
2947
        $bytes = $bytes . ' byte';
2948
    } else {
2949
        $bytes = '0 bytes';
2950
    }
2951
2952
    return $bytes;
2953
}
2954
2955
/**
2956
 * Generate user pair of keys.
2957
 *
2958
 * @param string $userPwd User password
2959
 *
2960
 * @return array
2961
 */
2962
function generateUserKeys($userPwd)
2963
{
2964
    // include library
2965
    include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php';
2966
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php';
2967
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
2968
2969
    // Load classes
2970
    $rsa = new Crypt_RSA();
2971
    $cipher = new Crypt_AES();
2972
2973
    // Create the private and public key
2974
    $res = $rsa->createKey(4096);
2975
2976
    // Encrypt the privatekey
2977
    $cipher->setPassword($userPwd);
2978
    $privatekey = $cipher->encrypt($res['privatekey']);
2979
2980
    return array(
2981
        'private_key' => base64_encode($privatekey),
2982
        'public_key' => base64_encode($res['publickey']),
2983
        'private_key_clear' => base64_encode($res['privatekey']),
2984
    );
2985
}
2986
2987
/**
2988
 * Permits to decrypt the user's privatekey.
2989
 *
2990
 * @param string $userPwd        User password
2991
 * @param string $userPrivateKey User private key
2992
 *
2993
 * @return string
2994
 */
2995
function decryptPrivateKey($userPwd, $userPrivateKey)
2996
{
2997
    if (empty($userPwd) === false) {
2998
        include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
2999
3000
        // Load classes
3001
        $cipher = new Crypt_AES();
3002
3003
        // Encrypt the privatekey
3004
        $cipher->setPassword($userPwd);
3005
3006
        return base64_encode($cipher->decrypt(base64_decode($userPrivateKey)));
3007
    }
3008
}
3009
3010
/**
3011
 * Permits to encrypt the user's privatekey.
3012
 *
3013
 * @param string $userPwd        User password
3014
 * @param string $userPrivateKey User private key
3015
 *
3016
 * @return string
3017
 */
3018
function encryptPrivateKey($userPwd, $userPrivateKey)
3019
{
3020
    if (empty($userPwd) === false) {
3021
        include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
3022
3023
        // Load classes
3024
        $cipher = new Crypt_AES();
3025
3026
        // Encrypt the privatekey
3027
        $cipher->setPassword($userPwd);
3028
3029
        return base64_encode($cipher->encrypt(base64_decode($userPrivateKey)));
3030
    }
3031
}
3032
3033
3034
/**
3035
 * Encrypts a string using AES.
3036
 *
3037
 * @param string $data String to encrypt
3038
 *
3039
 * @return array
3040
 */
3041
function doDataEncryption($data)
3042
{
3043
    // Includes
3044
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
3045
3046
    // Load classes
3047
    $cipher = new Crypt_AES(CRYPT_AES_MODE_CBC);
3048
3049
    // Generate an object key
3050
    $objectKey = uniqidReal(32);
3051
3052
    // Set it as password
3053
    $cipher->setPassword($objectKey);
3054
3055
    return array(
3056
        'encrypted' => base64_encode($cipher->encrypt($data)),
3057
        'objectKey' => base64_encode($objectKey),
3058
    );
3059
}
3060
3061
/**
3062
 * Decrypts a string using AES.
3063
 *
3064
 * @param string $data Encrypted data
3065
 * @param string $key  Key to uncrypt
3066
 *
3067
 * @return string
3068
 */
3069
function doDataDecryption($data, $key)
3070
{
3071
    // Includes
3072
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
3073
3074
    // Load classes
3075
    $cipher = new Crypt_AES();
3076
3077
    // Set the object key
3078
    $cipher->setPassword(base64_decode($key));
3079
3080
    return base64_encode($cipher->decrypt(base64_decode($data)));
3081
}
3082
3083
/**
3084
 * Encrypts using RSA a string using a public key.
3085
 *
3086
 * @param string $key       Key to be encrypted
3087
 * @param string $publicKey User public key
3088
 *
3089
 * @return string
3090
 */
3091
function encryptUserObjectKey($key, $publicKey)
3092
{
3093
    // Includes
3094
    include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php';
3095
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php';
3096
3097
    // Load classes
3098
    $rsa = new Crypt_RSA();
3099
    $rsa->loadKey(base64_decode($publicKey));
3100
3101
    // Encrypt
3102
    return base64_encode($rsa->encrypt(base64_decode($key)));
3103
}
3104
3105
/**
3106
 * Decrypts using RSA an encrypted string using a private key.
3107
 *
3108
 * @param string $key        Encrypted key
3109
 * @param string $privateKey User private key
3110
 *
3111
 * @return string
3112
 */
3113
function decryptUserObjectKey($key, $privateKey)
3114
{
3115
    // Includes
3116
    include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php';
3117
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php';
3118
3119
    // Load classes
3120
    $rsa = new Crypt_RSA();
3121
    $rsa->loadKey(base64_decode($privateKey));
3122
3123
    // Decrypt
3124
    return base64_encode($rsa->decrypt(base64_decode($key)));
3125
}
3126
3127
/**
3128
 * Encrypts a file.
3129
 *
3130
 * @param string $fileInName File name
3131
 * @param string $fileInPath Path to file
3132
 *
3133
 * @return array
3134
 */
3135
function encryptFile($fileInName, $fileInPath)
3136
{
3137
    if (defined('FILE_BUFFER_SIZE') === false) {
3138
        define('FILE_BUFFER_SIZE', 128 * 1024);
3139
    }
3140
3141
    // Includes
3142
    include_once '../includes/config/include.php';
3143
    include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php';
3144
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php';
3145
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
3146
3147
    // Load classes
3148
    $cipher = new Crypt_AES();
3149
3150
    // Generate an object key
3151
    $objectKey = uniqidReal(32);
3152
3153
    // Set it as password
3154
    $cipher->setPassword($objectKey);
3155
3156
    // Prevent against out of memory
3157
    $cipher->enableContinuousBuffer();
3158
    $cipher->disablePadding();
3159
3160
    // Encrypt the file content
3161
    $plaintext = file_get_contents(
3162
        filter_var($fileInPath . '/' . $fileInName, FILTER_SANITIZE_URL)
3163
    );
3164
    $ciphertext = $cipher->encrypt($plaintext);
3165
3166
    // Save new file
3167
    $hash = md5($plaintext);
3168
    $fileOut = $fileInPath . '/' . TP_FILE_PREFIX . $hash;
3169
    file_put_contents($fileOut, $ciphertext);
3170
    unlink($fileInPath . '/' . $fileInName);
3171
3172
    return array(
3173
        'fileHash' => base64_encode($hash),
3174
        'objectKey' => base64_encode($objectKey),
3175
    );
3176
}
3177
3178
/**
3179
 * Decrypt a file.
3180
 *
3181
 * @param string $fileName File name
3182
 * @param string $filePath Path to file
3183
 * @param string $key      Key to use
3184
 *
3185
 * @return string
3186
 */
3187
function decryptFile($fileName, $filePath, $key)
3188
{
3189
    if (!defined('FILE_BUFFER_SIZE')) {
3190
        define('FILE_BUFFER_SIZE', 128 * 1024);
3191
    }
3192
3193
    // Includes
3194
    include_once '../includes/config/include.php';
3195
    include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php';
3196
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php';
3197
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
3198
3199
    // Get file name
3200
    $fileName = base64_decode($fileName);
3201
3202
    // Load classes
3203
    $cipher = new Crypt_AES();
3204
3205
    // Set the object key
3206
    $cipher->setPassword(base64_decode($key));
3207
3208
    // Prevent against out of memory
3209
    $cipher->enableContinuousBuffer();
3210
    $cipher->disablePadding();
3211
3212
    // Get file content
3213
    $ciphertext = file_get_contents($filePath . '/' . TP_FILE_PREFIX . $fileName);
3214
3215
    // Decrypt file content and return
3216
    return base64_encode($cipher->decrypt($ciphertext));
3217
}
3218
3219
/**
3220
 * Generate a simple password
3221
 *
3222
 * @param integer $length          Length of string
3223
 * @param boolean $symbolsincluded Allow symbols
3224
 *
3225
 * @return string
3226
 */
3227
function generateQuickPassword($length = 16, $symbolsincluded = true)
3228
{
3229
    // Generate new user password
3230
    $small_letters = range('a', 'z');
3231
    $big_letters = range('A', 'Z');
3232
    $digits = range(0, 9);
3233
    $symbols = $symbolsincluded === true ?
3234
        array('#', '_', '-', '@', '$', '+', '&') : array();
3235
3236
    $res = array_merge($small_letters, $big_letters, $digits, $symbols);
3237
    $count = count($res);
3238
    // first variant
3239
3240
    $random_string = '';
3241
    for ($i = 0; $i < $length; ++$i) {
3242
        $random_string .= $res[random_int(0, $count - 1)];
3243
    }
3244
3245
    return $random_string;
3246
}
3247
3248
/**
3249
 * Permit to store the sharekey of an object for users.
3250
 *
3251
 * @param string $object_name             Type for table selection
3252
 * @param int    $post_folder_is_personal Personal
3253
 * @param int    $post_folder_id          Folder
3254
 * @param int    $post_object_id          Object
3255
 * @param string $objectKey               Object key
3256
 * @param array  $SETTINGS                Teampass settings
3257
 *
3258
 * @return void
3259
 */
3260
function storeUsersShareKey(
3261
    $object_name,
3262
    $post_folder_is_personal,
3263
    $post_folder_id,
3264
    $post_object_id,
3265
    $objectKey,
3266
    $SETTINGS
3267
) {
3268
    // include librairies & connect to DB
3269
    include_once $SETTINGS['cpassman_dir'] . '/includes/config/settings.php';
3270
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
3271
    if (defined('DB_PASSWD_CLEAR') === false) {
3272
        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...
3273
    }
3274
    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...
3275
    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...
3276
    DB::$password = DB_PASSWD_CLEAR;
3277
    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...
3278
    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...
3279
    DB::$encoding = DB_ENCODING;
3280
3281
    // Delete existing entries for this object
3282
    DB::delete(
3283
        $object_name,
3284
        'object_id = %i',
3285
        $post_object_id
3286
    );
3287
3288
    // Superglobals
3289
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
3290
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
3291
3292
    // Prepare superGlobal variables
3293
    $sessionPpersonaFolders = $superGlobal->get('personal_folders', 'SESSION');
3294
    $sessionUserId = $superGlobal->get('user_id', 'SESSION');
3295
    $sessionUserPublicKey = $superGlobal->get('public_key', 'SESSION', 'user');
3296
3297
    if (
3298
        (int) $post_folder_is_personal === 1
3299
        && in_array($post_folder_id, $sessionPpersonaFolders) === true
3300
    ) {
3301
        // If this is a personal object
3302
        // Only create the sharekey for user
3303
        DB::insert(
3304
            $object_name,
3305
            array(
3306
                'object_id' => $post_object_id,
3307
                'user_id' => $sessionUserId,
3308
                'share_key' => encryptUserObjectKey($objectKey, $sessionUserPublicKey),
3309
            )
3310
        );
3311
    } else {
3312
        // This is a public object
3313
        // Create sharekey for each user
3314
        $users = DB::query(
3315
            'SELECT id, public_key
3316
            FROM ' . prefixTable('users') . '
3317
            WHERE id NOT IN ("' . OTV_USER_ID . '","' . SSH_USER_ID . '","' . API_USER_ID . '")
3318
            AND public_key != ""'
3319
        );
3320
        foreach ($users as $user) {
3321
            // Insert in DB the new object key for this item by user
3322
            DB::insert(
3323
                $object_name,
3324
                array(
3325
                    'object_id' => $post_object_id,
3326
                    'user_id' => $user['id'],
3327
                    'share_key' => encryptUserObjectKey(
3328
                        $objectKey,
3329
                        $user['public_key']
3330
                    ),
3331
                )
3332
            );
3333
        }
3334
    }
3335
}
3336
3337
/**
3338
 * Is this string base64 encoded?
3339
 *
3340
 * @param string $str Encoded string?
3341
 *
3342
 * @return bool
3343
 */
3344
function isBase64($str)
3345
{
3346
    $str = (string) trim($str);
3347
3348
    if (!isset($str[0])) {
3349
        return false;
3350
    }
3351
3352
    $base64String = (string) base64_decode($str, true);
3353
    if ($base64String && base64_encode($base64String) === $str) {
3354
        return true;
3355
    }
3356
3357
    return false;
3358
}
3359
3360
/**
3361
 * Undocumented function
3362
 *
3363
 * @param string $field Parameter
3364
 *
3365
 * @return string|boolean
3366
 */
3367
function filterString($field)
3368
{
3369
    // Sanitize string
3370
    $field = filter_var(trim($field), FILTER_SANITIZE_STRING);
3371
    if (empty($field) === false) {
3372
        // Load AntiXSS
3373
        include_once '../includes/libraries/protect/AntiXSS/AntiXSS.php';
3374
        $antiXss = new voku\helper\AntiXSS();
3375
        // Return
3376
        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...
3377
    }
3378
3379
    return false;
3380
}
3381
3382
/**
3383
 * Connect to LDAP server
3384
 *
3385
 * @param array $SETTINGS Teampass settings
3386
 *
3387
 * @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...
3388
 */
3389
function ldapConnect($SETTINGS)
3390
{
3391
    // return if ldap not enabled
3392
    if ((int) $SETTINGS['ldap_mode'] === 1) {
3393
        return false;
3394
    }
3395
3396
    if ($SETTINGS['ldap_type'] === 'posix-search') {
3397
        // LDAP with posix search
3398
        $ldapURIs = '';
3399
        foreach (explode(',', $SETTINGS['ldap_domain_controler']) as $domainControler) {
3400
            if ($SETTINGS['ldap_ssl'] == 1) {
3401
                $ldapURIs .= 'ldaps://' . $domainControler . ':' . $SETTINGS['ldap_port'] . ' ';
3402
            } else {
3403
                $ldapURIs .= 'ldap://' . $domainControler . ':' . $SETTINGS['ldap_port'] . ' ';
3404
            }
3405
        }
3406
        // Connect
3407
        $ldapconn = ldap_connect($ldapURIs);
3408
3409
        // Case of LDAP over TLS
3410
        if ($SETTINGS['ldap_tls']) {
3411
            ldap_start_tls($ldapconn);
3412
        }
3413
3414
        // Set options
3415
        ldap_set_option($ldapconn, LDAP_OPT_PROTOCOL_VERSION, 3);
3416
        ldap_set_option($ldapconn, LDAP_OPT_REFERRALS, 0);
3417
3418
        // Is LDAP connection ready?
3419
        if ($ldapconn !== false) {
3420
            // Should we bind the connection?
3421
            if ($SETTINGS['ldap_bind_dn'] !== '' && $SETTINGS['ldap_bind_passwd'] !== '') {
3422
                return ldap_bind($ldapconn, $SETTINGS['ldap_bind_dn'], $SETTINGS['ldap_bind_passwd']);
3423
            }
3424
        }
3425
3426
        return false;
3427
    } else { }
3428
}
3429
3430
/**
3431
 * CHeck if provided credentials are allowed on server
3432
 *
3433
 * @param string $login    User Login
3434
 * @param string $password User Pwd
3435
 * @param array $SETTINGS  Teampass settings
3436
 *
3437
 * @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...
3438
 */
3439
function ldapCheckUserPassword($login, $password, $SETTINGS)
3440
{
3441
    $ldapconn = ldapConnect($SETTINGS);
3442
    if ($ldapconn) {
3443
        if ($SETTINGS['ldap_type'] === 'posix-search') {
3444
            // LDAP with posix search
3445
            $filter = '(&(' . $SETTINGS['ldap_user_attribute'] . '=' . $login . ')(objectClass=' . $SETTINGS['ldap_object_class'] . '))';
3446
            $result = ldap_search(
3447
                $ldapconn,
0 ignored issues
show
Bug introduced by
$ldapconn of type true is incompatible with the type resource expected by parameter $link_identifier of ldap_search(). ( Ignorable by Annotation )

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

3447
                /** @scrutinizer ignore-type */ $ldapconn,
Loading history...
3448
                $SETTINGS['ldap_search_base'],
3449
                $filter,
3450
                array('dn', 'mail', 'givenname', 'sn', 'samaccountname', 'shadowexpire', 'memberof')
3451
            );
3452
3453
            // Check if user was found in AD
3454
            if (ldap_count_entries($ldapconn, $result) > 0) {
0 ignored issues
show
Bug introduced by
$ldapconn of type true is incompatible with the type resource expected by parameter $link_identifier of ldap_count_entries(). ( Ignorable by Annotation )

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

3454
            if (ldap_count_entries(/** @scrutinizer ignore-type */ $ldapconn, $result) > 0) {
Loading history...
3455
                // Get user's info and especially the DN
3456
                $userLDAPInfo = ldap_get_entries($ldapconn, $result);
0 ignored issues
show
Bug introduced by
$ldapconn of type true is incompatible with the type resource expected by parameter $link_identifier of ldap_get_entries(). ( Ignorable by Annotation )

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

3456
                $userLDAPInfo = ldap_get_entries(/** @scrutinizer ignore-type */ $ldapconn, $result);
Loading history...
3457
3458
                // Try to auth inside LDAP
3459
                if (ldap_bind($ldapconn, $userLDAPInfo[0]['dn'], $password) === true) {
0 ignored issues
show
Bug introduced by
$ldapconn of type true is incompatible with the type resource expected by parameter $link_identifier of ldap_bind(). ( Ignorable by Annotation )

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

3459
                if (ldap_bind(/** @scrutinizer ignore-type */ $ldapconn, $userLDAPInfo[0]['dn'], $password) === true) {
Loading history...
3460
                    return true;
3461
                }
3462
            }
3463
        } else {
3464
            $adldap = new SplClassLoader('adLDAP', $SETTINGS['cpassman_dir'] . '/includes/libraries/LDAP');
3465
            $adldap->register();
3466
            $ldap_suffix = '';
3467
3468
            // Posix style LDAP handles user searches a bit differently
3469
            if ($SETTINGS['ldap_type'] === 'posix') {
3470
                $ldap_suffix = ',' . $SETTINGS['ldap_suffix'] . ',' . $SETTINGS['ldap_domain_dn'];
3471
            } elseif ($SETTINGS['ldap_type'] === 'windows') {
3472
                //Multiple Domain Names
3473
                $ldap_suffix = $SETTINGS['ldap_suffix'];
3474
            }
3475
3476
            // Ensure no double commas exist in ldap_suffix
3477
            $ldap_suffix = str_replace(',,', ',', $ldap_suffix);
3478
3479
            // Create LDAP connection
3480
            $adldap = new adLDAP\adLDAP(
3481
                array(
3482
                    'base_dn' => $SETTINGS['ldap_domain_dn'],
3483
                    'account_suffix' => $ldap_suffix,
3484
                    'domain_controllers' => explode(',', $SETTINGS['ldap_domain_controler']),
3485
                    'ad_port' => $SETTINGS['ldap_port'],
3486
                    'use_ssl' => $SETTINGS['ldap_ssl'],
3487
                    'use_tls' => $SETTINGS['ldap_tls'],
3488
                )
3489
            );
3490
3491
            // OpenLDAP expects an attribute=value pair
3492
            if ($SETTINGS['ldap_type'] === 'posix') {
3493
                $login = $SETTINGS['ldap_user_attribute'] . '=' . $login;
3494
            }
3495
3496
            // Authenticate the user
3497
            if ($adldap->authenticate($login, html_entity_decode($password))) {
3498
                return true;
3499
            }
3500
        }
3501
    }
3502
    return false;
3503
}
3504