Passed
Push — teampass_3.0 ( 416a97...870788 )
by Nils
06:42
created

encryptData()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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

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

129
                    /** @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...
130
                        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

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

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

146
                /** @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...
147
                    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

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

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

169
                /** @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...
170
                    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

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

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

184
            /** @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...
185
                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

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

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

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

229
    $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...
230
    // MAC the encrypted text
231
    $mac = hash_hmac('sha256', $encrypted, $staticSalt);
232
    // We're done!
233
    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

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

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

635
            /** @scrutinizer ignore-type */ $tree
Loading history...
636
        );
637
    } else {
638
        identUser(
639
            $groupesVisiblesUser,
640
            $groupesInterditsUser,
641
            $idFonctions,
642
            $SETTINGS,
643
            $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

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

1628
                /** @scrutinizer ignore-type */ $data,
Loading history...
1629
                true
1630
            );
1631
        } else {
1632
            return json_decode(
1633
                Encryption\Crypt\aesctr::decrypt(
1634
                    $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

1634
                    /** @scrutinizer ignore-type */ $data,
Loading history...
1635
                    $_SESSION['key'],
1636
                    256
1637
                ),
1638
                true
1639
            );
1640
        }
1641
    }
1642
}
1643
1644
/**
1645
 * Create a thumbnail.
1646
 *
1647
 * @param string $src           Source
1648
 * @param string $dest          Destination
1649
 * @param float  $desired_width Size of width
1650
 */
1651
function makeThumbnail($src, $dest, $desired_width)
1652
{
1653
    /* read the source image */
1654
    $source_image = imagecreatefrompng($src);
1655
    $width = imagesx($source_image);
1656
    $height = imagesy($source_image);
1657
1658
    /* find the "desired height" of this thumbnail, relative to the desired width  */
1659
    $desired_height = floor($height * ($desired_width / $width));
1660
1661
    /* create a new, "virtual" image */
1662
    $virtual_image = imagecreatetruecolor($desired_width, $desired_height);
0 ignored issues
show
Bug introduced by
$desired_width of type double is incompatible with the type integer expected by parameter $width of imagecreatetruecolor(). ( Ignorable by Annotation )

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

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

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

1662
    $virtual_image = imagecreatetruecolor($desired_width, /** @scrutinizer ignore-type */ $desired_height);
Loading history...
1663
1664
    /* copy source image at a resized size */
1665
    imagecopyresampled($virtual_image, $source_image, 0, 0, 0, 0, $desired_width, $desired_height, $width, $height);
0 ignored issues
show
Bug introduced by
$desired_height of type double is incompatible with the type integer expected by parameter $dst_h of imagecopyresampled(). ( Ignorable by Annotation )

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

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

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

1665
    imagecopyresampled($virtual_image, $source_image, 0, 0, 0, 0, /** @scrutinizer ignore-type */ $desired_width, $desired_height, $width, $height);
Loading history...
1666
1667
    /* create the physical thumbnail image to its destination */
1668
    imagejpeg($virtual_image, $dest);
1669
}
1670
1671
/**
1672
 * Check table prefix in SQL query.
1673
 *
1674
 * @param string $table Table name
1675
 *
1676
 * @return string
1677
 */
1678
function prefixTable($table)
1679
{
1680
    $safeTable = htmlspecialchars(DB_PREFIX.$table);
1681
    if (!empty($safeTable)) {
1682
        // sanitize string
1683
        return $safeTable;
1684
    } else {
1685
        // stop error no table
1686
        return 'table_not_exists';
1687
    }
1688
}
1689
1690
/*
1691
 * Creates a KEY using PasswordLib
1692
 */
