Passed
Push — teampass_3.0 ( 964531...a43472 )
by Nils
04:14
created

urlGetVerification()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 7
c 1
b 0
f 0
nc 4
nop 2
dl 0
loc 12
rs 9.6111
1
<?php
2
/**
3
 * Teampass - a collaborative passwords manager.
4
 * ---
5
 * This library is distributed in the hope that it will be useful,
6
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
7
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
8
 * ---
9
 * @project   Teampass
10
 * @file      main.functions.php
11
 * ---
12
 * @author    Nils Laumaillé ([email protected])
13
 * @copyright 2009-2019 Teampass.net
14
 * @license   https://spdx.org/licenses/GPL-3.0-only.html#licenseText GPL-3.0
15
 * ---
16
 * @see       https://www.teampass.net
17
 */
18
19
20
if (isset($_SESSION['CPM']) === false || (int)$_SESSION['CPM'] !== 1) {
21
    die('Hacking attempt...');
22
}
23
24
// Load config if $SETTINGS not defined
25
if (!isset($SETTINGS['cpassman_dir']) || empty($SETTINGS['cpassman_dir'])) {
26
    if (file_exists('../includes/config/tp.config.php')) {
27
        include_once '../includes/config/tp.config.php';
28
    } elseif (file_exists('./includes/config/tp.config.php')) {
29
        include_once './includes/config/tp.config.php';
30
    } elseif (file_exists('../../includes/config/tp.config.php')) {
31
        include_once '../../includes/config/tp.config.php';
32
    } else {
33
        throw new Exception("Error file '/includes/config/tp.config.php' not exists", 1);
34
    }
35
}
36
37
header('Content-type: text/html; charset=utf-8');
38
header('Cache-Control: no-cache, must-revalidate');
39
40
/**
41
 * Convert language code to string.
42
 *
43
 * @param string $string String to get
44
 *
45
 * @return string
46
 */
47
function langHdl($string)
48
{
49
    // Clean the string to convert
50
    $string = trim($string);
51
52
    if (empty($string) === true || isset($_SESSION['teampass']['lang'][$string]) === false) {
53
        // Manage error
54
        return 'ERROR in language strings!';
55
    } else {
56
        return str_replace(
57
            array('"', "'"),
58
            array('&quot;', '&apos;'),
59
            $_SESSION['teampass']['lang'][$string]
60
        );
61
    }
62
}
63
64
//Generate N# of random bits for use as salt
65
/**
66
 * Undocumented function.
67
 *
68
 * @param int $size Length
69
 *
70
 * @return array
71
 */
72
function getBits($size)
73
{
74
    $str = '';
75
    $var_x = $size + 10;
76
    for ($var_i = 0; $var_i < $var_x; ++$var_i) {
77
        $str .= base_convert(mt_rand(1, 36), 10, 36);
78
    }
79
80
    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...
81
}
82
83
//generate pbkdf2 compliant hash
84
function strHashPbkdf2($var_p, $var_s, $var_c, $var_kl, $var_a = 'sha256', $var_st = 0)
85
{
86
    $var_kb = $var_st + $var_kl; // Key blocks to compute
87
    $var_dk = ''; // Derived key
88
89
    for ($block = 1; $block <= $var_kb; ++$block) { // Create key
90
        $var_ib = $var_h = hash_hmac($var_a, $var_s . pack('N', $block), $var_p, true); // Initial hash for this block
91
        for ($var_i = 1; $var_i < $var_c; ++$var_i) { // Perform block iterations
92
            $var_ib ^= ($var_h = hash_hmac($var_a, $var_h, $var_p, true)); // XOR each iterate
93
        }
94
        $var_dk .= $var_ib; // Append iterated block
95
    }
96
97
    return substr($var_dk, $var_st, $var_kl); // Return derived key of correct length
98
}
99
100
/**
101
 * stringUtf8Decode().
102
 *
103
 * utf8_decode
104
 */
105
function stringUtf8Decode($string)
106
{
107
    return str_replace(' ', '+', utf8_decode($string));
108
}
109
110
/**
111
 * encryptOld().
112
 *
113
 * crypt a string
114
 *
115
 * @param string $text
116
 */
