Passed
Push — teampass_3.0 ( 4471af...0a7f15 )
by Nils
03:21
created

isBase64()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 7
c 1
b 0
f 0
nc 3
nop 1
dl 0
loc 14
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
44
/**
45
 * Convert language code to string.
46
 *
47
 * @param string $string String to get
48
 *
49
 * @return string
50
 */
51
function langHdl($string)
52
{
53
    // Clean the string to convert
54
    $string = trim($string);
55
56
    if (empty($string) === true || isset($_SESSION['teampass']['lang'][$string]) === false) {
57
        // Manage error
58
        return 'ERROR in language strings!';
59
    } else {
60
        return str_replace(
61
            array('"', "'"),
62
            array('&quot;', '&apos;'),
63
            $_SESSION['teampass']['lang'][$string]
64
        );
65
    }
66
}
67
68
//Generate N# of random bits for use as salt
69
/**
70
 * Undocumented function.
71
 *
72
 * @param int $size Length
73
 *
74
 * @return array
75
 */
76
function getBits($size)
77
{
78
    $str = '';
79
    $var_x = $size + 10;
80
    for ($var_i = 0; $var_i < $var_x; ++$var_i) {
81
        $str .= base_convert(mt_rand(1, 36), 10, 36);
82
    }
83
84
    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...
85
}
86
87
//generate pbkdf2 compliant hash
88
function strHashPbkdf2($var_p, $var_s, $var_c, $var_kl, $var_a = 'sha256', $var_st = 0)
89
{
90
    $var_kb = $var_st + $var_kl; // Key blocks to compute
91
    $var_dk = ''; // Derived key
92
93
    for ($block = 1; $block <= $var_kb; ++$block) { // Create key
94
        $var_ib = $var_h = hash_hmac($var_a, $var_s.pack('N', $block), $var_p, true); // Initial hash for this block
95
        for ($var_i = 1; $var_i < $var_c; ++$var_i) { // Perform block iterations
96
            $var_ib ^= ($var_h = hash_hmac($var_a, $var_h, $var_p, true)); // XOR each iterate
97
        }
98
        $var_dk .= $var_ib; // Append iterated block
99
    }
100
101
    return substr($var_dk, $var_st, $var_kl); // Return derived key of correct length
102
}
103
104
/**
105
 * stringUtf8Decode().
106
 *
107
 * utf8_decode
108
 */
109
function stringUtf8Decode($string)
110
{
111
    return str_replace(' ', '+', utf8_decode($string));
112
}
113
114
/**
115
 * encryptOld().
116
 *
117
 * crypt a string
118
 *
119
 * @param string $text
120
 */
121
function encryptOld($text, $personalSalt = '')
122
{
123
    if (empty($personalSalt) === false) {
124
        return trim(
125
            base64_encode(
126
                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

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

131
                    /** @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...
132
                        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

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

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

148
                /** @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...
149
                    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

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

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

171
                /** @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...
172
                    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

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

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

186
            /** @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...
187
                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

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

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

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

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

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

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

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

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

1644
                /** @scrutinizer ignore-type */ $data,
Loading history...
1645
                true
1646
            );
1647
        } else {
1648
            return json_decode(
1649
                Encryption\Crypt\aesctr::decrypt(
1650
                    $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

1650
                    /** @scrutinizer ignore-type */ $data,
Loading history...
1651
                    $_SESSION['key'],
1652
                    256
1653
                ),
1654
                true
1655
            );
1656
        }
1657
    }
1658
}
1659
1660
/**
1661
 * Create a thumbnail.
1662
 *
1663
 * @param string $src           Source
1664
 * @param string $dest          Destination
1665
 * @param float  $desired_width Size of width
1666
 */
1667
function makeThumbnail($src, $dest, $desired_width)
1668
{
1669
    /* read the source image */
1670
    $source_image = imagecreatefrompng($src);
1671
    $width = imagesx($source_image);
1672
    $height = imagesy($source_image);
1673
1674
    /* find the "desired height" of this thumbnail, relative to the desired width  */
1675
    $desired_height = floor($height * ($desired_width / $width));
1676
1677
    /* create a new, "virtual" image */
1678
    $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

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

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

1681
    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

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

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

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

2492
    while (false !== ($file = readdir(/** @scrutinizer ignore-type */ $pointer_dir))) {
Loading history...
2493
        if (($file === '.') || ($file === '..')) {
2494
            continue;
2495
        }
2496
2497
        $fullPath = $dir.'/'.$file;
2498
2499
        if (is_dir($fullPath)) {
2500
            if ($res = @chmod($fullPath, $dirPermissions)) {
2501
                $res = @chmodRecursive($fullPath, $dirPermissions, $filePermissions);
2502
            }
2503
        } else {
2504
            $res = chmod($fullPath, $filePermissions);
2505
        }
2506
        if (!$res) {
2507
            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

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

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

2572
        $bytes = openssl_random_pseudo_bytes(/** @scrutinizer ignore-type */ ceil($lenght / 2));
Loading history...
2573
    } else {
2574
        throw new Exception('no cryptographically secure random function available');
2575
    }
2576
2577
    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

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

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

3275
                'share_key' => encryptUserObjectKey(/** @scrutinizer ignore-type */ $objectKey, $_SESSION['user']['public_key']),
Loading history...
3276
            )
3277
        );
3278
    } else {
3279
        // This is a public object
3280
        // Create sharekey for each user
3281
        $users = DB::query(
3282
            'SELECT id, public_key
3283
            FROM '.prefixTable('users').'
3284
            WHERE id NOT IN ("'.OTV_USER_ID.'","'.SSH_USER_ID.'","'.API_USER_ID.'")
3285
            AND public_key != ""'
3286
        );
3287
        foreach ($users as $user) {
3288
            // Insert in DB the new object key for this item by user
3289
            DB::insert(
3290
                $object_name,
3291
                array(
3292
                    'object_id' => $post_object_id,
3293
                    'user_id' => $user['id'],
3294
                    'share_key' => encryptUserObjectKey(
3295
                        $objectKey,
3296
                        $user['public_key']
3297
                    ),
3298
                )
3299
            );
3300
        }
3301
    }
3302
}
3303
3304
/**
3305
 * Is this string base64 encoded?
3306
 *
3307
 * @param string $str Encoded string?
3308
 *
3309
 * @return bool
3310
 */
3311
function isBase64($str)
3312
{
3313
    $str = (string) trim($str);
3314
3315
    if (!isset($str[0])) {
3316
        return false;
3317
    }
3318
3319
    $base64String = (string) base64_decode($str, true);
3320
    if ($base64String && base64_encode($base64String) === $str) {
3321
        return true;
3322
    }
3323
3324
    return false;
3325
}
3326