Passed
Push — teampass_3.0 ( 0b4100...9ea87b )
by Nils
06:46
created

convertToArray()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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

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

Loading history...
120
                    MCRYPT_RIJNDAEL_256,
121
                    $personalSalt,
122
                    $text,
123
                    MCRYPT_MODE_ECB,
124
                    mcrypt_create_iv(
0 ignored issues
show
Deprecated Code introduced by
The function mcrypt_create_iv() has been deprecated: 7.1 ( Ignorable by Annotation )

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

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

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

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

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

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

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

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

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

Loading history...
126
                        MCRYPT_RAND
127
                    )
128
                )
129
            )
130
        );
131
    }
132
133
    // If $personalSalt is not empty
134
    return trim(
135
        base64_encode(
136
            mcrypt_encrypt(
1 ignored issue
show
Deprecated Code introduced by
The function mcrypt_encrypt() has been deprecated: 7.1 ( Ignorable by Annotation )

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

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

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

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

Loading history...
137
                MCRYPT_RIJNDAEL_256,
138
                SALT,
139
                $text,
140
                MCRYPT_MODE_ECB,
141
                mcrypt_create_iv(
0 ignored issues
show
Deprecated Code introduced by
The function mcrypt_create_iv() has been deprecated: 7.1 ( Ignorable by Annotation )

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

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

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

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

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

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

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

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

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

Loading history...
143
                    MCRYPT_RAND
144
                )
145
            )
146
        )
147
    );
148
}
149
150
/**
151
 * decryptOld().
152
 *
153
 * decrypt a crypted string
154
 */
155
function decryptOld($text, $personalSalt = '')
156
{
157
    if (!empty($personalSalt)) {
158
        return trim(
159
            mcrypt_decrypt(
0 ignored issues
show
Deprecated Code introduced by
The function mcrypt_decrypt() has been deprecated: 7.1 ( Ignorable by Annotation )

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

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

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

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

Loading history...
160
                MCRYPT_RIJNDAEL_256,
161
                $personalSalt,
162
                base64_decode($text),
163
                MCRYPT_MODE_ECB,
164
                mcrypt_create_iv(
0 ignored issues
show
Deprecated Code introduced by
The function mcrypt_create_iv() has been deprecated: 7.1 ( Ignorable by Annotation )

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

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

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

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

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

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

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

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

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

Loading history...
166
                    MCRYPT_RAND
167
                )
168
            )
169
        );
170
    }
171
172
    // No personal SK
173
    return trim(
174
        mcrypt_decrypt(
0 ignored issues
show
Deprecated Code introduced by
The function mcrypt_decrypt() has been deprecated: 7.1 ( Ignorable by Annotation )

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

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

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

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

Loading history...
175
            MCRYPT_RIJNDAEL_256,
176
            SALT,
177
            base64_decode($text),
178
            MCRYPT_MODE_ECB,
179
            mcrypt_create_iv(
0 ignored issues
show
Deprecated Code introduced by
The function mcrypt_create_iv() has been deprecated: 7.1 ( Ignorable by Annotation )

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Loading history...
Deprecated Code introduced by
The function mcrypt_get_iv_size() has been deprecated: 7.1 ( Ignorable by Annotation )

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

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

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

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

Loading history...
218
219
    //base64 trim
220
    if (strlen($ivBase64 = rtrim(base64_encode($init_vect), '=')) != 43) {
221
        return false;
222
    }
223
    // Encrypt $decrypted
224
    $encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $decrypted, 'ctr', $init_vect);
1 ignored issue
show
Deprecated Code introduced by
The function mcrypt_encrypt() has been deprecated: 7.1 ( Ignorable by Annotation )

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

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

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

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

Loading history...
225
    // MAC the encrypted text
226
    $mac = hash_hmac('sha256', $encrypted, $staticSalt);
227
    // We're done!
228
    return base64_encode($ivBase64.$encrypted.$mac.$pbkdf2Salt);
0 ignored issues
show
Bug introduced by
Are you sure $pbkdf2Salt of type array can be used in concatenation? ( Ignorable by Annotation )

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

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

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

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

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

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

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

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

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

1591
                /** @scrutinizer ignore-type */ $data,
Loading history...
1592
                true
1593
            );
1594
        } else {
1595
            return json_decode(
1596
                Encryption\Crypt\aesctr::decrypt(
1597
                    $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

1597
                    /** @scrutinizer ignore-type */ $data,
Loading history...
1598
                    $_SESSION['key'],
1599
                    256
1600
                ),
1601
                true
1602
            );
1603
        }
1604
    }
1605
}
1606
1607
/**
1608
 * Create a thumbnail.
1609
 *
1610
 * @param string $src           Source
1611
 * @param string $dest          Destination
1612
 * @param float  $desired_width Size of width
1613
 */
1614
function makeThumbnail($src, $dest, $desired_width)
1615
{
1616
    /* read the source image */
1617
    $source_image = imagecreatefrompng($src);
1618
    $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

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

1619
    $height = imagesy(/** @scrutinizer ignore-type */ $source_image);
Loading history...
1620
1621
    /* find the "desired height" of this thumbnail, relative to the desired width  */
1622
    $desired_height = floor($height * ($desired_width / $width));
1623
1624
    /* create a new, "virtual" image */
1625
    $virtual_image = imagecreatetruecolor($desired_width, $desired_height);
0 ignored issues
show
Bug introduced by
$desired_height of type double is incompatible with the type integer expected by parameter $height of imagecreatetruecolor(). ( Ignorable by Annotation )

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

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

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

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

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

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

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

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

1628
    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

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

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

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

2240
                /** @scrutinizer ignore-type */ $SETTINGS
Loading history...
2241
            );
2242
        // ---
2243
        } elseif ($type === 'encrypt') {
2244
            // Encrypt file
2245
            $err = defuseFileEncrypt(
2246
                $source_file,
2247
                $target_file,
2248
                \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

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

2249
                /** @scrutinizer ignore-type */ $SETTINGS
Loading history...
2250
            );
2251
        }
2252
    } else {
2253
        /*
2254
        File encryption/decryption is done with special password and not the SALTKEY
2255
         */
2256
2257
        $err = '';
2258
        if ($type === 'decrypt') {
2259
            // Decrypt file
2260
            $err = defuseFileDecrypt(
2261
                $source_file,
2262
                $target_file,
2263
                $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

2263
                /** @scrutinizer ignore-type */ $password,
Loading history...
2264
                $SETTINGS
2265
            );
2266
        // ---
2267
        } elseif ($type === 'encrypt') {
2268
            // Encrypt file
2269
            $err = defuseFileEncrypt(
2270
                $source_file,
2271
                $target_file,
2272
                $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

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

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

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

2436
    while (false !== ($file = readdir(/** @scrutinizer ignore-type */ $pointer_dir))) {
Loading history...
2437
        if (($file === '.') || ($file === '..')) {
2438
            continue;
2439
        }
2440
2441
        $fullPath = $dir.'/'.$file;
2442
2443
        if (is_dir($fullPath)) {
2444
            if ($res = @chmod($fullPath, $dirPermissions)) {
2445
                $res = @chmodRecursive($fullPath, $dirPermissions, $filePermissions);
2446
            }
2447
        } else {
2448
            $res = chmod($fullPath, $filePermissions);
2449
        }
2450
        if (!$res) {
2451
            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

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

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

2516
        $bytes = openssl_random_pseudo_bytes(/** @scrutinizer ignore-type */ ceil($lenght / 2));
Loading history...
2517
    } else {
2518
        throw new Exception('no cryptographically secure random function available');
2519
    }
2520
2521
    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

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