117
function encryptOld($text, $personalSalt = '')
118
{
119
    if (empty($personalSalt) === false) {
120
        return trim(
121
            base64_encode(
122
                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

122
                /** @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...
123
                    MCRYPT_RIJNDAEL_256,
124
                    $personalSalt,
125
                    $text,
126
                    MCRYPT_MODE_ECB,
127
                    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

127
                    /** @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...
128
                        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

128
                        /** @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...
129
                        MCRYPT_RAND
130
                    )
131
                )
132
            )
133
        );
134
    }
135
136
    // If $personalSalt is not empty
137
    return trim(
138
        base64_encode(
139
            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

139
            /** @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...
140
                MCRYPT_RIJNDAEL_256,
141
                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...
142
                $text,
143
                MCRYPT_MODE_ECB,
144
                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

144
                /** @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...
145
                    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

145
                    /** @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...
146
                    MCRYPT_RAND
147
                )
148
            )
149
        )
150
    );
151
}
152
153
/**
154
 * decryptOld().
155
 *
156
 * decrypt a crypted string
157
 */
158
function decryptOld($text, $personalSalt = '')
159
{
160
    if (!empty($personalSalt)) {
161
        return trim(
162
            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

162
            /** @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...
163
                MCRYPT_RIJNDAEL_256,
164
                $personalSalt,
165
                base64_decode($text),
166
                MCRYPT_MODE_ECB,
167
                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

167
                /** @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...
168
                    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

168
                    /** @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...
169
                    MCRYPT_RAND
170
                )
171
            )
172
        );
173
    }
174
175
    // No personal SK
176
    return trim(
177
        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

177
        /** @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...
178
            MCRYPT_RIJNDAEL_256,
179
            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...
180
            base64_decode($text),
181
            MCRYPT_MODE_ECB,
182
            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

182
            /** @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...
183
                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

183
                /** @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...
184
                MCRYPT_RAND
185
            )
186
        )
187
    );
188
}
189
190
/**
191
 * encrypt().
192
 *
193
 * crypt a string
194
 *
195
 * @param string $decrypted
196
 */
197
function encrypt($decrypted, $personalSalt = '')
198
{
199
    global $SETTINGS;
200
201
    if (!isset($SETTINGS['cpassman_dir']) || empty($SETTINGS['cpassman_dir'])) {
202
        require_once '../includes/libraries/Encryption/PBKDF2/PasswordHash.php';
203
    } else {
204
        require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Encryption/PBKDF2/PasswordHash.php';
205
    }
206
207
    if (!empty($personalSalt)) {
208
        $staticSalt = $personalSalt;
209
    } else {
210
        $staticSalt = 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...
211
    }
212
213
    //set our salt to a variable
214
    // Get 64 random bits for the salt for pbkdf2
215
    $pbkdf2Salt = getBits(64);
216
    // generate a pbkdf2 key to use for the encryption.
217
    $key = substr(pbkdf2('sha256', $staticSalt, $pbkdf2Salt, ITCOUNT, 16 + 32, true), 32, 16);
218
    // Build $init_vect and $ivBase64.  We use a block size of 256 bits (AES compliant)
219
    // and CTR mode.  (Note: ECB mode is inadequate as IV is not used.)
220
    $init_vect = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, 'ctr'), MCRYPT_RAND);
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

220
    $init_vect = /** @scrutinizer ignore-deprecated */ mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, 'ctr'), MCRYPT_RAND);

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...
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

220
    $init_vect = mcrypt_create_iv(/** @scrutinizer ignore-deprecated */ mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, 'ctr'), MCRYPT_RAND);

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...
221
222
    //base64 trim
223
    if (strlen($ivBase64 = rtrim(base64_encode($init_vect), '=')) != 43) {
224
        return false;
225
    }
226
    // Encrypt $decrypted
227
    $encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $decrypted, 'ctr', $init_vect);
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

227
    $encrypted = /** @scrutinizer ignore-deprecated */ mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $decrypted, 'ctr', $init_vect);

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...
228
    // MAC the encrypted text
229
    $mac = hash_hmac('sha256', $encrypted, $staticSalt);
230
    // We're done!
231
    return base64_encode($ivBase64 . $encrypted . $mac . $pbkdf2Salt);
0 ignored issues
show
Bug introduced by
Are you sure $pbkdf2Salt of type array can be used in concatenation? ( Ignorable by Annotation )

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

231
    return base64_encode($ivBase64 . $encrypted . $mac . /** @scrutinizer ignore-type */ $pbkdf2Salt);
Loading history...
232
}
233
234
/**
235
 * decrypt().
236
 *
237
 * decrypt a crypted string
238
 */
239
function decrypt($encrypted, $personalSalt = '')
240
{
241
    global $SETTINGS;
242
243
    if (!isset($SETTINGS['cpassman_dir']) || empty($SETTINGS['cpassman_dir'])) {
244
        include_once '../includes/libraries/Encryption/PBKDF2/PasswordHash.php';
245
    } else {
246
        include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Encryption/PBKDF2/PasswordHash.php';
247
    }
248
249
    if (!empty($personalSalt)) {
250
        $staticSalt = $personalSalt;
251
    } else {
252
        $staticSalt = 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...
253
    }
254
    //base64 decode the entire payload
255
    $encrypted = base64_decode($encrypted);
256
    // get the salt
257
    $pbkdf2Salt = substr($encrypted, -64);
258
    //remove the salt from the string
259
    $encrypted = substr($encrypted, 0, -64);
260
    $key = substr(pbkdf2('sha256', $staticSalt, $pbkdf2Salt, ITCOUNT, 16 + 32, true), 32, 16);
261
    // Retrieve $init_vect which is the first 22 characters plus ==, base64_decoded.
262
    $init_vect = base64_decode(substr($encrypted, 0, 43) . '==');
263
    // Remove $init_vect from $encrypted.
264
    $encrypted = substr($encrypted, 43);
265
    // Retrieve $mac which is the last 64 characters of $encrypted.
266
    $mac = substr($encrypted, -64);
267
    // Remove the last 64 chars from encrypted (remove MAC)
268
    $encrypted = substr($encrypted, 0, -64);
269
    //verify the sha256hmac from the encrypted data before even trying to decrypt it
270
    if (hash_hmac('sha256', $encrypted, $staticSalt) != $mac) {
271
        return false;
272
    }
273
    // Decrypt the data.
274
    $decrypted = rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $encrypted, 'ctr', $init_vect), "\0\4");
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

