Passed
Push — teampass_3.0 ( fcc9dc...305ec6 )
by Nils
06:57
created

stringUtf8Decode()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
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 string
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);
78
}
79
80
81
82
/**
83
 * encryptOld().
84
 *
85
 * crypt a string
86
 *
87
 * @param string $text
88
 */
89
function encryptOld($text, $personalSalt = '')
90
{
91
    if (empty($personalSalt) === false) {
92
        return trim(
93
            base64_encode(
94
                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

94
                /** @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...
95
                    MCRYPT_RIJNDAEL_256,
96
                    $personalSalt,
97
                    $text,
98
                    MCRYPT_MODE_ECB,
99
                    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

99
                    /** @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...
100
                        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

100
                        /** @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...
101
                        MCRYPT_RAND
102
                    )
103
                )
104
            )
105
        );
106
    }
107
108
    // If $personalSalt is not empty
109
    return trim(
110
        base64_encode(
111
            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

111
            /** @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...
112
                MCRYPT_RIJNDAEL_256,
113
                SALT,
114
                $text,
115
                MCRYPT_MODE_ECB,
116
                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

116
                /** @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...
117
                    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

117
                    /** @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...
118
                    MCRYPT_RAND
119
                )
120
            )
121
        )
122
    );
123
}
124
125
/**
126
 * decryptOld().
127
 *
128
 * decrypt a crypted string
129
 */
130
function decryptOld($text, $personalSalt = '')
131
{
132
    if (!empty($personalSalt)) {
133
        return trim(
134
            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

134
            /** @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...
135
                MCRYPT_RIJNDAEL_256,
136
                $personalSalt,
137
                base64_decode($text),
138
                MCRYPT_MODE_ECB,
139
                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

139
                /** @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...
140
                    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

140
                    /** @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...
141
                    MCRYPT_RAND
142
                )
143
            )
144
        );
145
    }
146
147
    // No personal SK
148
    return trim(
149
        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

149
        /** @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...
150
            MCRYPT_RIJNDAEL_256,
151
            SALT,
152
            base64_decode($text),
153
            MCRYPT_MODE_ECB,
154
            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

154
            /** @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...
155
                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

155
                /** @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...
156
                MCRYPT_RAND
157
            )
158
        )
159
    );
160
}
161
162
/**
163
 * encrypt().
164
 *
165
 * crypt a string
166
 *
167
 * @param string $decrypted
168
 */
169
function encrypt($decrypted, $personalSalt = '')
170
{
171
    global $SETTINGS;
172
173
    if (!isset($SETTINGS['cpassman_dir']) || empty($SETTINGS['cpassman_dir'])) {
174
        require_once '../includes/libraries/Encryption/PBKDF2/PasswordHash.php';
175
    } else {
176
        require_once $SETTINGS['cpassman_dir'].'/includes/libraries/Encryption/PBKDF2/PasswordHash.php';
177
    }
178
179
    if (!empty($personalSalt)) {
180
        $staticSalt = $personalSalt;
181
    } else {
182
        $staticSalt = SALT;
183
    }
184
185
    //set our salt to a variable
186
    // Get 64 random bits for the salt for pbkdf2
187
    $pbkdf2Salt = getBits(64);
188
    // generate a pbkdf2 key to use for the encryption.
189
    $key = substr(pbkdf2('sha256', $staticSalt, $pbkdf2Salt, ITCOUNT, 16 + 32, true), 32, 16);
190
    // Build $init_vect and $ivBase64.  We use a block size of 256 bits (AES compliant)
191
    // and CTR mode.  (Note: ECB mode is inadequate as IV is not used.)
192
    $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_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

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

192
    $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...
193
194
    //base64 trim
195
    if (strlen($ivBase64 = rtrim(base64_encode($init_vect), '=')) != 43) {
196
        return false;
197
    }
198
    // Encrypt $decrypted
199
    $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

199
    $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...
200
    // MAC the encrypted text
201
    $mac = hash_hmac('sha256', $encrypted, $staticSalt);
202
    // We're done!
203
    return base64_encode($ivBase64.$encrypted.$mac.$pbkdf2Salt);
204
}
205
206
/**
207
 * decrypt().
208
 *
209
 * decrypt a crypted string
210
 */
211
function decrypt($encrypted, $personalSalt = '')
212
{
213
    global $SETTINGS;
214
215
    if (!isset($SETTINGS['cpassman_dir']) || empty($SETTINGS['cpassman_dir'])) {
216
        include_once '../includes/libraries/Encryption/PBKDF2/PasswordHash.php';
217
    } else {
218
        include_once $SETTINGS['cpassman_dir'].'/includes/libraries/Encryption/PBKDF2/PasswordHash.php';
219
    }
220
221
    if (!empty($personalSalt)) {
222
        $staticSalt = $personalSalt;
223
    } else {
224
        $staticSalt = file_get_contents(SECUREPATH.'/teampass-seckey.txt');
225
    }
226
    //base64 decode the entire payload
227
    $encrypted = base64_decode($encrypted);
228
    // get the salt
229
    $pbkdf2Salt = substr($encrypted, -64);
230
    //remove the salt from the string
231
    $encrypted = substr($encrypted, 0, -64);
232
    $key = substr(pbkdf2('sha256', $staticSalt, $pbkdf2Salt, ITCOUNT, 16 + 32, true), 32, 16);
233
    // Retrieve $init_vect which is the first 22 characters plus ==, base64_decoded.
234
    $init_vect = base64_decode(substr($encrypted, 0, 43).'==');
235
    // Remove $init_vect from $encrypted.
236
    $encrypted = substr($encrypted, 43);
237
    // Retrieve $mac which is the last 64 characters of $encrypted.
238
    $mac = substr($encrypted, -64);
239
    // Remove the last 64 chars from encrypted (remove MAC)
240
    $encrypted = substr($encrypted, 0, -64);
241
    //verify the sha256hmac from the encrypted data before even trying to decrypt it
242
    if (hash_hmac('sha256', $encrypted, $staticSalt) != $mac) {
243
        return false;
244
    }
245
    // Decrypt the data.
246
    $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

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

573
            /** @scrutinizer ignore-type */ $tree
Loading history...
574
        );
575
    } else {
576
        identUser(
577
            $groupesVisiblesUser,
578
            $groupesInterditsUser,
579
            $idFonctions,
580
            $SETTINGS,
581
            $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

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

1601
                /** @scrutinizer ignore-type */ $data,
Loading history...
1602
                true
1603
            );
1604
        } else {
1605
            return json_decode(
1606
                Encryption\Crypt\aesctr::decrypt(
1607
                    $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

1607
                    /** @scrutinizer ignore-type */ $data,
Loading history...
1608
                    $_SESSION['key'],
1609
                    256
1610
                ),
1611
                true
1612
            );
1613
        }
1614
    }
1615
}
1616
1617
/**
1618
 * Create a thumbnail.
1619
 *
1620
 * @param string $src           Source
1621
 * @param string $dest          Destination
1622
 * @param float  $desired_width Size of width
1623
 */
1624
function makeThumbnail($src, $dest, $desired_width)
1625
{
1626
    /* read the source image */
1627
    $source_image = imagecreatefrompng($src);
1628
    $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

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

1629
    $height = imagesy(/** @scrutinizer ignore-type */ $source_image);
Loading history...
1630
1631
    /* find the "desired height" of this thumbnail, relative to the desired width  */
1632
    $desired_height = floor($height * ($desired_width / $width));
1633
1634
    /* create a new, "virtual" image */
1635
    $virtual_image = imagecreatetruecolor($desired_width, $desired_height);
0 ignored issues
show
Bug introduced by
$desired_width of type double is incompatible with the type integer expected by parameter $width of imagecreatetruecolor(). ( Ignorable by Annotation )

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

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

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

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

1638
    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

1638
    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

1638
    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

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

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

2249
                /** @scrutinizer ignore-type */ \Defuse\Crypto\Key::loadFromAsciiSafeString($ascii_key),
Loading history...
2250
                $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

2250
                /** @scrutinizer ignore-type */ $SETTINGS
Loading history...
2251
            );
2252
        // ---
2253
        } elseif ($type === 'encrypt') {
2254
            // Encrypt file
2255
            $err = defuseFileEncrypt(
2256
                $source_file,
2257
                $target_file,
2258
                \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

2258
                /** @scrutinizer ignore-type */ \Defuse\Crypto\Key::loadFromAsciiSafeString($ascii_key),
Loading history...
2259
                $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

2259
                /** @scrutinizer ignore-type */ $SETTINGS
Loading history...
2260
            );
2261
        }
2262
    } else {
2263
        /*
2264
        File encryption/decryption is done with special password and not the SALTKEY
2265
         */
2266
2267
        $err = '';
2268
        if ($type === 'decrypt') {
2269
            // Decrypt file
2270
            $err = defuseFileDecrypt(
2271
                $source_file,
2272
                $target_file,
2273
                $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

2273
                /** @scrutinizer ignore-type */ $password,
Loading history...
2274
                $SETTINGS
2275
            );
2276
        // ---
2277
        } elseif ($type === 'encrypt') {
2278
            // Encrypt file
2279
            $err = defuseFileEncrypt(
2280
                $source_file,
2281
                $target_file,
2282
                $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

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

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

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

2446
    while (false !== ($file = readdir(/** @scrutinizer ignore-type */ $pointer_dir))) {
Loading history...
2447
        if (($file === '.') || ($file === '..')) {
2448
            continue;
2449
        }
2450
2451
        $fullPath = $dir.'/'.$file;
2452
2453
        if (is_dir($fullPath)) {
2454
            if ($res = @chmod($fullPath, $dirPermissions)) {
2455
                $res = @chmodRecursive($fullPath, $dirPermissions, $filePermissions);
2456
            }
2457
        } else {
2458
            $res = chmod($fullPath, $filePermissions);
2459
        }
2460
        if (!$res) {
2461
            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

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

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

2526
        $bytes = openssl_random_pseudo_bytes(/** @scrutinizer ignore-type */ ceil($lenght / 2));
Loading history...
2527
    } else {
2528
        throw new Exception('no cryptographically secure random function available');
2529
    }
2530
2531
    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

2531
    return substr(bin2hex($bytes), 0, /** @scrutinizer ignore-type */ $lenght);
Loading history...
2532
}
2533
2534
/**
2535
 * Obfuscate an email.
2536
 *
2537
 * @param string $email Email address
2538
 *
2539
 * @return string
2540
 */
2541
function obfuscateEmail($email)
2542
{
2543
    $prop = 2;
2544
    $start = '';
2545
    $end = '';
2546
    $domain = substr(strrchr($email, '@'), 1);
2547
    $mailname = str_replace($domain, '', $email);
2548
    $name_l = strlen($mailname);
2549
    $domain_l = strlen($domain);
2550
    for ($i = 0; $i <= $name_l / $prop - 1; ++$i) {
2551
        $start .= 'x';
2552
    }
2553
2554
    for ($i = 0; $i <= $domain_l / $prop - 1; ++$i) {
2555
        $end .= 'x';
2556
    }
2557
2558
    return substr_replace($mailname, $start, 2, $name_l / $prop)
2559
        .substr_replace($domain, $end, 2, $domain_l / $prop);
2560
}
2561
2562
/**
2563
 * Permits to get LDAP information about a user.
2564
 *
2565
 * @param string $username User name
2566
 * @param string $password User password
2567
 * @param array  $SETTINGS Settings
2568
 *
2569
 * @return string
2570
 */
2571
function connectLDAP($username, $password, $SETTINGS)
2572
{
2573
    $ldapInfo = '';
2574
2575
    // Prepare LDAP connection if set up
2576
2577
    if ($SETTINGS['ldap_type'] === 'posix-search') {
2578
        $ldapInfo = ldapPosixSearch(
2579
            $username,
2580
            $password,
2581
            $SETTINGS
2582
        );
2583
    } else {
2584
        $ldapInfo = ldapPosixAndWindows(
2585
            $username,
2586
            $password,
2587
            $SETTINGS
2588
        );
2589
    }
2590
2591
    return json_encode($ldapInfo);
2592
}
2593
2594
/**
2595
 * Undocumented function.
2596
 *
2597
 * @param string $username Username
2598
 * @param string $password Password
2599
 * @param array  $SETTINGS Settings
2600
 *
2601
 * @return array
2602
 */
2603
function ldapPosixSearch($username, $password, $SETTINGS)
2604
{
2605
    $ldapURIs = '';
2606
    $user_email = '';
2607
    $user_found = false;
2608
    $user_lastname = '';
2609
    $user_name = '';
2610
    $ldapConnection = false;
2611
2612
    foreach (explode(',', $SETTINGS['ldap_domain_controler']) as $domainControler) {
2613
        if ($SETTINGS['ldap_ssl'] == 1) {
2614
            $ldapURIs .= 'ldaps://'.$domainControler.':'.$SETTINGS['ldap_port'].' ';
2615
        } else {
2616
            $ldapURIs .= 'ldap://'.$domainControler.':'.$SETTINGS['ldap_port'].' ';
2617
        }
2618
    }
2619
    $ldapconn = ldap_connect($ldapURIs);
2620
2621
    if ($SETTINGS['ldap_tls']) {
2622
        ldap_start_tls($ldapconn);
2623
    }
2624
    ldap_set_option($ldapconn, LDAP_OPT_PROTOCOL_VERSION, 3);
2625
    ldap_set_option($ldapconn, LDAP_OPT_REFERRALS, 0);
2626
2627
    // Is LDAP connection ready?
2628
    if ($ldapconn !== false) {
2629
        // Should we bind the connection?
2630
        if (empty($SETTINGS['ldap_bind_dn']) === false
2631
            && empty($SETTINGS['ldap_bind_passwd']) === false
2632
        ) {
2633
            $ldapbind = ldap_bind($ldapconn, $SETTINGS['ldap_bind_dn'], $SETTINGS['ldap_bind_passwd']);
2634
        } else {
2635
            $ldapbind = false;
2636
        }
2637
        if ((empty($SETTINGS['ldap_bind_dn']) === true && empty($SETTINGS['ldap_bind_passwd']) === true)
2638
            || $ldapbind === true
2639
        ) {
2640
            $filter = '(&('.$SETTINGS['ldap_user_attribute'].'='.$username.')(objectClass='.$SETTINGS['ldap_object_class'].'))';
2641
            $result = ldap_search(
2642
                $ldapconn,
2643
                $SETTINGS['ldap_search_base'],
2644
                $filter,
2645
                array('dn', 'mail', 'givenname', 'sn', 'samaccountname')
2646
            );
2647
2648
            // Check if user was found in AD
2649
            if (ldap_count_entries($ldapconn, $result) > 0) {
2650
                // Get user's info and especially the DN
2651
                $result = ldap_get_entries($ldapconn, $result);
2652
                $user_dn = $result[0]['dn'];
2653
                $user_email = $result[0]['mail'][0];
2654
                $user_lastname = $result[0]['sn'][0];
2655
                $user_name = isset($result[0]['givenname'][0]) === true ? $result[0]['givenname'][0] : '';
2656
                $user_found = true;
2657
2658
                // Should we restrain the search in specified user groups
2659
                $GroupRestrictionEnabled = false;
2660
                if (isset($SETTINGS['ldap_usergroup']) === true
2661
                    && empty($SETTINGS['ldap_usergroup']) === false
2662
                ) {
2663
                    // New way to check User's group membership
2664
                    $filter_group = 'memberUid='.$username;
2665
                    $result_group = ldap_search(
2666
                        $ldapconn,
2667
                        $SETTINGS['ldap_search_base'],
2668
                        $filter_group,
2669
                        array('dn', 'samaccountname')
2670
                    );
2671
2672
                    if ($result_group) {
0 ignored issues
show
introduced by
$result_group is of type resource, thus it always evaluated to false.
Loading history...
2673
                        $entries = ldap_get_entries($ldapconn, $result_group);
2674
2675
                        if ($entries['count'] > 0) {
2676
                            // Now check if group fits
2677
                            for ($i = 0; $i < $entries['count']; ++$i) {
2678
                                $parsr = ldap_explode_dn($entries[$i]['dn'], 0);
2679
                                if (str_replace(array('CN=', 'cn='), '', $parsr[0]) === $SETTINGS['ldap_usergroup']) {
2680
                                    $GroupRestrictionEnabled = true;
2681
                                    break;
2682
                                }
2683
                            }
2684
                        }
2685
                    }
2686
                }
2687
2688
                // Is user in the LDAP?
2689
                if ($GroupRestrictionEnabled === true
2690
                    || ($GroupRestrictionEnabled === false
2691
                    && (isset($SETTINGS['ldap_usergroup']) === false
2692
                    || (isset($SETTINGS['ldap_usergroup']) === true
2693
                    && empty($SETTINGS['ldap_usergroup']) === true)))
2694
                ) {
2695
                    // Try to auth inside LDAP
2696
                    $ldapbind = ldap_bind($ldapconn, $user_dn, $password);
2697
                    if ($ldapbind === true) {
2698
                        $ldapConnection = true;
2699
                    } else {
2700
                        $ldapConnection = false;
2701
                    }
2702
                }
2703
            } else {
2704
                $ldapConnection = false;
2705
            }
2706
        } else {
2707
            $ldapConnection = false;
2708
        }
2709
    } else {
2710
        $ldapConnection = false;
2711
    }
2712
2713
    return array(
2714
        'lastname' => $user_lastname,
2715
        'name' => $user_name,
2716
        'email' => $user_email,
2717
        'auth_success' => $ldapConnection,
2718
        'user_found' => $user_found,
2719
    );
2720
}
2721
2722
/**
2723
 * Undocumented function.
2724
 *
2725
 * @param string $username Username
2726
 * @param string $password Password
2727
 * @param array  $SETTINGS Settings
2728
 *
2729
 * @return array
2730
 */
2731
function ldapPosixAndWindows($username, $password, $SETTINGS)
2732
{
2733
    $user_email = '';
2734
    $user_found = false;
2735
    $user_lastname = '';
2736
    $user_name = '';
2737
    $ldapConnection = false;
2738
    $ldap_suffix = '';
2739
2740
    //Multiple Domain Names
2741
    if (strpos(html_entity_decode($username), '\\') === true) {
2742
        $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...
2743
        $username = substr(html_entity_decode($username), strpos(html_entity_decode($username), '\\') + 1);
2744
    }
2745
    //load ClassLoader
2746
    include_once $SETTINGS['cpassman_dir'].'/sources/SplClassLoader.php';
2747
2748
    $adldap = new SplClassLoader('adLDAP', '../includes/libraries/LDAP');
2749
    $adldap->register();
2750
2751
    // Posix style LDAP handles user searches a bit differently
2752
    if ($SETTINGS['ldap_type'] === 'posix') {
2753
        $ldap_suffix = ','.$SETTINGS['ldap_suffix'].','.$SETTINGS['ldap_domain_dn'];
2754
    } else {
2755
        // case where $SETTINGS['ldap_type'] equals 'windows'
2756
        //Multiple Domain Names
2757
        $ldap_suffix = $SETTINGS['ldap_suffix'];
2758
    }
2759
2760
    // Ensure no double commas exist in ldap_suffix
2761
    $ldap_suffix = str_replace(',,', ',', $ldap_suffix);
2762
2763
    // Create LDAP connection
2764
    $adldap = new adLDAP\adLDAP(
2765
        array(
2766
            'base_dn' => $SETTINGS['ldap_domain_dn'],
2767
            'account_suffix' => $ldap_suffix,
2768
            'domain_controllers' => explode(',', $SETTINGS['ldap_domain_controler']),
2769
            'ad_port' => $SETTINGS['ldap_port'],
2770
            'use_ssl' => $SETTINGS['ldap_ssl'],
2771
            'use_tls' => $SETTINGS['ldap_tls'],
2772
        )
2773
    );
2774
2775
    // OpenLDAP expects an attribute=value pair
2776
    if ($SETTINGS['ldap_type'] === 'posix') {
2777
        $auth_username = $SETTINGS['ldap_user_attribute'].'='.$username;
2778
    } else {
2779
        $auth_username = $username;
2780
    }
2781
2782
    // Authenticate the user
2783
    if ($adldap->authenticate($auth_username, html_entity_decode($password))) {
2784
        // Get user info
2785
        $result = $adldap->user()->info($auth_username, array('mail', 'givenname', 'sn'));
2786
        $user_email = $result[0]['mail'][0];
2787
        $user_lastname = $result[0]['sn'][0];
2788
        $user_name = $result[0]['givenname'][0];
2789
        $user_found = true;
2790
2791
        // Is user in allowed group
2792
        if (isset($SETTINGS['ldap_allowed_usergroup']) === true
2793
            && empty($SETTINGS['ldap_allowed_usergroup']) === false
2794
        ) {
2795
            if ($adldap->user()->inGroup($auth_username, $SETTINGS['ldap_allowed_usergroup']) === true) {
2796
                $ldapConnection = true;
2797
            } else {
2798
                $ldapConnection = false;
2799
            }
2800
        } else {
2801
            $ldapConnection = true;
2802
        }
2803
    } else {
2804
        $ldapConnection = false;
2805
    }
2806
2807
    return array(
2808
        'lastname' => $user_lastname,
2809
        'name' => $user_name,
2810
        'email' => $user_email,
2811
        'auth_success' => $ldapConnection,
2812
        'user_found' => $user_found,
2813
    );
2814
}
2815
2816
//--------------------------------
2817
2818
/**
2819
 * Perform a Query.
2820
 *
2821
 * @param array  $SETTINGS Teamapss settings
2822
 * @param string $fields   Fields to use
2823
 * @param string $table    Table to use
2824
 *
2825
 * @return array
2826
 */
2827
function performDBQuery($SETTINGS, $fields, $table)
2828
{
2829
    // include librairies & connect to DB
2830
    include_once $SETTINGS['cpassman_dir'].'/includes/config/settings.php';
2831
    include_once $SETTINGS['cpassman_dir'].'/includes/libraries/Database/Meekrodb/db.class.php';
2832
    if (defined('DB_PASSWD_CLEAR') === false) {
2833
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
2834
    }
2835
    DB::$host = DB_HOST;
2836
    DB::$user = DB_USER;
2837
    DB::$password = DB_PASSWD_CLEAR;
2838
    DB::$dbName = DB_NAME;
2839
    DB::$port = DB_PORT;
2840
    DB::$encoding = DB_ENCODING;
2841
    //$link = mysqli_connect(DB_HOST, DB_USER, DB_PASSWD_CLEAR, DB_NAME, DB_PORT);
2842
    //$link->set_charset(DB_ENCODING);
2843
2844
    // Insert log in DB
2845
    return DB::query(
2846
        'SELECT '.$fields.'
2847
        FROM '.prefixTable($table)
2848
    );
2849
}
2850
2851
/**
2852
 * Undocumented function.
2853
 *
2854
 * @param int $bytes Size of file
2855
 *
2856
 * @return string
2857
 */
2858
function formatSizeUnits($bytes)
2859
{
2860
    if ($bytes >= 1073741824) {
2861
        $bytes = number_format($bytes / 1073741824, 2).' GB';
2862
    } elseif ($bytes >= 1048576) {
2863
        $bytes = number_format($bytes / 1048576, 2).' MB';
2864
    } elseif ($bytes >= 1024) {
2865
        $bytes = number_format($bytes / 1024, 2).' KB';
2866
    } elseif ($bytes > 1) {
2867
        $bytes = $bytes.' bytes';
2868
    } elseif ($bytes == 1) {
2869
        $bytes = $bytes.' byte';
2870
    } else {
2871
        $bytes = '0 bytes';
2872
    }
2873
2874
    return $bytes;
2875
}
2876
2877
/**
2878
 * Generate user pair of keys.
2879
 *
2880
 * @param string $userPwd User password
2881
 *
2882
 * @return array
2883
 */
2884
function generateUserKeys($userPwd)
2885
{
2886
    // include library
2887
    include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php';
2888
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php';
2889
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
2890
2891
    // Load classes
2892
    $rsa = new Crypt_RSA();
2893
    $cipher = new Crypt_AES();
2894
2895
    // Create the private and public key
2896
    $res = $rsa->createKey(4096);
2897
2898
    // Encrypt the privatekey
2899
    $cipher->setPassword($userPwd);
2900
    $privatekey = $cipher->encrypt($res['privatekey']);
2901
2902
    return array(
2903
        'private_key' => base64_encode($privatekey),
2904
        'public_key' => base64_encode($res['publickey']),
2905
        'private_key_clear' => base64_encode($res['privatekey']),
2906
    );
2907
}
2908
2909
/**
2910
 * Permits to decrypt the user's privatekey.
2911
 *
2912
 * @param string $userPwd        User password
2913
 * @param string $userPrivateKey User private key
2914
 *
2915
 * @return string
2916
 */
2917
function decryptPrivateKey($userPwd, $userPrivateKey)
2918
{
2919
    if (empty($userPwd) === false) {
2920
        include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
2921
2922
        // Load classes
2923
        $cipher = new Crypt_AES();
2924
2925
        // Encrypt the privatekey
2926
        $cipher->setPassword($userPwd);
2927
2928
        return base64_encode($cipher->decrypt(base64_decode($userPrivateKey)));
2929
    }
2930
}
2931
2932
/**
2933
 * Permits to encrypt the user's privatekey.
2934
 *
2935
 * @param string $userPwd        User password
2936
 * @param string $userPrivateKey User private key
2937
 *
2938
 * @return string
2939
 */
2940
function encryptPrivateKey($userPwd, $userPrivateKey)
2941
{
2942
    if (empty($userPwd) === false) {
2943
        include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
2944
2945
        // Load classes
2946
        $cipher = new Crypt_AES();
2947
2948
        // Encrypt the privatekey
2949
        $cipher->setPassword($userPwd);
2950
2951
        return base64_encode($cipher->encrypt(base64_decode($userPrivateKey)));
2952
    }
2953
}
2954
2955
/**
2956
 * Performs an AES encryption of the.
2957
 *
2958
 * @param string $userPwd        User password
2959
 * @param string $userPrivateKey User private key
2960
 *
2961
 * @return string
2962
 */
2963
/*function encryptData($userPwd, $userPrivateKey)
2964
{
2965
    if (empty($userPwd) === false) {
2966
        include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
2967
2968
        // Load classes
2969
        $cipher = new Crypt_AES();
2970
2971
        // Encrypt the privatekey
2972
        $cipher->setPassword($userPwd);
2973
2974
        return $cipher->decrypt(base64_decode($userPrivateKey));
2975
    }
2976
}
2977
*/
2978
2979
/**
2980
 * Generate a key.
2981
 *
2982
 * @param int $length Length of the key to generate
2983
 *
2984
 * @return string
2985
 */
2986
/*
2987
function randomStr($length)
2988
{
2989
    $keyspace = str_shuffle('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ');
2990
    $pieces = [];
2991
    $max = mb_strlen($keyspace, '8bit') - 1;
2992
    for ($i = 0; $i < $length; ++$i) {
2993
        $pieces[] = $keyspace[random_int(0, $max)];
2994
    }
2995
2996
    return implode('', $pieces);
2997
}
2998
*/
2999
3000
/**
3001
 * Encrypts a string using AES.
3002
 *
3003
 * @param string $data String to encrypt
3004
 *
3005
 * @return array
3006
 */
3007
function doDataEncryption($data)
3008
{
3009
    // Includes
3010
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
3011
3012
    // Load classes
3013
    $cipher = new Crypt_AES(CRYPT_AES_MODE_CBC);
3014
3015
    // Generate an object key
3016
    $objectKey = uniqidReal(32);
3017
3018
    // Set it as password
3019
    $cipher->setPassword($objectKey);
3020
3021
    return array(
3022
        'encrypted' => base64_encode($cipher->encrypt($data)),
3023
        'objectKey' => base64_encode($objectKey),
3024
    );
3025
}
3026
3027
/**
3028
 * Decrypts a string using AES.
3029
 *
3030
 * @param string $data Encrypted data
3031
 * @param string $key  Key to uncrypt
3032
 *
3033
 * @return string
3034
 */
3035
function doDataDecryption($data, $key)
3036
{
3037
    // Includes
3038
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
3039
3040
    // Load classes
3041
    $cipher = new Crypt_AES();
3042
3043
    // Set the object key
3044
    $cipher->setPassword(base64_decode($key));
3045
3046
    return base64_encode($cipher->decrypt(base64_decode($data)));
3047
}
3048
3049
/**
3050
 * Encrypts using RSA a string using a public key.
3051
 *
3052
 * @param string $key       Key to be encrypted
3053
 * @param string $publicKey User public key
3054
 *
3055
 * @return string
3056
 */
3057
function encryptUserObjectKey($key, $publicKey)
3058
{
3059
    // Includes
3060
    include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php';
3061
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php';
3062
3063
    // Load classes
3064
    $rsa = new Crypt_RSA();
3065
    $rsa->loadKey(base64_decode($publicKey));
3066
3067
    // Encrypt
3068
    return base64_encode($rsa->encrypt(base64_decode($key)));
3069
}
3070
3071
/**
3072
 * Decrypts using RSA an encrypted string using a private key.
3073
 *
3074
 * @param string $key        Encrypted key
3075
 * @param string $privateKey User private key
3076
 *
3077
 * @return string
3078
 */
3079
function decryptUserObjectKey($key, $privateKey)
3080
{
3081
    // Includes
3082
    include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php';
3083
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php';
3084
3085
    // Load classes
3086
    $rsa = new Crypt_RSA();
3087
    $rsa->loadKey(base64_decode($privateKey));
3088
3089
    // Decrypt
3090
    return base64_encode($rsa->decrypt(base64_decode($key)));
3091
}
3092
3093
/**
3094
 * Encrypts a file.
3095
 *
3096
 * @param string $fileInName File name
3097
 * @param string $fileInPath Path to file
3098
 *
3099
 * @return array
3100
 */
3101
function encryptFile($fileInName, $fileInPath)
3102
{
3103
    if (defined('FILE_BUFFER_SIZE') === false) {
3104
        define('FILE_BUFFER_SIZE', 128 * 1024);
3105
    }
3106
3107
    // Includes
3108
    include_once '../includes/config/include.php';
3109
    include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php';
3110
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php';
3111
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
3112
3113
    // Load classes
3114
    $cipher = new Crypt_AES();
3115
3116
    // Generate an object key
3117
    $objectKey = uniqidReal(32);
3118
3119
    // Set it as password
3120
    $cipher->setPassword($objectKey);
3121
3122
    // Prevent against out of memory
3123
    $cipher->enableContinuousBuffer();
3124
    $cipher->disablePadding();
3125
3126
    // Encrypt the file content
3127
    $plaintext = file_get_contents(
3128
        filter_var($fileInPath.'/'.$fileInName, FILTER_SANITIZE_URL)
3129
    );
3130
    $ciphertext = $cipher->encrypt($plaintext);
3131
3132
    // Save new file
3133
    $hash = md5($plaintext);
3134
    $fileOut = $fileInPath.'/'.TP_FILE_PREFIX.$hash;
3135
    file_put_contents($fileOut, $ciphertext);
3136
    unlink($fileInPath.'/'.$fileInName);
3137
3138
    return array(
3139
        'fileHash' => base64_encode($hash),
3140
        'objectKey' => base64_encode($objectKey),
3141
    );
3142
}
3143
3144
/**
3145
 * Decrypt a file.
3146
 *
3147
 * @param string $fileName File name
3148
 * @param string $filePath Path to file
3149
 * @param string $key      Key to use
3150
 *
3151
 * @return string
3152
 */
3153
function decryptFile($fileName, $filePath, $key)
3154
{
3155
    define('FILE_BUFFER_SIZE', 128 * 1024);
3156
3157
    // Includes
3158
    include_once '../includes/config/include.php';
3159
    include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php';
3160
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php';
3161
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
3162
3163
    // Get file name
3164
    $fileName = base64_decode($fileName);
3165
3166
    // Load classes
3167
    $cipher = new Crypt_AES();
3168
3169
    // Set the object key
3170
    $cipher->setPassword(base64_decode($key));
3171
3172
    // Prevent against out of memory
3173
    $cipher->enableContinuousBuffer();
3174
    $cipher->disablePadding();
3175
3176
    // Get file content
3177
    $ciphertext = file_get_contents($filePath.'/'.TP_FILE_PREFIX.$fileName);
3178
3179
    // Decrypt file content and return
3180
    return base64_encode($cipher->decrypt($ciphertext));
3181
}
3182
3183
/**
3184
 * Undocumented function.
3185
 *
3186
 * @param int $length Length of password
3187
 *
3188
 * @return string
3189
 */
3190
function generateQuickPassword($length = 16, $symbolsincluded = true)
3191
{
3192
    // Generate new user password
3193
    $small_letters = range('a', 'z');
3194
    $big_letters = range('A', 'Z');
3195
    $digits = range(0, 9);
3196
    $symbols = $symbolsincluded === true ?
3197
        array('#', '_', '-', '@', '$', '+', '&') :
3198
        array();
3199
3200
    $res = array_merge($small_letters, $big_letters, $digits, $symbols);
3201
    $c = count($res);
3202
    // first variant
3203
3204
    $random_string = '';
3205
    for ($i = 0; $i < $length; ++$i) {
3206
        $random_string .= $res[random_int(0, $c - 1)];
3207
    }
3208
3209
    return $random_string;
3210
}
3211
3212
/**
3213
 * Permit to store the sharekey of an object for users.
3214
 *
3215
 * @param string $object_name             Type for table selection
3216
 * @param int    $post_folder_is_personal Personal
3217
 * @param int    $post_folder_id          Folder
3218
 * @param int    $post_object_id          Object
3219
 * @param string $objectKey               Object key
3220
 * @param array  $SETTINGS                Teampass settings
3221
 */
3222
function storeUsersShareKey(
3223
    $object_name,
3224
    $post_folder_is_personal,
3225
    $post_folder_id,
3226
    $post_object_id,
3227
    $objectKey,
3228
    $SETTINGS
3229
) {
3230
    // include librairies & connect to DB
3231
    include_once $SETTINGS['cpassman_dir'].'/includes/config/settings.php';
3232
    include_once $SETTINGS['cpassman_dir'].'/includes/libraries/Database/Meekrodb/db.class.php';
3233
    if (defined('DB_PASSWD_CLEAR') === false) {
3234
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
3235
    }
3236
    DB::$host = DB_HOST;
3237
    DB::$user = DB_USER;
3238
    DB::$password = DB_PASSWD_CLEAR;
3239
    DB::$dbName = DB_NAME;
3240
    DB::$port = DB_PORT;
3241
    DB::$encoding = DB_ENCODING;
3242
3243
    // Delete existing entries for this object
3244
    DB::delete(
3245
        $object_name,
3246
        'object_id = %i',
3247
        $post_object_id
3248
    );
3249
3250
    if ((int) $post_folder_is_personal === 1
3251
        && in_array($post_folder_id, $_SESSION['personal_folders']) === true
3252
    ) {
3253
        // If this is a personal object
3254
        // Only create the sharekey for user
3255
        DB::insert(
3256
            $object_name,
3257
            array(
3258
                'object_id' => $post_object_id,
3259
                'user_id' => $_SESSION['user_id'],
3260
                'share_key' => encryptUserObjectKey($objectKey, $_SESSION['user']['public_key']),
3261
            )
3262
        );
3263
    } else {
3264
        // This is a public object
3265
        // Create sharekey for each user
3266
        $users = DB::query(
3267
            'SELECT id, public_key
3268
            FROM '.prefixTable('users').'
3269
            WHERE id NOT IN ("'.OTV_USER_ID.'","'.SSH_USER_ID.'","'.API_USER_ID.'")
3270
            AND public_key != ""'
3271
        );
3272
        foreach ($users as $user) {
3273
            // Insert in DB the new object key for this item by user
3274
            DB::insert(
3275
                $object_name,
3276
                array(
3277
                    'object_id' => $post_object_id,
3278
                    'user_id' => $user['id'],
3279
                    'share_key' => encryptUserObjectKey(
3280
                        $objectKey,
3281
                        $user['public_key']
3282
                    ),
3283
                )
3284
            );
3285
        }
3286
    }
3287
}
3288
3289
/**
3290
 * Is this string base64 encoded?
3291
 *
3292
 * @param string $str Encoded string?
3293
 *
3294
 * @return bool
3295
 */
3296
function isBase64($str)
3297
{
3298
    $str = (string) trim($str);
3299
3300
    if (!isset($str[0])) {
3301
        return false;
3302
    }
3303
3304
    $base64String = (string) base64_decode($str, true);
3305
    if ($base64String && base64_encode($base64String) === $str) {
3306
        return true;
3307
    }
3308
3309
    return false;
3310
}
3311