Passed
Push — teampass_3.0 ( b41394...43cd8c )
by Nils
03:23
created

geItemReadablePath()   A

Complexity

Conditions 6
Paths 12

Size

Total Lines 25
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

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

119
                /** @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...
120
                    MCRYPT_RIJNDAEL_256,
121
                    $personalSalt,
122
                    $text,
123
                    MCRYPT_MODE_ECB,
124
                    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

124
                    /** @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...
125
                        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

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

136
            /** @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...
137
                MCRYPT_RIJNDAEL_256,
138
                SALT,
139
                $text,
140
                MCRYPT_MODE_ECB,
141
                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

141
                /** @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...
142
                    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

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

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

164
                /** @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...
165
                    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

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

174
        /** @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...
175
            MCRYPT_RIJNDAEL_256,
176
            SALT,
177
            base64_decode($text),
178
            MCRYPT_MODE_ECB,
179
            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

179
            /** @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...
180
                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

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

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

217
    $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...
218
219
    //base64 trim
220
    if (strlen($ivBase64 = rtrim(base64_encode($init_vect), '=')) != 43) {
221
        return false;
222
    }
223
    // Encrypt $decrypted
224
    $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

224
    $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...
225
    // MAC the encrypted text
226
    $mac = hash_hmac('sha256', $encrypted, $staticSalt);
227
    // We're done!
228
    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

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

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

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

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

1615
                /** @scrutinizer ignore-type */ $data,
Loading history...
1616
                true
1617
            );
1618
        } else {
1619
            return json_decode(
1620
                Encryption\Crypt\aesctr::decrypt(
1621
                    $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

1621
                    /** @scrutinizer ignore-type */ $data,
Loading history...
1622
                    $_SESSION['key'],
1623
                    256
1624
                ),
1625
                true
1626
            );
1627
        }
1628
    }
1629
}
1630
1631
/**
1632
 * Create a thumbnail.
1633
 *
1634
 * @param string $src           Source
1635
 * @param string $dest          Destination
1636
 * @param float  $desired_width Size of width
1637
 */
1638
function makeThumbnail($src, $dest, $desired_width)
1639
{
1640
    /* read the source image */
1641
    $source_image = imagecreatefrompng($src);
1642
    $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

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

1643
    $height = imagesy(/** @scrutinizer ignore-type */ $source_image);
Loading history...
1644
1645
    /* find the "desired height" of this thumbnail, relative to the desired width  */
1646
    $desired_height = floor($height * ($desired_width / $width));
1647
1648
    /* create a new, "virtual" image */
1649
    $virtual_image = imagecreatetruecolor($desired_width, $desired_height);
0 ignored issues
show
Bug introduced by
$desired_height of type double is incompatible with the type integer expected by parameter $height of imagecreatetruecolor(). ( Ignorable by Annotation )

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

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

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

1649
    $virtual_image = imagecreatetruecolor(/** @scrutinizer ignore-type */ $desired_width, $desired_height);
Loading history...
1650
1651
    /* copy source image at a resized size */
1652
    imagecopyresampled($virtual_image, $source_image, 0, 0, 0, 0, $desired_width, $desired_height, $width, $height);
0 ignored issues
show
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

1652
    imagecopyresampled($virtual_image, /** @scrutinizer ignore-type */ $source_image, 0, 0, 0, 0, $desired_width, $desired_height, $width, $height);
Loading history...
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

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

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

1652
    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

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

1655
    imagejpeg(/** @scrutinizer ignore-type */ $virtual_image, $dest);
Loading history...
1656
}
1657
1658
/**
1659
 * Check table prefix in SQL query.
1660
 *
1661
 * @param string $table Table name
1662
 *
1663
 * @return string
1664
 */
1665
function prefixTable($table)
1666
{
1667
    $safeTable = htmlspecialchars(DB_PREFIX.$table);
1668
    if (!empty($safeTable)) {
1669
        // sanitize string
1670
        return $safeTable;
1671
    } else {
1672
        // stop error no table
1673
        return 'table_not_exists';
1674
    }
1675
}
1676
1677
/*
1678
 * Creates a KEY using PasswordLib
1679
 */
