Passed
Push — teampass_3.0 ( a4c55d...9035f8 )
by Nils
06:20
created

decryptPrivateKey()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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

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

133
                    /** @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...
134
                        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

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

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

150
                /** @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...
151
                    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

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

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

173
                /** @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...
174
                    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

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

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

188
            /** @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...
189
                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

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

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

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

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

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

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

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

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

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

1638
                    /** @scrutinizer ignore-type */ $data,
Loading history...
1639
                    $_SESSION['key'],
1640
                    256
1641
                ),
1642
                true
1643
            );
1644
        }
1645
    }
1646
}
1647
1648
/**
1649
 * Create a thumbnail.
1650
 *
1651
 * @param string $src           Source
1652
 * @param string $dest          Destination
1653
 * @param float  $desired_width Size of width
1654
 */
1655
function makeThumbnail($src, $dest, $desired_width)
1656
{
1657
    /* read the source image */
1658
    $source_image = imagecreatefrompng($src);
1659
    $width = imagesx($source_image);
1660
    $height = imagesy($source_image);
1661
1662
    /* find the "desired height" of this thumbnail, relative to the desired width  */
1663
    $desired_height = floor($height * ($desired_width / $width));
1664
1665
    /* create a new, "virtual" image */
1666
    $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

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

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

1669
    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

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

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

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

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

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

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

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

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