1693
function GenerateCryptKey($size = null, $secure = false, $numerals = false, $capitalize = false, $symbols = false)
1694
{
1695
    global $SETTINGS;
1696
    include_once $SETTINGS['cpassman_dir'].'/sources/SplClassLoader.php';
1697
1698
    if ($secure === true) {
1699
        $numerals = true;
1700
        $capitalize = true;
1701
        $symbols = true;
1702
    }
1703
1704
    // Load libraries
1705
    $generator = new SplClassLoader('PasswordGenerator\Generator', '../includes/libraries');
1706
    $generator->register();
1707
    $generator = new PasswordGenerator\Generator\ComputerPasswordGenerator();
1708
1709
    // Can we use PHP7 random_int function?
1710
    /*if (version_compare(phpversion(), '7.0', '>=')) {
1711
        include_once $SETTINGS['cpassman_dir'].'/includes/libraries/PasswordGenerator/RandomGenerator/Php7RandomGenerator.php';
1712
        $generator->setRandomGenerator(new PasswordGenerator\RandomGenerator\Php7RandomGenerator());
1713
    }*/
1714
1715
    // init
1716
    if (empty($size) === false && is_null($size) === false) {
1717
        $generator->setLength(intval($size));
1718
    }
1719
    if (empty($numerals) === false) {
1720
        $generator->setNumbers($numerals);
1721
    }
1722
    if (empty($capitalize) === false) {
1723
        $generator->setUppercase($capitalize);
1724
    }
1725
    if (empty($symbols) === false) {
1726
        $generator->setSymbols($symbols);
1727
    }
1728
1729
    // generate and send back
1730
    return $generator->generatePassword();
1731
}
1732
1733
/*
1734
* Send sysLOG message
1735
* @param string $message
1736
* @param string $host
1737
*/
1738
function send_syslog($message, $host, $port, $component = 'teampass')
1739
{
1740
    $sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
1741
    $syslog_message = '<123>'.date('M d H:i:s ').$component.': '.$message;
1742
    socket_sendto($sock, $syslog_message, strlen($syslog_message), 0, $host, $port);
1743
    socket_close($sock);
1744
}
1745
1746
/**
1747
 * logEvents().
1748
 *
1749
 * permits to log events into DB
1750
 *
1751
 * @param string $type
1752
 * @param string $label
1753
 * @param string $field_1
1754
 */
1755
function logEvents($type, $label, $who, $login = null, $field_1 = null)
1756
{
1757
    global $server, $user, $pass, $database, $port, $encoding;
1758
    global $SETTINGS;
1759
1760
    if (empty($who)) {
1761
        $who = getClientIpServer();
1762
    }
1763
1764
    // include librairies & connect to DB
1765
    require_once $SETTINGS['cpassman_dir'].'/includes/libraries/Database/Meekrodb/db.class.php';
1766
    DB::$host = DB_HOST;
1767
    DB::$user = DB_USER;
1768
    DB::$password = defuseReturnDecrypted(DB_PASSWD, $SETTINGS);
1769
    DB::$dbName = DB_NAME;
1770
    DB::$port = DB_PORT;
1771
    DB::$encoding = DB_ENCODING;
1772
    //DB::$errorHandler = true;
1773
1774
    $link = mysqli_connect(DB_HOST, DB_USER, defuseReturnDecrypted(DB_PASSWD, $SETTINGS), DB_NAME, DB_PORT);
1775
    $link->set_charset(DB_ENCODING);
1776
1777
    DB::insert(
1778
        prefixTable('log_system'),
1779
        array(
1780
            'type' => $type,
1781
            'date' => time(),
1782
            'label' => $label,
1783
            'qui' => $who,
1784
            'field_1' => $field_1 === null ? '' : $field_1,
1785
        )
1786
    );
1787
1788
    // If SYSLOG
1789
    if (isset($SETTINGS['syslog_enable']) === true && (int) $SETTINGS['syslog_enable'] === 1) {
1790
        if ($type === 'user_mngt') {
1791
            send_syslog(
1792
                'action='.str_replace('at_', '', $label).' attribute=user user='.$who.' userid="'.$login.'" change="'.$field_1.'" ',
1793
                $SETTINGS['syslog_host'],
1794
                $SETTINGS['syslog_port'],
1795
                'teampass'
1796
            );
1797
        } else {
1798
            send_syslog(
1799
                'action='.$type.' attribute='.$label.' user='.$who.' userid="'.$login.'" ',
1800
                $SETTINGS['syslog_host'],
1801
                $SETTINGS['syslog_port'],
1802
                'teampass'
1803
            );
1804
        }
1805
    }
1806
}
1807
1808
/**
1809
 * Log events.
1810
 *
1811
 * @param array  $SETTINGS        Teampass settings
1812
 * @param int    $item_id         Item id
1813
 * @param string $item_label      Item label
1814
 * @param int    $id_user         User id
1815
 * @param string $action          Code for reason
1816
 * @param string $login           User login
1817
 * @param string $raison          Code for reason
1818
 * @param string $encryption_type Encryption on
1819
 */