274
    $decrypted = rtrim(/** @scrutinizer ignore-deprecated */ mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $encrypted, 'ctr', $init_vect), "\0\4");

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...
275
    // Yay!
276
    return $decrypted;
277
}
278
279
/**
280
 * genHash().
281
 *
282
 * Generate a hash for user login
283
 *
284
 * @param string $password
285
 */
286
function bCrypt($password, $cost)
287
{
288
    $salt = sprintf('$2y$%02d$', $cost);
289
    if (function_exists('openssl_random_pseudo_bytes')) {
290
        $salt .= bin2hex(openssl_random_pseudo_bytes(11));
291
    } else {
292
        $chars = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
293
        for ($i = 0; $i < 22; ++$i) {
294
            $salt .= $chars[mt_rand(0, 63)];
295
        }
296
    }
297
298
    return crypt($password, $salt);
299
}
300
301
function testHex2Bin($val)
302
{
303
    if (!@hex2bin($val)) {
304
        throw new Exception('ERROR');
305
    }
306
307
    return hex2bin($val);
308
}
309
310
/**
311
 * Defuse cryption function.
312
 *
313
 * @param string $message   what to de/crypt
314
 * @param string $ascii_key key to use
315
 * @param string $type      operation to perform
316
 * @param array  $SETTINGS  Teampass settings
317
 *
318
 * @return array
319
 */
320
function cryption($message, $ascii_key, $type, $SETTINGS)
321
{
322
    // load PhpEncryption library
323
    if (isset($SETTINGS['cpassman_dir']) === false || empty($SETTINGS['cpassman_dir']) === true) {
324
        $path = '../includes/libraries/Encryption/Encryption/';
325
    } else {
326
        $path = $SETTINGS['cpassman_dir'] . '/includes/libraries/Encryption/Encryption/';
327
    }
328
329
    include_once $path . 'Crypto.php';
330
    include_once $path . 'Encoding.php';
331
    include_once $path . 'DerivedKeys.php';
332
    include_once $path . 'Key.php';
333
    include_once $path . 'KeyOrPassword.php';
334
    include_once $path . 'File.php';
335
    include_once $path . 'RuntimeTests.php';
336
    include_once $path . 'KeyProtectedByPassword.php';
337
    include_once $path . 'Core.php';
338
339
    // init
340
    $err = '';
341
    if (empty($ascii_key) === true) {
342
        $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...
343
    }
344
345
    //echo $path.' -- '.$message.' ;; '.$ascii_key.' --- ';
346
    // convert KEY
347
    $key = \Defuse\Crypto\Key::loadFromAsciiSafeString($ascii_key);
348
349
    try {
350
        if ($type === 'encrypt') {
351
            $text = \Defuse\Crypto\Crypto::encrypt($message, $key);
352
        } elseif ($type === 'decrypt') {
353
            $text = \Defuse\Crypto\Crypto::decrypt($message, $key);
354
        }
355
    } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
356
        $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.';
357
    } catch (Defuse\Crypto\Exception\BadFormatException $ex) {
358
        $err = $ex;
359
    } catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) {
360
        $err = $ex;
361
    } catch (Defuse\Crypto\Exception\CryptoException $ex) {
362
        $err = $ex;
363
    } catch (Defuse\Crypto\Exception\IOException $ex) {
364
        $err = $ex;
365
    }
366
    //echo \Defuse\Crypto\Crypto::decrypt($message, $key).' ## ';
367
368
    return array(
369
        'string' => isset($text) ? $text : '',
370
        'error' => $err,
371
    );
372
}
373
374
/**
375
 * Generating a defuse key.
376
 *
377
 * @return string
378
 */