1680
function GenerateCryptKey($size = null, $secure = false, $numerals = false, $capitalize = false, $symbols = false)
1681
{
1682
    include_once 'SplClassLoader.php';
1683
1684
    if ($secure === true) {
1685
        $numerals = true;
1686
        $capitalize = true;
1687
        $symbols = true;
1688
    }
1689
1690
    // Load libraries
1691
    if (file_exists('../includes/config/tp.config.php')) {
1692
        $generator = new SplClassLoader('PasswordGenerator\Generator', '../includes/libraries');
1693
    } elseif (file_exists('./includes/config/tp.config.php')) {
1694
        $generator = new SplClassLoader('PasswordGenerator\Generator', './includes/libraries');
1695
    } else {
1696
        throw new Exception('Error file not exists', 1);
1697
    }
1698
1699
    $generator->register();
1700
    $generator = new PasswordGenerator\Generator\ComputerPasswordGenerator();
1701
1702
    // Can we use PHP7 random_int function?
1703
    /*if (version_compare(phpversion(), '7.0', '>=')) {
1704
        include_once $SETTINGS['cpassman_dir'].'/includes/libraries/PasswordGenerator/RandomGenerator/Php7RandomGenerator.php';
1705
        $generator->setRandomGenerator(new PasswordGenerator\RandomGenerator\Php7RandomGenerator());
1706
    }*/
1707
1708
    // init
1709
    if (empty($size) === false && is_null($size) === false) {
1710
        $generator->setLength(intval($size));
1711
    }
1712
    if (empty($numerals) === false) {
1713
        $generator->setNumbers($numerals);
1714
    }
1715
    if (empty($capitalize) === false) {
1716
        $generator->setUppercase($capitalize);
1717
    }
1718
    if (empty($symbols) === false) {
1719
        $generator->setSymbols($symbols);
1720
    }
1721
1722
    // generate and send back
1723
    return $generator->generatePassword();
1724
}
1725
1726
/*
1727
* Send sysLOG message
1728
* @param string $message
1729
* @param string $host
1730
*/
1731
function send_syslog($message, $host, $port, $component = 'teampass')
1732
{
1733
    $sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
1734
    $syslog_message = '<123>'.date('M d H:i:s ').$component.': '.$message;
1735
    socket_sendto($sock, $syslog_message, strlen($syslog_message), 0, $host, $port);
1736
    socket_close($sock);
1737
}
1738
1739
/**
1740
 * logEvents().
1741
 *
1742
 * permits to log events into DB
1743
 *
1744
 * @param string $type
1745
 * @param string $label
1746
 * @param string $field_1
1747
 */
1748
function logEvents($type, $label, $who, $login = null, $field_1 = null)
1749
{
1750
    global $server, $user, $pass, $database, $port, $encoding;
1751
    global $SETTINGS;
1752
1753
    if (empty($who)) {
1754
        $who = getClientIpServer();
1755
    }
1756
1757
    // include librairies & connect to DB
1758
    require_once $SETTINGS['cpassman_dir'].'/includes/libraries/Database/Meekrodb/db.class.php';
1759
    if (defined('DB_PASSWD_CLEAR') === false) {
1760
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
1761
    }
1762
    DB::$host = DB_HOST;
1763
    DB::$user = DB_USER;
1764
    DB::$password = DB_PASSWD_CLEAR;
1765
    DB::$dbName = DB_NAME;
1766
    DB::$port = DB_PORT;
1767
    DB::$encoding = DB_ENCODING;
1768
    //$link = mysqli_connect(DB_HOST, DB_USER, DB_PASSWD_CLEAR, DB_NAME, DB_PORT);
1769
    //$link->set_charset(DB_ENCODING);
1770
1771
    DB::insert(
1772
        prefixTable('log_system'),
1773
        array(
1774
            'type' => $type,
1775
            'date' => time(),
1776
            'label' => $label,
1777
            'qui' => $who,
1778
            'field_1' => $field_1 === null ? '' : $field_1,
1779
        )
1780
    );
1781
1782
    // If SYSLOG
1783
    if (isset($SETTINGS['syslog_enable']) === true && (int) $SETTINGS['syslog_enable'] === 1) {
1784
        if ($type === 'user_mngt') {
1785
            send_syslog(
1786
                'action='.str_replace('at_', '', $label).' attribute=user user='.$who.' userid="'.$login.'" change="'.$field_1.'" ',
1787
                $SETTINGS['syslog_host'],
1788
                $SETTINGS['syslog_port'],
1789
                'teampass'
1790
            );
1791
        } else {
1792
            send_syslog(
1793
                'action='.$type.' attribute='.$label.' user='.$who.' userid="'.$login.'" ',
1794
                $SETTINGS['syslog_host'],
1795
                $SETTINGS['syslog_port'],
1796
                'teampass'
1797
            );
1798
        }
1799
    }
1800
}
1801
1802
/**
1803
 * Log events.
1804
 *
1805
 * @param array  $SETTINGS        Teampass settings
1806
 * @param int    $item_id         Item id
1807
 * @param string $item_label      Item label
1808
 * @param int    $id_user         User id
1809
 * @param string $action          Code for reason
1810
 * @param string $login           User login
1811
 * @param string $raison          Code for reason
1812
 * @param string $encryption_type Encryption on
1813
 */