1820
function logItems(
1821
    $SETTINGS,
1822
    $item_id,
1823
    $item_label,
1824
    $id_user,
1825
    $action,
1826
    $login = null,
1827
    $raison = null,
1828
    $encryption_type = null
1829
) {
1830
    $dataItem = '';
1831
1832
    // include librairies & connect to DB
1833
    include_once $SETTINGS['cpassman_dir'].'/includes/libraries/Database/Meekrodb/db.class.php';
1834
    DB::$host = DB_HOST;
1835
    DB::$user = DB_USER;
1836
    DB::$password = defuseReturnDecrypted(DB_PASSWD, $SETTINGS);
1837
    DB::$dbName = DB_NAME;
1838
    DB::$port = DB_PORT;
1839
    DB::$encoding = DB_ENCODING;
1840
    $link = mysqli_connect(DB_HOST, DB_USER, defuseReturnDecrypted(DB_PASSWD, $SETTINGS), DB_NAME, DB_PORT);
1841
    $link->set_charset(DB_ENCODING);
1842
1843
    // Insert log in DB
1844
    DB::insert(
1845
        prefixTable('log_items'),
1846
        array(
1847
            'id_item' => $item_id,
1848
            'date' => time(),
1849
            'id_user' => $id_user,
1850
            'action' => $action,
1851
            'raison' => $raison,
1852
            'raison_iv' => '',
1853
            'encryption_type' => is_null($encryption_type) === true ? '' : $encryption_type,
1854
        )
1855
    );
1856
    // Timestamp the last change
1857
    if ($action === 'at_creation' || $action === 'at_modifiation' || $action === 'at_delete' || $action === 'at_import') {
1858
        DB::update(
1859
            prefixTable('misc'),
1860
            array(
1861
                'valeur' => time(),
1862
                ),
1863
            'type = %s AND intitule = %s',
1864
            'timestamp',
1865
            'last_item_change'
1866
        );
1867
    }
1868
1869
    // SYSLOG
1870
    if (isset($SETTINGS['syslog_enable']) === true && $SETTINGS['syslog_enable'] === '1') {
1871
        // Extract reason
1872
        $attribute = explode(' : ', $raison);
1873
1874
        // Get item info if not known
1875
        if (empty($item_label) === true) {
1876
            $dataItem = DB::queryfirstrow(
1877
                'SELECT id, id_tree, label
1878
                FROM '.prefixTable('items').'
1879
                WHERE id = %i',
1880
                $item_id
1881
            );
1882
1883
            $item_label = $dataItem['label'];
1884
        }
1885
1886
        send_syslog(
1887
            'action='.str_replace('at_', '', $action).' attribute='.str_replace('at_', '', $attribute[0]).' itemno='.$item_id.' user='.addslashes($login).' itemname="'.addslashes($item_label).'"',
1888
            $SETTINGS['syslog_host'],
1889
            $SETTINGS['syslog_port'],
1890
            'teampass'
1891
        );
1892
    }
1893
1894
    // send notification if enabled
1895
    if (isset($SETTINGS['enable_email_notification_on_item_shown']) === true
1896
        && $SETTINGS['enable_email_notification_on_item_shown'] === '1'
1897
        && $action === 'at_shown'
1898
    ) {
1899
        // Get info about item
1900
        if (empty($dataItem) === true || empty($item_label) === true) {
1901
            $dataItem = DB::queryfirstrow(
1902
                'SELECT id, id_tree, label
1903
                FROM '.prefixTable('items').'
1904
                WHERE id = %i',
1905
                $item_id
1906
            );
1907
            $item_label = $dataItem['label'];
1908
        }
1909
1910
        // send back infos
1911
        DB::insert(
1912
            prefixTable('emails'),
1913
            array(
1914
                'timestamp' => time(),
1915
                'subject' => langHdl('email_on_open_notification_subject'),
1916
                'body' => str_replace(
1917
                    array('#tp_user#', '#tp_item#', '#tp_link#'),
1918
                    array(
1919
                        addslashes($_SESSION['login']),
1920
                        addslashes($item_label),
1921
                        $SETTINGS['cpassman_url'].'/index.php?page=items&group='.$dataItem['id_tree'].'&id='.$dataItem['id'],
1922
                    ),
1923
                    langHdl('email_on_open_notification_mail')
1924
                ),
1925
                'receivers' => $_SESSION['listNotificationEmails'],
1926
                'status' => '',
1927
            )
1928
        );
1929
    }
1930
}
1931
1932
/**
1933
 * Get the client ip address.
1934
 *
1935
 * @return string IP address
1936
 */