379
function defuse_generate_key()
380
{
381
    // load PhpEncryption library
382
    if (file_exists('../includes/config/tp.config.php') === true) {
383
        $path = '../includes/libraries/Encryption/Encryption/';
384
    } elseif (file_exists('./includes/config/tp.config.php') === true) {
385
        $path = './includes/libraries/Encryption/Encryption/';
386
    } else {
387
        $path = '../includes/libraries/Encryption/Encryption/';
388
    }
389
390
    include_once $path . 'Crypto.php';
391
    include_once $path . 'Encoding.php';
392
    include_once $path . 'DerivedKeys.php';
393
    include_once $path . 'Key.php';
394
    include_once $path . 'KeyOrPassword.php';
395
    include_once $path . 'File.php';
396
    include_once $path . 'RuntimeTests.php';
397
    include_once $path . 'KeyProtectedByPassword.php';
398
    include_once $path . 'Core.php';
399
400
    $key = \Defuse\Crypto\Key::createNewRandomKey();
401
    $key = $key->saveToAsciiSafeString();
402
403
    return $key;
404
}
405
406
/**
407
 * Generate a Defuse personal key.
408
 *
409
 * @param string $psk psk used
410
 *
411
 * @return string
412
 */
413
function defuse_generate_personal_key($psk)
414
{
415
    // load PhpEncryption library
416
    if (file_exists('../includes/config/tp.config.php') === true) {
417
        $path = '../includes/libraries/Encryption/Encryption/';
418
    } elseif (file_exists('./includes/config/tp.config.php') === true) {
419
        $path = './includes/libraries/Encryption/Encryption/';
420
    } else {
421
        $path = '../includes/libraries/Encryption/Encryption/';
422
    }
423
424
    include_once $path . 'Crypto.php';
425
    include_once $path . 'Encoding.php';
426
    include_once $path . 'DerivedKeys.php';
427
    include_once $path . 'Key.php';
428
    include_once $path . 'KeyOrPassword.php';
429
    include_once $path . 'File.php';
430
    include_once $path . 'RuntimeTests.php';
431
    include_once $path . 'KeyProtectedByPassword.php';
432
    include_once $path . 'Core.php';
433
434
    $protected_key = \Defuse\Crypto\KeyProtectedByPassword::createRandomPasswordProtectedKey($psk);
435
    $protected_key_encoded = $protected_key->saveToAsciiSafeString();
436
437
    return $protected_key_encoded; // save this in user table
438
}
439
440
/**
441
 * Validate persoanl key with defuse.
442
 *
443
 * @param string $psk                   the user's psk
444
 * @param string $protected_key_encoded special key
445
 *
446
 * @return string
447
 */
448
function defuse_validate_personal_key($psk, $protected_key_encoded)
449
{
450
    // load PhpEncryption library
451
    if (file_exists('../includes/config/tp.config.php') === true) {
452
        $path = '../includes/libraries/Encryption/Encryption/';
453
    } elseif (file_exists('./includes/config/tp.config.php') === true) {
454
        $path = './includes/libraries/Encryption/Encryption/';
455
    } else {
456
        $path = '../includes/libraries/Encryption/Encryption/';
457
    }
458
459
    include_once $path . 'Crypto.php';
460
    include_once $path . 'Encoding.php';
461
    include_once $path . 'DerivedKeys.php';
462
    include_once $path . 'Key.php';
463
    include_once $path . 'KeyOrPassword.php';
464
    include_once $path . 'File.php';
465
    include_once $path . 'RuntimeTests.php';
466
    include_once $path . 'KeyProtectedByPassword.php';
467
    include_once $path . 'Core.php';
468
469
    try {
470
        $protected_key = \Defuse\Crypto\KeyProtectedByPassword::loadFromAsciiSafeString($protected_key_encoded);
471
        $user_key = $protected_key->unlockKey($psk);
472
        $user_key_encoded = $user_key->saveToAsciiSafeString();
473
    } catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) {
474
        return 'Error - Major issue as the encryption is broken.';
475
    } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
476
        return 'Error - The saltkey is not the correct one.';
477
    }
478
479
    return $user_key_encoded; // store it in session once user has entered his psk
480
}
481
482
/**
483
 * Decrypt a defuse string if encrypted.
484
 *
485
 * @param string $value Encrypted string
486
 *
487
 * @return string Decrypted string
488
 */
489
function defuseReturnDecrypted($value, $SETTINGS)
490
{
491
    if (substr($value, 0, 3) === 'def') {
492
        $value = cryption($value, '', 'decrypt', $SETTINGS)['string'];
493
    }
494
495
    return $value;
496
}
497
498
/**
499
 * Trims a string depending on a specific string.
500
 *
501
 * @param string|array $chaine  what to trim
502
 * @param string       $element trim on what
503
 *
504
 * @return string
505
 */
506
function trimElement($chaine, $element)
507
{
508
    if (!empty($chaine)) {
509
        if (is_array($chaine) === true) {
510
            $chaine = implode(';', $chaine);
511
        }
512
        $chaine = trim($chaine);
513
        if (substr($chaine, 0, 1) === $element) {
514
            $chaine = substr($chaine, 1);
515
        }
516
        if (substr($chaine, strlen($chaine) - 1, 1) === $element) {
517
            $chaine = substr($chaine, 0, strlen($chaine) - 1);
518
        }
519
    }
520
521
    return $chaine;
522
}
523
524
/**
525
 * Permits to suppress all "special" characters from string.
526
 *
527
 * @param string $string  what to clean
528
 * @param bool   $special use of special chars?
529
 *
530
 * @return string
531
 */