1814
function logItems(
1815
    $SETTINGS,
1816
    $item_id,
1817
    $item_label,
1818
    $id_user,
1819
    $action,
1820
    $login = null,
1821
    $raison = null,
1822
    $encryption_type = null
1823
) {
1824
    $dataItem = '';
0 ignored issues
show
Unused Code introduced by
The assignment to $dataItem is dead and can be removed.
Loading history...
1825
1826
    // include librairies & connect to DB
1827
    include_once $SETTINGS['cpassman_dir'].'/includes/libraries/Database/Meekrodb/db.class.php';
1828
    if (defined('DB_PASSWD_CLEAR') === false) {
1829
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
1830
    }
1831
    DB::$host = DB_HOST;
1832
    DB::$user = DB_USER;
1833
    DB::$password = DB_PASSWD_CLEAR;
1834
    DB::$dbName = DB_NAME;
1835
    DB::$port = DB_PORT;
1836
    DB::$encoding = DB_ENCODING;
1837
1838
    // Insert log in DB
1839
    DB::insert(
1840
        prefixTable('log_items'),
1841
        array(
1842
            'id_item' => $item_id,
1843
            'date' => time(),
1844
            'id_user' => $id_user,
1845
            'action' => $action,
1846
            'raison' => $raison,
1847
            'raison_iv' => '',
1848
            'encryption_type' => is_null($encryption_type) === true ? TP_ENCRYPTION_NAME : $encryption_type,
1849
        )
1850
    );
1851
    // Timestamp the last change
1852
    if ($action === 'at_creation' || $action === 'at_modifiation' || $action === 'at_delete' || $action === 'at_import') {
1853
        DB::update(
1854
            prefixTable('misc'),
1855
            array(
1856
                'valeur' => time(),
1857
                ),
1858
            'type = %s AND intitule = %s',
1859
            'timestamp',
1860
            'last_item_change'
1861
        );
1862
    }
1863
1864
    // SYSLOG
1865
    if (isset($SETTINGS['syslog_enable']) === true && $SETTINGS['syslog_enable'] === '1') {
1866
        // Extract reason
1867
        $attribute = explode(' : ', $raison);
1868
1869
        // Get item info if not known
1870
        if (empty($item_label) === true) {
1871
            $dataItem = DB::queryfirstrow(
1872
                'SELECT id, id_tree, label
1873
                FROM '.prefixTable('items').'
1874
                WHERE id = %i',
1875
                $item_id
1876
            );
1877
1878
            $item_label = $dataItem['label'];
1879
        }
1880
1881
        send_syslog(
1882
            'action='.str_replace('at_', '', $action).' attribute='.str_replace('at_', '', $attribute[0]).' itemno='.$item_id.' user='.addslashes($login).' itemname="'.addslashes($item_label).'"',
1883
            $SETTINGS['syslog_host'],
1884
            $SETTINGS['syslog_port'],
1885
            'teampass'
1886
        );
1887
    }
1888
1889
    // send notification if enabled
1890
    notifyOnChange($item_id, $action, $SETTINGS);
0 ignored issues
show
Bug introduced by
$action of type string is incompatible with the type array expected by parameter $action of notifyOnChange(). ( Ignorable by Annotation )

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

1890
    notifyOnChange($item_id, /** @scrutinizer ignore-type */ $action, $SETTINGS);
Loading history...
1891
}
1892
1893
/**
1894
 * If enabled, then notify admin/manager.
1895
 *
1896
 * @param int   $item_id  Item id
1897
 * @param array $action   Action to do
1898
 * @param array $SETTINGS Teampass settings
1899
 */