1937
function getClientIpServer()
1938
{
1939
    if (getenv('HTTP_CLIENT_IP')) {
1940
        $ipaddress = getenv('HTTP_CLIENT_IP');
1941
    } elseif (getenv('HTTP_X_FORWARDED_FOR')) {
1942
        $ipaddress = getenv('HTTP_X_FORWARDED_FOR');
1943
    } elseif (getenv('HTTP_X_FORWARDED')) {
1944
        $ipaddress = getenv('HTTP_X_FORWARDED');
1945
    } elseif (getenv('HTTP_FORWARDED_FOR')) {
1946
        $ipaddress = getenv('HTTP_FORWARDED_FOR');
1947
    } elseif (getenv('HTTP_FORWARDED')) {
1948
        $ipaddress = getenv('HTTP_FORWARDED');
1949
    } elseif (getenv('REMOTE_ADDR')) {
1950
        $ipaddress = getenv('REMOTE_ADDR');
1951
    } else {
1952
        $ipaddress = 'UNKNOWN';
1953
    }
1954
1955
    return $ipaddress;
1956
}
1957
1958
/**
1959
 * Escape all HTML, JavaScript, and CSS.
1960
 *
1961
 * @param string $input    The input string
1962
 * @param string $encoding Which character encoding are we using?
1963
 *
1964
 * @return string
1965
 */
1966
function noHTML($input, $encoding = 'UTF-8')
1967
{
1968
    return htmlspecialchars($input, ENT_QUOTES | ENT_XHTML, $encoding, false);
1969
}
1970
1971
/**
1972
 * handleConfigFile().
1973
 *
1974
 * permits to handle the Teampass config file
1975
 * $action accepts "rebuild" and "update"
1976
 */
1977
function handleConfigFile($action, $field = null, $value = null)
1978
{
1979
    global $server, $user, $pass, $database, $port, $encoding;
1980
    global $SETTINGS;
1981
1982
    $tp_config_file = '../includes/config/tp.config.php';
1983
1984
    // include librairies & connect to DB
1985
    include_once $SETTINGS['cpassman_dir'].'/includes/libraries/Database/Meekrodb/db.class.php';
1986
    DB::$host = DB_HOST;
1987
    DB::$user = DB_USER;
1988
    DB::$password = defuseReturnDecrypted(DB_PASSWD, $SETTINGS);
1989
    DB::$dbName = DB_NAME;
1990
    DB::$port = DB_PORT;
1991
    DB::$encoding = DB_ENCODING;
1992
    //DB::$errorHandler = true;
1993
1994
    $link = mysqli_connect(DB_HOST, DB_USER, defuseReturnDecrypted(DB_PASSWD, $SETTINGS), DB_NAME, DB_PORT);
1995
    $link->set_charset(DB_ENCODING);
1996
1997
    if (file_exists($tp_config_file) === false || $action === 'rebuild') {
1998
        // perform a copy
1999
        if (file_exists($tp_config_file)) {
2000
            if (!copy($tp_config_file, $tp_config_file.'.'.date('Y_m_d_His', time()))) {
2001
                return "ERROR: Could not copy file '".$tp_config_file."'";
2002
            }
2003
        }
2004
2005
        // regenerate
2006
        $data = array();
2007
        $data[0] = "<?php\n";
2008
        $data[1] = "global \$SETTINGS;\n";
2009
        $data[2] = "\$SETTINGS = array (\n";
2010
        $rows = DB::query(
2011
            'SELECT * FROM '.prefixTable('misc').' WHERE type=%s',
2012
            'admin'
2013
        );
2014
        foreach ($rows as $record) {
2015
            array_push($data, "    '".$record['intitule']."' => '".$record['valeur']."',\n");
2016
        }
2017
        array_push($data, ");\n");
2018
        $data = array_unique($data);
2019
    } elseif ($action === 'update' && empty($field) === false) {
2020
        $data = file($tp_config_file);
2021
        $inc = 0;
2022
        $bFound = false;
2023
        foreach ($data as $line) {
2024
            if (stristr($line, ');')) {
2025
                break;
2026
            }
2027
2028
            if (stristr($line, "'".$field."' => '")) {
2029
                $data[$inc] = "    '".$field."' => '".filter_var($value, FILTER_SANITIZE_STRING)."',\n";
2030
                $bFound = true;
2031
                break;
2032
            }
2033
            ++$inc;
2034
        }
2035
        if ($bFound === false) {
2036
            $data[($inc)] = "    '".$field."' => '".filter_var($value, FILTER_SANITIZE_STRING)."',\n);\n";
2037
        }
2038
    }
2039
2040
    // update file
2041
    file_put_contents($tp_config_file, implode('', isset($data) ? $data : array()));
2042
2043
    return true;
2044
}
2045
2046
/*
2047
** Permits to replace &#92; to permit correct display
2048
*/
2049
/**
2050
 * @param string $input
2051
 */