532
function cleanString($string, $special = false)
533
{
534
    // Create temporary table for special characters escape
535
    $tabSpecialChar = array();
536
    for ($i = 0; $i <= 31; ++$i) {
537
        $tabSpecialChar[] = chr($i);
538
    }
539
    array_push($tabSpecialChar, '<br />');
540
    if ((int) $special === 1) {
541
        $tabSpecialChar = array_merge($tabSpecialChar, array('</li>', '<ul>', '<ol>'));
542
    }
543
544
    return str_replace($tabSpecialChar, "\n", $string);
545
}
546
547
/**
548
 * Erro manager for DB.
549
 *
550
 * @param array $params output from query
551
 */
552
function db_error_handler($params)
553
{
554
    echo 'Error: ' . $params['error'] . "<br>\n";
555
    echo 'Query: ' . $params['query'] . "<br>\n";
556
    throw new Exception('Error - Query', 1);
557
}
558
559
/**
560
 * [identifyUserRights description].
561
 *
562
 * @param string $groupesVisiblesUser  [description]
563
 * @param string $groupesInterditsUser [description]
564
 * @param string $isAdmin              [description]
565
 * @param string $idFonctions          [description]
566
 *
567
 * @return string [description]
568
 */
569
function identifyUserRights(
570
    $groupesVisiblesUser,
571
    $groupesInterditsUser,
572
    $isAdmin,
573
    $idFonctions,
574
    $SETTINGS
575
) {
576
    //load ClassLoader
577
    include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php';
578
579
    //Connect to DB
580
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
581
    if (defined('DB_PASSWD_CLEAR') === false) {
582
        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...
583
    }
584
    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...
585
    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...
586
    DB::$password = DB_PASSWD_CLEAR;
587
    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...
588
    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...
589
    DB::$encoding = DB_ENCODING;
590
591
    //Build tree
592
    $tree = new SplClassLoader('Tree\NestedTree', $SETTINGS['cpassman_dir'] . '/includes/libraries');
593
    $tree->register();
594
    $tree = new Tree\NestedTree\NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
595
596
    // Check if user is ADMINISTRATOR
597
    if ((int) $isAdmin === 1) {
598
        identAdmin(
599
            $idFonctions,
600
            $SETTINGS,
601
            $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

601
            /** @scrutinizer ignore-type */ $tree
Loading history...
602
        );
603
    } else {
604
        identUser(
605
            $groupesVisiblesUser,
606
            $groupesInterditsUser,
607
            $idFonctions,
608
            $SETTINGS,
609
            $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

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

1633
                /** @scrutinizer ignore-type */ $data,
Loading history...
1634
                true
1635
            );
1636
        } else {
1637
            return json_decode(
1638
                Encryption\Crypt\aesctr::decrypt(
1639
                    $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

1639
                    /** @scrutinizer ignore-type */ $data,
Loading history...
1640
                    $_SESSION['key'],
1641
                    256
1642
                ),
1643
                true
1644
            );
1645
        }
1646
    }
1647
}
1648
1649
/**
1650
 * Create a thumbnail.
1651
 *
1652
 * @param string $src           Source
1653
 * @param string $dest          Destination
1654
 * @param float  $desired_width Size of width
1655
 */
1656
function makeThumbnail($src, $dest, $desired_width)
1657
{
1658
    /* read the source image */
1659
    $source_image = imagecreatefrompng($src);
1660
    $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

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

1661
    $height = imagesy(/** @scrutinizer ignore-type */ $source_image);
Loading history...
1662
1663
    /* find the "desired height" of this thumbnail, relative to the desired width  */
1664
    $desired_height = floor($height * ($desired_width / $width));
1665
1666
    /* create a new, "virtual" image */
1667
    $virtual_image = imagecreatetruecolor($desired_width, $desired_height);
0 ignored issues
show
Bug introduced by
$desired_width of type double is incompatible with the type integer expected by parameter $width of imagecreatetruecolor(). ( Ignorable by Annotation )

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

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

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

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

1670
    imagecopyresampled($virtual_image, $source_image, 0, 0, 0, 0, $desired_width, /** @scrutinizer ignore-type */ $desired_height, $width, $height);
Loading history...
Bug introduced by
It seems like $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

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

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

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

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

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

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

2287
                /** @scrutinizer ignore-type */ \Defuse\Crypto\Key::loadFromAsciiSafeString($ascii_key)
Loading history...
2288
            );
2289
            // ---
2290
        } elseif ($type === 'encrypt') {
2291
            // Encrypt file
2292
            $err = defuseFileEncrypt(
2293
                $source_file,
2294
                $target_file,
2295
                $SETTINGS,
2296
                \Defuse\Crypto\Key::loadFromAsciiSafeString($ascii_key)
0 ignored issues
show
Bug introduced by
Defuse\Crypto\Key::loadF...iSafeString($ascii_key) of type Defuse\Crypto\Key is incompatible with the type string expected by parameter $password of defuseFileEncrypt(). ( Ignorable by Annotation )

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

2296
                /** @scrutinizer ignore-type */ \Defuse\Crypto\Key::loadFromAsciiSafeString($ascii_key)
Loading history...
2297
            );
2298
        }