1900
function notifyOnChange($item_id, $action, $SETTINGS)
1901
{
1902
    if (isset($SETTINGS['enable_email_notification_on_item_shown']) === true
1903
        && (int) $SETTINGS['enable_email_notification_on_item_shown'] === 1
1904
        && $action === 'at_shown'
0 ignored issues
show
introduced by
The condition $action === 'at_shown' is always false.
Loading history...
1905
    ) {
1906
        // Get info about item
1907
        $dataItem = DB::queryfirstrow(
1908
            'SELECT id, id_tree, label
1909
            FROM '.prefixTable('items').'
1910
            WHERE id = %i',
1911
            $item_id
1912
        );
1913
        $item_label = $dataItem['label'];
1914
1915
        // send back infos
1916
        DB::insert(
1917
            prefixTable('emails'),
1918
            array(
1919
                'timestamp' => time(),
1920
                'subject' => langHdl('email_on_open_notification_subject'),
1921
                'body' => str_replace(
1922
                    array('#tp_user#', '#tp_item#', '#tp_link#'),
1923
                    array(
1924
                        addslashes($_SESSION['name'].' '.$_SESSION['lastname']),
1925
                        addslashes($item_label),
1926
                        $SETTINGS['cpassman_url'].'/index.php?page=items&group='.$dataItem['id_tree'].'&id='.$item_id,
1927
                    ),
1928
                    langHdl('email_on_open_notification_mail')
1929
                ),
1930
                'receivers' => $_SESSION['listNotificationEmails'],
1931
                'status' => '',
1932
            )
1933
        );
1934
    }
1935
}
1936
1937
/**
1938
 * Prepare notification email to subscribers.
1939
 *
1940
 * @param int    $item_id  Item id
1941
 * @param string $label    Item label
1942
 * @param array  $changes  List of changes
1943
 * @param array  $SETTINGS Teampass settings
1944
 */
1945
function notifyChangesToSubscribers($item_id, $label, $changes, $SETTINGS)
1946
{
1947
    // send email to user that what to be notified
1948
    $notification = DB::queryOneColumn(
1949
        'email',
1950
        'SELECT *
1951
        FROM '.prefixTable('notification').' AS n
1952
        INNER JOIN '.prefixTable('users').' AS u ON (n.user_id = u.id)
1953
        WHERE n.item_id = %i AND n.user_id != %i',
1954
        $item_id,
1955
        $_SESSION['user_id']
1956
    );
1957
1958
    if (DB::count() > 0) {
1959
        // Prepare path
1960
        $path = geItemReadablePath($item_id, '', $SETTINGS);
1961
1962
        // Get list of changes
1963
        $htmlChanges = '<ul>';
1964
        foreach ($changes as $change) {
1965
            $htmlChanges .= '<li>'.$change.'</li>';
1966
        }
1967
        $htmlChanges .= '</ul>';
1968
1969
        // send email
1970
        DB::insert(
1971
            prefixTable('emails'),
1972
            array(
1973
                'timestamp' => time(),
1974
                'subject' => langHdl('email_subject_item_updated'),
1975
                'body' => str_replace(
1976
                    array('#item_label#', '#folder_name#', '#item_id#', '#url#', '#name#', '#lastname#', '#changes#'),
1977
                    array($label, $path, $item_id, $SETTINGS['cpassman_url'], $_SESSION['name'], $_SESSION['lastname'], $htmlChanges),
1978
                    langHdl('email_body_item_updated')
1979
                ),
1980
                'receivers' => implode(',', $notification),
1981
                'status' => '',
1982
            )
1983
        );
1984
    }
1985
}
1986
1987
/**
1988
 * Returns the Item + path.
1989
 *
1990
 * @param int    $id_tree
1991
 * @param string $label
1992
 * @param array  $SETTINGS
1993
 *
1994
 * @return string
1995
 */