2052
function handleBackslash($input)
2053
{
2054
    return str_replace('&amp;#92;', '&#92;', $input);
2055
}
2056
2057
/*
2058
** Permits to loas settings
2059
*/
2060
function loadSettings()
2061
{
2062
    global $SETTINGS;
2063
2064
    /* LOAD CPASSMAN SETTINGS */
2065
    if (!isset($SETTINGS['loaded']) || $SETTINGS['loaded'] != 1) {
2066
        $SETTINGS['duplicate_folder'] = 0; //by default, this is set to 0;
2067
        $SETTINGS['duplicate_item'] = 0; //by default, this is set to 0;
2068
        $SETTINGS['number_of_used_pw'] = 5; //by default, this value is set to 5;
2069
        $settings = array();
2070
2071
        $rows = DB::query(
2072
            'SELECT * FROM '.prefixTable('misc').' WHERE type=%s_type OR type=%s_type2',
2073
            array(
2074
                'type' => 'admin',
2075
                'type2' => 'settings',
2076
            )
2077
        );
2078
        foreach ($rows as $record) {
2079
            if ($record['type'] === 'admin') {
2080
                $SETTINGS[$record['intitule']] = $record['valeur'];
2081
            } else {
2082
                $settings[$record['intitule']] = $record['valeur'];
2083
            }
2084
        }
2085
        $SETTINGS['loaded'] = 1;
2086
        $SETTINGS['default_session_expiration_time'] = 5;
2087
    }
2088
}
2089
2090
/*
2091
** check if folder has custom fields.
2092
** Ensure that target one also has same custom fields
2093
*/
2094
function checkCFconsistency($source_id, $target_id)
2095
{
2096
    $source_cf = array();
2097
    $rows = DB::QUERY(
2098
        'SELECT id_category
2099
        FROM '.prefixTable('categories_folders').'
2100
        WHERE id_folder = %i',
2101
        $source_id
2102
    );
2103
    foreach ($rows as $record) {
2104
        array_push($source_cf, $record['id_category']);
2105
    }
2106
2107
    $target_cf = array();
2108
    $rows = DB::QUERY(
2109
        'SELECT id_category
2110
        FROM '.prefixTable('categories_folders').'
2111
        WHERE id_folder = %i',
2112
        $target_id
2113
    );
2114
    foreach ($rows as $record) {
2115
        array_push($target_cf, $record['id_category']);
2116
    }
2117
2118
    $cf_diff = array_diff($source_cf, $target_cf);
2119
    if (count($cf_diff) > 0) {
2120
        return false;
2121
    }
2122
2123
    return true;
2124
}
2125
2126
/**
2127
 * Shall we crypt/decrypt.
2128
 *
2129
 * @param string $filename_to_rework File name
2130
 * @param string $filename_status    Its status
2131
 * @param array  $SETTINGS           Settings
2132
 */