2299
    } else {
2300
        /*
2301
        File encryption/decryption is done with special password and not the SALTKEY
2302
         */
2303
2304
        $err = '';
2305
        if ($type === 'decrypt') {
2306
            // Decrypt file
2307
            $err = defuseFileDecrypt(
2308
                $source_file,
2309
                $target_file,
2310
                $SETTINGS,
2311
                $password
2312
            );
2313
            // ---
2314
        } elseif ($type === 'encrypt') {
2315
            // Encrypt file
2316
            $err = defuseFileEncrypt(
2317
                $source_file,
2318
                $target_file,
2319
                $SETTINGS,
2320
                $password
2321
            );
2322
        }
2323
    }
2324
2325
    // return error
2326
    return empty($err) === false ? $err : true;
2327
}
2328
2329
/**
2330
 * Encrypt a file with Defuse.
2331
 *
2332
 * @param string $source_file path to source file
2333
 * @param string $target_file path to target file
2334
 * @param array  $SETTINGS    Settings
2335
 * @param string $password    A password
2336
 *
2337
 * @return string|bool
2338
 */
2339
function defuseFileEncrypt(
2340
    $source_file,
2341
    $target_file,
2342
    $SETTINGS,
2343
    $password = null
2344
) {
2345
    // load PhpEncryption library
2346
    $path_to_encryption = '/includes/libraries/Encryption/Encryption/';
2347
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Crypto.php';
2348
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Encoding.php';
2349
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'DerivedKeys.php';
2350
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Key.php';
2351
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'KeyOrPassword.php';
2352
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'File.php';
2353
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'RuntimeTests.php';
2354
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'KeyProtectedByPassword.php';
2355
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Core.php';
2356
2357
    try {
2358
        \Defuse\Crypto\File::encryptFileWithPassword(
2359
            $source_file,
2360
            $target_file,
2361
            $password
2362
        );
2363
    } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
2364
        $err = 'wrong_key';
2365
    } catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) {
2366
        $err = $ex;
2367
    } catch (Defuse\Crypto\Exception\IOException $ex) {
2368
        $err = $ex;
2369
    }
2370
2371
    // return error
2372
    return empty($err) === false ? $err : true;
2373
}
2374
2375
/**
2376
 * Decrypt a file with Defuse.
2377
 *
2378
 * @param string $source_file path to source file
2379
 * @param string $target_file path to target file
2380
 * @param array  $SETTINGS    Settings
2381
 * @param string $password    A password
2382
 *
2383
 * @return string|bool
2384
 */
2385
function defuseFileDecrypt(
2386
    $source_file,
2387
    $target_file,
2388
    $SETTINGS,
2389
    $password = null
2390
) {
2391
    // load PhpEncryption library
2392
    $path_to_encryption = '/includes/libraries/Encryption/Encryption/';
2393
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Crypto.php';
2394
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Encoding.php';
2395
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'DerivedKeys.php';
2396
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Key.php';
2397
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'KeyOrPassword.php';
2398
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'File.php';
2399
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'RuntimeTests.php';
2400
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'KeyProtectedByPassword.php';
2401
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Core.php';
2402
2403
    try {
2404
        \Defuse\Crypto\File::decryptFileWithPassword(
2405
            $source_file,
2406
            $target_file,
2407
            $password
2408
        );
2409
    } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
2410
        $err = 'wrong_key';
2411
    } catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) {
2412
        $err = $ex;
2413
    } catch (Defuse\Crypto\Exception\IOException $ex) {
2414
        $err = $ex;
2415
    }
2416
2417
    // return error
2418
    return empty($err) === false ? $err : true;
2419
}
2420
2421
/*
2422
* NOT TO BE USED
2423
*/
2424
/**
2425
 * Undocumented function.
2426
 *
2427
 * @param string $text Text to debug
2428
 */
2429
function debugTeampass($text)
2430
{
2431
    $debugFile = fopen('D:/wamp64/www/TeamPass/debug.txt', 'r+');
2432
    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

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

2433
    fclose(/** @scrutinizer ignore-type */ $debugFile);
Loading history...
2434
}
2435
2436
/**
2437
 * DELETE the file with expected command depending on server type.
2438
 *
2439
 * @param string $file     Path to file
2440
 * @param array  $SETTINGS Teampass settings
2441
 */
2442
function fileDelete($file, $SETTINGS)
2443
{
2444
    // Load AntiXSS
2445
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/AntiXSS/AntiXSS.php';
2446
    $antiXss = new voku\helper\AntiXSS();
2447
2448
    $file = $antiXss->xss_clean($file);
2449
    if (is_file($file)) {
2450
        unlink($file);
2451
    }
2452
}
2453
2454
/**
2455
 * Permits to extract the file extension.
2456
 *
2457
 * @param string $file File name
2458
 *
2459
 * @return string
2460
 */