1996
function geItemReadablePath($id_tree, $label, $SETTINGS)
1997
{
1998
    // Class loader
1999
    require_once $SETTINGS['cpassman_dir'].'/sources/SplClassLoader.php';
2000
2001
    //Load Tree
2002
    $tree = new SplClassLoader('Tree\NestedTree', '../includes/libraries');
2003
    $tree->register();
2004
    $tree = new Tree\NestedTree\NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
2005
2006
    $arbo = $tree->getPath($id_tree, true);
2007
    $path = '';
2008
    foreach ($arbo as $elem) {
2009
        if (empty($path) === true) {
2010
            $path = htmlspecialchars(stripslashes(htmlspecialchars_decode($elem->title, ENT_QUOTES)), ENT_QUOTES).' ';
2011
        } else {
2012
            $path .= '&#8594; '.htmlspecialchars(stripslashes(htmlspecialchars_decode($elem->title, ENT_QUOTES)), ENT_QUOTES);
2013
        }
2014
    }
2015
2016
    // Build text to show user
2017
    if (empty($label) === false) {
2018
        return empty($path) === true ? addslashes($label) : addslashes($label).' ('.$path.')';
2019
    } else {
2020
        return empty($path) === true ? '' : $path;
2021
    }
2022
}
2023
2024
/**
2025
 * Get the client ip address.
2026
 *
2027
 * @return string IP address
2028
 */
2029
function getClientIpServer()
2030
{
2031
    if (getenv('HTTP_CLIENT_IP')) {
2032
        $ipaddress = getenv('HTTP_CLIENT_IP');
2033
    } elseif (getenv('HTTP_X_FORWARDED_FOR')) {
2034
        $ipaddress = getenv('HTTP_X_FORWARDED_FOR');
2035
    } elseif (getenv('HTTP_X_FORWARDED')) {
2036
        $ipaddress = getenv('HTTP_X_FORWARDED');
2037
    } elseif (getenv('HTTP_FORWARDED_FOR')) {
2038
        $ipaddress = getenv('HTTP_FORWARDED_FOR');
2039
    } elseif (getenv('HTTP_FORWARDED')) {
2040
        $ipaddress = getenv('HTTP_FORWARDED');
2041
    } elseif (getenv('REMOTE_ADDR')) {
2042
        $ipaddress = getenv('REMOTE_ADDR');
2043
    } else {
2044
        $ipaddress = 'UNKNOWN';
2045
    }
2046
2047
    return $ipaddress;
2048
}
2049
2050
/**
2051
 * Escape all HTML, JavaScript, and CSS.
2052
 *
2053
 * @param string $input    The input string
2054
 * @param string $encoding Which character encoding are we using?
2055
 *
2056
 * @return string
2057
 */
2058
function noHTML($input, $encoding = 'UTF-8')
2059
{
2060
    return htmlspecialchars($input, ENT_QUOTES | ENT_XHTML, $encoding, false);
2061
}
2062
2063
/**
2064
 * handleConfigFile().
2065
 *
2066
 * permits to handle the Teampass config file
2067
 * $action accepts "rebuild" and "update"
2068
 */