2133
function encryptOrDecryptFile(
2134
    $filename_to_rework,
2135
    $filename_status,
2136
    $SETTINGS
2137
) {
2138
    // Include librairies & connect to DB
2139
    include_once $SETTINGS['cpassman_dir'].'/includes/libraries/Database/Meekrodb/db.class.php';
2140
    DB::$host = DB_HOST;
2141
    DB::$user = DB_USER;
2142
    DB::$password = defuseReturnDecrypted(DB_PASSWD, $SETTINGS);
2143
    DB::$dbName = DB_NAME;
2144
    DB::$port = DB_PORT;
2145
    DB::$encoding = DB_ENCODING;
2146
    $link = mysqli_connect(DB_HOST, DB_USER, defuseReturnDecrypted(DB_PASSWD, $SETTINGS), DB_NAME, DB_PORT);
2147
    $link->set_charset(DB_ENCODING);
2148
2149
    // Get file info in DB
2150
    $fileInfo = DB::queryfirstrow(
2151
        'SELECT id FROM '.prefixTable('files').' WHERE file = %s',
2152
        filter_var($filename_to_rework, FILTER_SANITIZE_STRING)
2153
    );
2154
    if (empty($fileInfo['id']) === false) {
2155
        // Load PhpEncryption library
2156
        $path_to_encryption = '/includes/libraries/Encryption/Encryption/';
2157
        include_once $SETTINGS['cpassman_dir'].$path_to_encryption.'Crypto.php';
2158
        include_once $SETTINGS['cpassman_dir'].$path_to_encryption.'Encoding.php';
2159
        include_once $SETTINGS['cpassman_dir'].$path_to_encryption.'DerivedKeys.php';
2160
        include_once $SETTINGS['cpassman_dir'].$path_to_encryption.'Key.php';
2161
        include_once $SETTINGS['cpassman_dir'].$path_to_encryption.'KeyOrPassword.php';
2162
        include_once $SETTINGS['cpassman_dir'].$path_to_encryption.'File.php';
2163
        include_once $SETTINGS['cpassman_dir'].$path_to_encryption.'RuntimeTests.php';
2164
        include_once $SETTINGS['cpassman_dir'].$path_to_encryption.'KeyProtectedByPassword.php';
2165
        include_once $SETTINGS['cpassman_dir'].$path_to_encryption.'Core.php';
2166
2167
        // Get KEY
2168
        $ascii_key = file_get_contents(SECUREPATH.'/teampass-seckey.txt');
2169
2170
        if (isset($SETTINGS['enable_attachment_encryption'])
2171
            && $SETTINGS['enable_attachment_encryption'] === '1'
2172
            && isset($filename_status) === true
2173
            && ($filename_status === 'clear' || $filename_status === '0')
2174
        ) {
2175
            // File needs to be encrypted
2176
            if (file_exists($SETTINGS['path_to_upload_folder'].'/'.$filename_to_rework) === true) {
2177
                // Make a copy of file
2178
                if (copy(
2179
                    $SETTINGS['path_to_upload_folder'].'/'.$filename_to_rework,
2180
                    $SETTINGS['path_to_upload_folder'].'/'.$filename_to_rework.'.copy'
2181
                )
2182
                    === false
2183
                ) {
2184
                    return;
2185
                } else {
2186
                    // Do a bck
2187
                    copy(
2188
                        $SETTINGS['path_to_upload_folder'].'/'.$filename_to_rework,
2189
                        $SETTINGS['path_to_upload_folder'].'/'.$filename_to_rework.'.bck'
2190
                    );
2191
                }
2192
2193
                unlink($SETTINGS['path_to_upload_folder'].'/'.$filename_to_rework);
2194
2195
                // Now encrypt the file with saltkey
2196
                $err = '';
2197
                try {
2198
                    \Defuse\Crypto\File::encryptFile(
2199
                        $SETTINGS['path_to_upload_folder'].'/'.$filename_to_rework.'.copy',
2200
                        $SETTINGS['path_to_upload_folder'].'/'.$filename_to_rework,
2201
                        \Defuse\Crypto\Key::loadFromAsciiSafeString($ascii_key)
2202
                    );
2203
                } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
2204
                    $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.';
2205
                } catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) {
2206
                    $err = $ex;
2207
                } catch (Defuse\Crypto\Exception\IOException $ex) {
2208
                    $err = $ex;
2209
                }
2210
                if (empty($err) === false) {
2211
                    echo $err;
2212
                }
2213
2214
                unlink($SETTINGS['path_to_upload_folder'].'/'.$filename_to_rework.'.copy');
2215
2216
                // update table
2217
                DB::update(
2218
                    prefixTable('files'),
2219
                    array(
2220
                        'status' => 'encrypted',
2221
                        ),
2222
                    'id = %i',
2223
                    $fileInfo['id']
2224
                );
2225
            }
2226
        } elseif (isset($SETTINGS['enable_attachment_encryption'])
2227
            && $SETTINGS['enable_attachment_encryption'] === '0'
2228
            && isset($filename_status)
2229
            && $filename_status === 'encrypted'
2230
        ) {
2231
            // file needs to be decrypted
2232
            if (file_exists($SETTINGS['path_to_upload_folder'].'/'.$filename_to_rework)) {
2233
                // make a copy of file
2234
                if (!copy(
2235
                    $SETTINGS['path_to_upload_folder'].'/'.$filename_to_rework,
2236
                    $SETTINGS['path_to_upload_folder'].'/'.$filename_to_rework.'.copy'
2237
                )) {
2238
                    return;
2239
                } else {
2240
                    // do a bck
2241
                    copy(
2242
                        $SETTINGS['path_to_upload_folder'].'/'.$filename_to_rework,
2243
                        $SETTINGS['path_to_upload_folder'].'/'.$filename_to_rework.'.bck'
2244
                    );
2245
                }
2246
2247
                unlink($SETTINGS['path_to_upload_folder'].'/'.$filename_to_rework);
2248
2249
                // Now encrypt the file with saltkey
2250
                $err = '';
2251
                try {
2252
                    \Defuse\Crypto\File::decryptFile(
2253
                        $SETTINGS['path_to_upload_folder'].'/'.$filename_to_rework.'.copy',
2254
                        $SETTINGS['path_to_upload_folder'].'/'.$filename_to_rework,
2255
                        \Defuse\Crypto\Key::loadFromAsciiSafeString($ascii_key)
2256
                    );
2257
                } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
2258
                    $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.';
2259
                } catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) {
2260
                    $err = $ex;
2261
                } catch (Defuse\Crypto\Exception\IOException $ex) {
2262
                    $err = $ex;
2263
                }