2461
function getFileExtension($file)
2462
{
2463
    if (strpos($file, '.') === false) {
2464
        return $file;
2465
    }
2466
2467
    return substr($file, strrpos($file, '.') + 1);
2468
}
2469
2470
/**
2471
 * Performs chmod operation on subfolders.
2472
 *
2473
 * @param string $dir             Parent folder
2474
 * @param int    $dirPermissions  New permission on folders
2475
 * @param int    $filePermissions New permission on files
2476
 *
2477
 * @return bool
2478
 */
2479
function chmodRecursive($dir, $dirPermissions, $filePermissions)
2480
{
2481
    $pointer_dir = opendir($dir);
2482
    $res = true;
2483
    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

2483
    while (false !== ($file = readdir(/** @scrutinizer ignore-type */ $pointer_dir))) {
Loading history...
2484
        if (($file === '.') || ($file === '..')) {
2485
            continue;
2486
        }
2487
2488
        $fullPath = $dir . '/' . $file;
2489
2490
        if (is_dir($fullPath)) {
2491
            if ($res = @chmod($fullPath, $dirPermissions)) {
2492
                $res = @chmodRecursive($fullPath, $dirPermissions, $filePermissions);
2493
            }
2494
        } else {
2495
            $res = chmod($fullPath, $filePermissions);
2496
        }
2497
        if (!$res) {
2498
            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

2498
            closedir(/** @scrutinizer ignore-type */ $pointer_dir);
Loading history...
2499
2500
            return false;
2501
        }
2502
    }
2503
    closedir($pointer_dir);
2504
    if (is_dir($dir) && $res) {
2505
        $res = @chmod($dir, $dirPermissions);
2506
    }
2507
2508
    return $res;
2509
}
2510
2511
/**
2512
 * Check if user can access to this item.
2513
 *
2514
 * @param int $item_id ID of item
2515
 */
2516
function accessToItemIsGranted($item_id)
2517
{
2518
    global $SETTINGS;
2519
2520
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
2521
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
2522
2523
    // Prepare superGlobal variables
2524
    $session_groupes_visibles = $superGlobal->get('groupes_visibles', 'SESSION');
2525
    $session_list_restricted_folders_for_items = $superGlobal->get('list_restricted_folders_for_items', 'SESSION');
2526
2527
    // Load item data
2528
    $data = DB::queryFirstRow(
2529
        'SELECT id_tree
2530
        FROM ' . prefixTable('items') . '
2531
        WHERE id = %i',
2532
        $item_id
2533
    );
2534
2535
    // Check if user can access this folder
2536
    if (in_array($data['id_tree'], $session_groupes_visibles) === false) {
2537
        // Now check if this folder is restricted to user
2538
        if (
2539
            isset($session_list_restricted_folders_for_items[$data['id_tree']])
2540
            && !in_array($item_id, $session_list_restricted_folders_for_items[$data['id_tree']])
2541
        ) {
2542
            return 'ERR_FOLDER_NOT_ALLOWED';
2543
        } else {
2544
            return 'ERR_FOLDER_NOT_ALLOWED';
2545
        }
2546
    }
2547
2548
    return true;
2549
}
2550
2551
/**
2552
 * Creates a unique key.
2553
 *
2554
 * @param float $lenght Key lenght
2555
 *
2556
 * @return string
2557
 */
2558
function uniqidReal($lenght = 13)
2559
{
2560
    // uniqid gives 13 chars, but you could adjust it to your needs.
2561
    if (function_exists('random_bytes')) {
2562
        $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

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

2564
        $bytes = openssl_random_pseudo_bytes(/** @scrutinizer ignore-type */ ceil($lenght / 2));
Loading history...
2565
    } else {
2566
        throw new Exception('no cryptographically secure random function available');
2567
    }
2568
2569
    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

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

1 path for user data to reach this point

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

General Strategies to prevent injection

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

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

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

$sanitized = (integer) $tainted;
Loading history...
3133
    unlink($fileInPath . '/' . $fileInName);
3134
3135
    return array(
3136
        'fileHash' => base64_encode($hash),
3137
        'objectKey' => base64_encode($objectKey),
3138
    );
3139
}
3140
3141
/**
3142
 * Decrypt a file.
3143
 *
3144
 * @param string $fileName File name
3145
 * @param string $filePath Path to file
3146
 * @param string $key      Key to use
3147
 *
3148
 * @return string
3149
 */