2069
function handleConfigFile($action, $field = null, $value = null)
2070
{
2071
    global $server, $user, $pass, $database, $port, $encoding;
2072
    global $SETTINGS;
2073
2074
    $tp_config_file = '../includes/config/tp.config.php';
2075
2076
    // include librairies & connect to DB
2077
    include_once $SETTINGS['cpassman_dir'].'/includes/libraries/Database/Meekrodb/db.class.php';
2078
    if (defined('DB_PASSWD_CLEAR') === false) {
2079
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
2080
    }
2081
    DB::$host = DB_HOST;
2082
    DB::$user = DB_USER;
2083
    DB::$password = DB_PASSWD_CLEAR;
2084
    DB::$dbName = DB_NAME;
2085
    DB::$port = DB_PORT;
2086
    DB::$encoding = DB_ENCODING;
2087
    //$link = mysqli_connect(DB_HOST, DB_USER, DB_PASSWD_CLEAR, DB_NAME, DB_PORT);
2088
    //$link->set_charset(DB_ENCODING);
2089
2090
    if (file_exists($tp_config_file) === false || $action === 'rebuild') {
2091
        // perform a copy
2092
        if (file_exists($tp_config_file)) {
2093
            if (!copy($tp_config_file, $tp_config_file.'.'.date('Y_m_d_His', time()))) {
2094
                return "ERROR: Could not copy file '".$tp_config_file."'";
2095
            }
2096
        }
2097
2098
        // regenerate
2099
        $data = array();
2100
        $data[0] = "<?php\n";
2101
        $data[1] = "global \$SETTINGS;\n";
2102
        $data[2] = "\$SETTINGS = array (\n";
2103
        $rows = DB::query(
2104
            'SELECT * FROM '.prefixTable('misc').' WHERE type=%s',
2105
            'admin'
2106
        );
2107
        foreach ($rows as $record) {
2108
            array_push($data, "    '".$record['intitule']."' => '".$record['valeur']."',\n");
2109
        }
2110
        array_push($data, ");\n");
2111
        $data = array_unique($data);
2112
    } elseif ($action === 'update' && empty($field) === false) {
2113
        $data = file($tp_config_file);
2114
        $inc = 0;
2115
        $bFound = false;
2116
        foreach ($data as $line) {
2117
            if (stristr($line, ');')) {
2118
                break;
2119
            }
2120
2121
            if (stristr($line, "'".$field."' => '")) {
2122
                $data[$inc] = "    '".$field."' => '".filter_var($value, FILTER_SANITIZE_STRING)."',\n";
2123
                $bFound = true;
2124
                break;
2125
            }
2126
            ++$inc;
2127
        }
2128
        if ($bFound === false) {
2129
            $data[($inc)] = "    '".$field."' => '".filter_var($value, FILTER_SANITIZE_STRING)."',\n);\n";
2130
        }
2131
    }
2132
2133
    // update file
2134
    file_put_contents($tp_config_file, implode('', isset($data) ? $data : array()));
2135
2136
    return true;
2137
}
2138
2139
/*
2140
** Permits to replace &#92; to permit correct display
2141
*/
2142
/**
2143
 * @param string $input
2144
 */
2145
function handleBackslash($input)
2146
{
2147
    return str_replace('&amp;#92;', '&#92;', $input);
2148
}
2149
2150
/*
2151
** Permits to loas settings
2152
*/
2153
function loadSettings()
2154
{
2155
    global $SETTINGS;
2156
2157
    /* LOAD CPASSMAN SETTINGS */
2158
    if (!isset($SETTINGS['loaded']) || $SETTINGS['loaded'] != 1) {
2159
        $SETTINGS['duplicate_folder'] = 0; //by default, this is set to 0;
2160
        $SETTINGS['duplicate_item'] = 0; //by default, this is set to 0;
2161
        $SETTINGS['number_of_used_pw'] = 5; //by default, this value is set to 5;
2162
        $settings = array();
2163
2164
        $rows = DB::query(
2165
            'SELECT * FROM '.prefixTable('misc').' WHERE type=%s_type OR type=%s_type2',
2166
            array(
2167
                'type' => 'admin',
2168
                'type2' => 'settings',
2169
            )
2170
        );
2171
        foreach ($rows as $record) {
2172
            if ($record['type'] === 'admin') {
2173
                $SETTINGS[$record['intitule']] = $record['valeur'];
2174
            } else {
2175
                $settings[$record['intitule']] = $record['valeur'];
2176
            }
2177
        }
2178
        $SETTINGS['loaded'] = 1;
2179
        $SETTINGS['default_session_expiration_time'] = 5;
2180
    }
2181
}
2182
2183
/*
2184
** check if folder has custom fields.
2185
** Ensure that target one also has same custom fields
2186
*/
2187
function checkCFconsistency($source_id, $target_id)
2188
{
2189
    $source_cf = array();
2190
    $rows = DB::QUERY(
2191
        'SELECT id_category
2192
        FROM '.prefixTable('categories_folders').'
2193
        WHERE id_folder = %i',
2194
        $source_id
2195
    );
2196
    foreach ($rows as $record) {
2197
        array_push($source_cf, $record['id_category']);
2198
    }
2199
2200
    $target_cf = array();
2201
    $rows = DB::QUERY(
2202
        'SELECT id_category
2203
        FROM '.prefixTable('categories_folders').'
2204
        WHERE id_folder = %i',
2205
        $target_id
2206
    );
2207
    foreach ($rows as $record) {
2208
        array_push($target_cf, $record['id_category']);
2209
    }
2210
2211
    $cf_diff = array_diff($source_cf, $target_cf);
2212
    if (count($cf_diff) > 0) {
2213
        return false;
2214
    }
2215
2216
    return true;
2217
}
2218
2219
/**
2220
 * Will encrypte/decrypt a fil eusing Defuse.
2221
 *
2222
 * @param string $type        can be either encrypt or decrypt
2223
 * @param string $source_file path to source file
2224
 * @param string $target_file path to target file
2225
 * @param array  $SETTINGS    Settings
2226
 * @param string $password    A password
2227
 *
2228
 * @return string|bool
2229
 */