2264
                if (empty($err) === false) {
2265
                    echo $err;
2266
                }
2267
2268
                unlink($SETTINGS['path_to_upload_folder'].'/'.$filename_to_rework.'.copy');
2269
2270
                // update table
2271
                DB::update(
2272
                    prefixTable('files'),
2273
                    array(
2274
                        'status' => 'clear',
2275
                        ),
2276
                    'id = %i',
2277
                    $fileInfo['id']
2278
                );
2279
            }
2280
        }
2281
    }
2282
2283
    // Exit
2284
    return false;
2285
}
2286
2287
/**
2288
 * Will encrypte/decrypt a fil eusing Defuse.
2289
 *
2290
 * @param string $type        can be either encrypt or decrypt
2291
 * @param string $source_file path to source file
2292
 * @param string $target_file path to target file
2293
 * @param array  $SETTINGS    Settings
2294
 * @param string $password    A password
2295
 *
2296
 * @return string|bool
2297
 */
2298
function prepareFileWithDefuse(
2299
    $type,
2300
    $source_file,
2301
    $target_file,
2302
    $SETTINGS,
2303
    $password = null
2304
) {
2305
    // Load AntiXSS
2306
    include_once $SETTINGS['cpassman_dir'].'/includes/libraries/protect/AntiXSS/AntiXSS.php';
2307
    $antiXss = new protect\AntiXSS\AntiXSS();
2308
2309
    // Protect against bad inputs
2310
    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...
2311
        return 'error_cannot_be_array';
2312
    }
2313
2314
    // Sanitize
2315
    $source_file = $antiXss->xss_clean($source_file);
2316
    $target_file = $antiXss->xss_clean($target_file);
2317
2318
    // load PhpEncryption library
2319
    $path_to_encryption = '/includes/libraries/Encryption/Encryption/';
2320
    include_once $SETTINGS['cpassman_dir'].$path_to_encryption.'Crypto.php';
2321
    include_once $SETTINGS['cpassman_dir'].$path_to_encryption.'Encoding.php';
2322
    include_once $SETTINGS['cpassman_dir'].$path_to_encryption.'DerivedKeys.php';
2323
    include_once $SETTINGS['cpassman_dir'].$path_to_encryption.'Key.php';
2324
    include_once $SETTINGS['cpassman_dir'].$path_to_encryption.'KeyOrPassword.php';
2325
    include_once $SETTINGS['cpassman_dir'].$path_to_encryption.'File.php';
2326
    include_once $SETTINGS['cpassman_dir'].$path_to_encryption.'RuntimeTests.php';
2327
    include_once $SETTINGS['cpassman_dir'].$path_to_encryption.'KeyProtectedByPassword.php';
2328
    include_once $SETTINGS['cpassman_dir'].$path_to_encryption.'Core.php';
2329
2330
    if (empty($password) === true || is_null($password) === true) {
2331
        /*
2332
        File encryption/decryption is done with the SALTKEY
2333
         */
2334
2335
        // get KEY
2336
        $ascii_key = file_get_contents(SECUREPATH.'/teampass-seckey.txt');
2337
2338
        // Now perform action on the file
2339
        $err = '';
2340
        if ($type === 'decrypt') {
2341
            try {
2342
                \Defuse\Crypto\File::decryptFile(
2343
                    $source_file,
2344
                    $target_file,
2345
                    \Defuse\Crypto\Key::loadFromAsciiSafeString($ascii_key)
2346
                );
2347
            } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
2348
                $err = 'decryption_not_possible';
2349
            } catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) {
2350
                $err = $ex;
2351
            } catch (Defuse\Crypto\Exception\IOException $ex) {
2352
                $err = $ex;
2353
            }
2354
        } elseif ($type === 'encrypt') {
2355
            try {
2356
                \Defuse\Crypto\File::encryptFile(
2357
                    $source_file,
2358
                    $target_file,
2359
                    \Defuse\Crypto\Key::loadFromAsciiSafeString($ascii_key)
2360
                );
2361
            } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
2362
                $err = 'encryption_not_possible';
2363
            } catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) {
2364
                $err = $ex;
2365
            } catch (Defuse\Crypto\Exception\IOException $ex) {
2366
                $err = $ex;
2367
            }
2368
        }
2369
    } else {
2370
        /*
2371
        File encryption/decryption is done with special password and not the SALTKEY
2372
         */
2373
2374
        $err = '';
2375
        if ($type === 'decrypt') {
2376
            try {
2377
                \Defuse\Crypto\File::decryptFileWithPassword(
2378
                    $source_file,
2379
                    $target_file,
2380
                    $password
2381
                );
2382
            } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
2383
                $err = 'wrong_key';
2384
            } catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) {
2385
                $err = $ex;
2386
            } catch (Defuse\Crypto\Exception\IOException $ex) {
2387
                $err = $ex;
2388
            }