3150
function decryptFile($fileName, $filePath, $key)
3151
{
3152
    if (!defined('FILE_BUFFER_SIZE')) {
3153
        define('FILE_BUFFER_SIZE', 128 * 1024);
3154
    }
3155
3156
    // Includes
3157
    include_once '../includes/config/include.php';
3158
    include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php';
3159
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php';
3160
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
3161
3162
    // Get file name
3163
    $fileName = base64_decode($fileName);
3164
3165
    // Load classes
3166
    $cipher = new Crypt_AES();
3167
3168
    // Set the object key
3169
    $cipher->setPassword(base64_decode($key));
3170
3171
    // Prevent against out of memory
3172
    $cipher->enableContinuousBuffer();
3173
    $cipher->disablePadding();
3174
3175
    // Get file content
3176
    $ciphertext = file_get_contents($filePath . '/' . TP_FILE_PREFIX . $fileName);
3177
3178
    // Decrypt file content and return
3179
    return base64_encode($cipher->decrypt($ciphertext));
3180
}
3181
3182
/**
3183
 * Generate a simple password
3184
 *
3185
 * @param integer $length          Length of string
3186
 * @param boolean $symbolsincluded Allow symbols
3187
 * @return string
3188
 */
3189
function generateQuickPassword($length = 16, $symbolsincluded = true)
3190
{
3191
    // Generate new user password
3192
    $small_letters = range('a', 'z');
3193
    $big_letters = range('A', 'Z');
3194
    $digits = range(0, 9);
3195
    $symbols = $symbolsincluded === true ?
3196
        array('#', '_', '-', '@', '$', '+', '&') : array();
3197
3198
    $res = array_merge($small_letters, $big_letters, $digits, $symbols);
3199
    $c = count($res);
3200
    // first variant
3201
3202
    $random_string = '';
3203
    for ($i = 0; $i < $length; ++$i) {
3204
        $random_string .= $res[random_int(0, $c - 1)];
3205
    }
3206
3207
    return $random_string;
3208
}
3209
3210
/**
3211
 * Permit to store the sharekey of an object for users.
3212
 *
3213
 * @param string $object_name             Type for table selection
3214
 * @param int    $post_folder_is_personal Personal
3215
 * @param int    $post_folder_id          Folder
3216
 * @param int    $post_object_id          Object
3217
 * @param string $objectKey               Object key
3218
 * @param array  $SETTINGS                Teampass settings
3219
 */
3220
function storeUsersShareKey(
3221
    $object_name,
3222
    $post_folder_is_personal,
3223
    $post_folder_id,
3224
    $post_object_id,
3225
    $objectKey,
3226
    $SETTINGS
3227
) {
3228
    // include librairies & connect to DB
3229
    include_once $SETTINGS['cpassman_dir'] . '/includes/config/settings.php';
3230
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
3231
    if (defined('DB_PASSWD_CLEAR') === false) {
3232
        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...
3233
    }
3234
    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...
3235
    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...
3236
    DB::$password = DB_PASSWD_CLEAR;
3237
    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...
3238
    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...
3239
    DB::$encoding = DB_ENCODING;
3240
3241
    // Delete existing entries for this object
3242
    DB::delete(
3243
        $object_name,
3244
        'object_id = %i',
3245
        $post_object_id
3246
    );
3247
3248
    if (
3249
        (int) $post_folder_is_personal === 1
3250
        && in_array($post_folder_id, $_SESSION['personal_folders']) === true
3251
    ) {
3252
        // If this is a personal object
3253
        // Only create the sharekey for user
3254
        DB::insert(
3255
            $object_name,
3256
            array(
3257
                'object_id' => $post_object_id,
3258
                'user_id' => $_SESSION['user_id'],
3259
                'share_key' => encryptUserObjectKey($objectKey, $_SESSION['user']['public_key']),
3260
            )
3261
        );
3262
    } else {
3263
        // This is a public object
3264
        // Create sharekey for each user
3265
        $users = DB::query(
3266
            'SELECT id, public_key
3267
            FROM ' . prefixTable('users') . '
3268
            WHERE id NOT IN ("' . OTV_USER_ID . '","' . SSH_USER_ID . '","' . API_USER_ID . '")
3269
            AND public_key != ""'
3270
        );
3271
        foreach ($users as $user) {
3272
            // Insert in DB the new object key for this item by user
3273
            DB::insert(
3274
                $object_name,
3275
                array(
3276
                    'object_id' => $post_object_id,
3277
                    'user_id' => $user['id'],
3278
                    'share_key' => encryptUserObjectKey(
3279
                        $objectKey,
3280
                        $user['public_key']
3281
                    ),
3282
                )
3283
            );
3284
        }
3285
    }
3286
}
3287
3288
/**
3289
 * Is this string base64 encoded?
3290
 *
3291
 * @param string $str Encoded string?
3292
 *
3293
 * @return bool
3294
 */
3295
function isBase64($str)
3296
{
3297
    $str = (string) trim($str);
3298
3299
    if (!isset($str[0])) {
3300
        return false;
3301
    }
3302
3303
    $base64String = (string) base64_decode($str, true);
3304
    if ($base64String && base64_encode($base64String) === $str) {
3305
        return true;
3306
    }
3307
3308
    return false;
3309
}
3310