2230
function prepareFileWithDefuse(
2231
    $type,
2232
    $source_file,
2233
    $target_file,
2234
    $SETTINGS,
2235
    $password = null
2236
) {
2237
    // Load AntiXSS
2238
    include_once $SETTINGS['cpassman_dir'].'/includes/libraries/protect/AntiXSS/AntiXSS.php';
2239
    $antiXss = new protect\AntiXSS\AntiXSS();
2240
2241
    // Protect against bad inputs
2242
    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...
2243
        return 'error_cannot_be_array';
2244
    }
2245
2246
    // Sanitize
2247
    $source_file = $antiXss->xss_clean($source_file);
2248
    $target_file = $antiXss->xss_clean($target_file);
2249
2250
    if (empty($password) === true || is_null($password) === true) {
2251
        /*
2252
        File encryption/decryption is done with the SALTKEY
2253
         */
2254
2255
        // get KEY
2256
        $ascii_key = file_get_contents(SECUREPATH.'/teampass-seckey.txt');
2257
2258
        // Now perform action on the file
2259
        $err = '';
2260
        if ($type === 'decrypt') {
2261
            // Decrypt file
2262
            $err = defuseFileDecrypt(
2263
                $source_file,
2264
                $target_file,
2265
                \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 array expected by parameter $SETTINGS 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

2265
                /** @scrutinizer ignore-type */ \Defuse\Crypto\Key::loadFromAsciiSafeString($ascii_key),
Loading history...
2266
                $SETTINGS
0 ignored issues
show
Bug introduced by
$SETTINGS of type array 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

2266
                /** @scrutinizer ignore-type */ $SETTINGS
Loading history...
2267
            );
2268
        // ---
2269
        } elseif ($type === 'encrypt') {
2270
            // Encrypt file
2271
            $err = defuseFileEncrypt(
2272
                $source_file,
2273
                $target_file,
2274
                \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 array expected by parameter $SETTINGS 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

2274
                /** @scrutinizer ignore-type */ \Defuse\Crypto\Key::loadFromAsciiSafeString($ascii_key),
Loading history...
2275
                $SETTINGS
0 ignored issues
show
Bug introduced by
$SETTINGS of type array 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

2275
                /** @scrutinizer ignore-type */ $SETTINGS
Loading history...
2276
            );
2277
        }
2278
    } else {
2279
        /*
2280
        File encryption/decryption is done with special password and not the SALTKEY
2281
         */
2282
2283
        $err = '';
2284
        if ($type === 'decrypt') {
2285
            // Decrypt file
2286
            $err = defuseFileDecrypt(
2287
                $source_file,
2288
                $target_file,
2289
                $password,
0 ignored issues
show
Bug introduced by
$password of type string is incompatible with the type array expected by parameter $SETTINGS 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

2289
                /** @scrutinizer ignore-type */ $password,
Loading history...
2290
                $SETTINGS
2291
            );
2292
        // ---
2293
        } elseif ($type === 'encrypt') {
2294
            // Encrypt file
2295
            $err = defuseFileEncrypt(
2296
                $source_file,
2297
                $target_file,
2298
                $password,
0 ignored issues
show
Bug introduced by
$password of type string is incompatible with the type array expected by parameter $SETTINGS 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

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

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

2412
    fclose(/** @scrutinizer ignore-type */ $debugFile);
Loading history...
2413
}
2414
2415
/**
2416
 * DELETE the file with expected command depending on server type.
2417
 *
2418
 * @param string $file     Path to file
2419
 * @param array  $SETTINGS Teampass settings
2420
 */
2421
function fileDelete($file, $SETTINGS)
2422
{
2423
    // Load AntiXSS
2424
    include_once $SETTINGS['cpassman_dir'].'/includes/libraries/protect/AntiXSS/AntiXSS.php';
2425
    $antiXss = new protect\AntiXSS\AntiXSS();
2426
2427
    $file = $antiXss->xss_clean($file);
2428
    if (is_file($file)) {
2429
        unlink($file);
2430
    }
2431
}
2432
2433
/**
2434
 * Permits to extract the file extension.
2435
 *
2436
 * @param string $file File name
2437
 *
2438
 * @return string
2439
 */
2440
function getFileExtension($file)
2441
{
2442
    if (strpos($file, '.') === false) {
2443
        return $file;
2444
    }
2445
2446
    return substr($file, strrpos($file, '.') + 1);
2447
}
2448
2449
/**
2450
 * Performs chmod operation on subfolders.
2451
 *
2452
 * @param string $dir             Parent folder
2453
 * @param int    $dirPermissions  New permission on folders
2454
 * @param int    $filePermissions New permission on files
2455
 *
2456
 * @return bool
2457
 */
2458
function chmodRecursive($dir, $dirPermissions, $filePermissions)
2459
{
2460
    $pointer_dir = opendir($dir);
2461
    $res = true;
2462
    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

2462
    while (false !== ($file = readdir(/** @scrutinizer ignore-type */ $pointer_dir))) {
Loading history...
2463
        if (($file === '.') || ($file === '..')) {
2464
            continue;
2465
        }
2466
2467
        $fullPath = $dir.'/'.$file;
2468
2469
        if (is_dir($fullPath)) {
2470
            if ($res = @chmod($fullPath, $dirPermissions)) {
2471
                $res = @chmodRecursive($fullPath, $dirPermissions, $filePermissions);
2472
            }
2473
        } else {
2474
            $res = chmod($fullPath, $filePermissions);
2475
        }
2476
        if (!$res) {
2477
            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

2477
            closedir(/** @scrutinizer ignore-type */ $pointer_dir);
Loading history...
2478
2479
            return false;
2480
        }
2481
    }
2482
    closedir($pointer_dir);
2483
    if (is_dir($dir) && $res) {
2484
        $res = @chmod($dir, $dirPermissions);
2485
    }
2486
2487
    return $res;
2488
}
2489
2490
/**
2491
 * Check if user can access to this item.
2492
 *
2493
 * @param int $item_id ID of item
2494
 */
2495
function accessToItemIsGranted($item_id)
2496
{
2497
    global $SETTINGS;
2498
2499
    include_once $SETTINGS['cpassman_dir'].'/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
2500
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
2501
2502
    // Prepare superGlobal variables
2503
    $session_groupes_visibles = $superGlobal->get('groupes_visibles', 'SESSION');
2504
    $session_list_restricted_folders_for_items = $superGlobal->get('list_restricted_folders_for_items', 'SESSION');
2505
2506
    // Load item data
2507
    $data = DB::queryFirstRow(
2508
        'SELECT id_tree
2509
        FROM '.prefixTable('items').'
2510
        WHERE id = %i',
2511
        $item_id
2512
    );
2513
2514
    // Check if user can access this folder
2515
    if (in_array($data['id_tree'], $session_groupes_visibles) === false) {
2516
        // Now check if this folder is restricted to user
2517
        if (isset($session_list_restricted_folders_for_items[$data['id_tree']])
2518
            && !in_array($item_id, $session_list_restricted_folders_for_items[$data['id_tree']])
2519
        ) {
2520
            return 'ERR_FOLDER_NOT_ALLOWED';
2521
        } else {
2522
            return 'ERR_FOLDER_NOT_ALLOWED';
2523
        }
2524
    }
2525
2526
    return true;
2527
}
2528
2529
/**
2530
 * Creates a unique key.
2531
 *
2532
 * @param float $lenght Key lenght
2533
 *
2534
 * @return string
2535
 */
2536
function uniqidReal($lenght = 13)
2537
{
2538
    // uniqid gives 13 chars, but you could adjust it to your needs.
2539
    if (function_exists('random_bytes')) {
2540
        $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

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

2542
        $bytes = openssl_random_pseudo_bytes(/** @scrutinizer ignore-type */ ceil($lenght / 2));
Loading history...
2543
    } else {
2544
        throw new Exception('no cryptographically secure random function available');
2545
    }
2546
2547
    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

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

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