2389
        } elseif ($type === 'encrypt') {
2390
            try {
2391
                \Defuse\Crypto\File::encryptFileWithPassword(
2392
                    $source_file,
2393
                    $target_file,
2394
                    $password
2395
                );
2396
            } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
2397
                $err = 'wrong_key';
2398
            } catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) {
2399
                $err = $ex;
2400
            } catch (Defuse\Crypto\Exception\IOException $ex) {
2401
                $err = $ex;
2402
            }
2403
        }
2404
    }
2405
2406
    // return error
2407
    if (empty($err) === false) {
2408
        return $err;
2409
    } else {
2410
        return true;
2411
    }
2412
}
2413
2414
/*
2415
* NOT TO BE USED
2416
*/
2417
/**
2418
 * Undocumented function.
2419
 *
2420
 * @param string $text Text to debug
2421
 */
2422
function debugTeampass($text)
2423
{
2424
    $debugFile = fopen('D:/wamp64/www/TeamPass/debug.txt', 'r+');
2425
    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

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

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

2476
    while (false !== ($file = readdir(/** @scrutinizer ignore-type */ $pointer_dir))) {
Loading history...
2477
        if (($file === '.') || ($file === '..')) {
2478
            continue;
2479
        }
2480
2481
        $fullPath = $dir.'/'.$file;
2482
2483
        if (is_dir($fullPath)) {
2484
            if ($res = @chmod($fullPath, $dirPermissions)) {
2485
                $res = @chmodRecursive($fullPath, $dirPermissions, $filePermissions);
2486
            }
2487
        } else {
2488
            $res = chmod($fullPath, $filePermissions);
2489
        }
2490
        if (!$res) {
2491
            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

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

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

2556
        $bytes = openssl_random_pseudo_bytes(/** @scrutinizer ignore-type */ ceil($lenght / 2));
Loading history...
2557
    } else {
2558
        throw new Exception('no cryptographically secure random function available');
2559
    }
2560
2561
    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

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

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

3096
     $aes = /** @scrutinizer ignore-call */ new \phpseclib\Crypt\AES();

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
3097
     $aes->setKey($this->key);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $this seems to be never defined.
Loading history...
3098
     $ciphertext = file_get_contents($this->getFileUploadDir() . '/' . $this->file_name);
3099
     $plaintext = $aes->decrypt($ciphertext);
3100
     $hash = md5($plaintext);
3101
     $this->save_name = "DecryptedFile_" . $hash;
3102
     file_put_contents($this->getFileRootDir() . '/' . $this->save_name, $plaintext);
3103
     unlink($this->file->getPathname());
3104
     return new CryptoFile($hash, $this->getWebPath() . '/' . $this->save_name);
0 ignored issues
show
Bug introduced by
The type CryptoFile was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
3105
 }
3106
3107
 /**
3108
  * Encrypts a file
3109
  *
3110
  * @param string $fileInName File name
3111
  * @param string $fileInPath Path to file
3112
  *
3113
  * @return array
3114
  */
3115
function encryptFile($fileInName, $fileInPath)
3116
{
3117
    define('FILE_BUFFER_SIZE', 128 * 1024);
3118
3119
    // Includes
3120
    include_once '../includes/config/include.php';
3121
    include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php';
3122
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php';
3123
3124
    // Load classes
3125
    $cipher = new Crypt_AES(CRYPT_AES_MODE_CBC);
3126
3127
    // Generate an object key
3128
    $objectKey = uniqidReal(32);
3129
3130
    // Set it as password
3131
    $cipher->setPassword($objectKey);
3132
3133
    // Prevent against out of memory
3134
    $cipher->enableContinuousBuffer();
3135
    $cipher->disablePadding();
3136
3137
    // Encrypt the file content
3138
    $plaintext = file_get_contents($fileInPath . '/' . $fileInName);
3139
    $ciphertext = $cipher->encrypt($plaintext);
3140
3141
    // Save new file
3142
    $hash = md5($plaintext);
3143
    $fileOut = $fileInPath . '/' . TP_FILE_PREFIX . $hash;
3144
    file_put_contents($fileOut, $ciphertext);
3145
    unlink($fileInPath . '/' . $fileInName);
3146
3147
    return array(
3148
        'fileHash' => base64_encode($hash),
3149
        'objectKey' => base64_encode($objectKey),
3150
    );
3151
}
3152