Passed
Push — teampass_3.0 ( a6e558...2a77bf )
by Nils
03:33
created

storeUsersShareKey()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 53
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 30
c 1
b 0
f 0
nc 3
nop 6
dl 0
loc 53
rs 9.44

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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

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

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

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

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

Loading history...
125
                    MCRYPT_RIJNDAEL_256,
126
                    $personalSalt,
127
                    $text,
128
                    MCRYPT_MODE_ECB,
129
                    mcrypt_create_iv(
0 ignored issues
show
Deprecated Code introduced by
The function mcrypt_create_iv() has been deprecated: 7.1 ( Ignorable by Annotation )

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Loading history...
142
                MCRYPT_RIJNDAEL_256,
143
                SALT,
144
                $text,
145
                MCRYPT_MODE_ECB,
146
                mcrypt_create_iv(
0 ignored issues
show
Deprecated Code introduced by
The function mcrypt_create_iv() has been deprecated: 7.1 ( Ignorable by Annotation )

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Loading history...
165
                MCRYPT_RIJNDAEL_256,
166
                $personalSalt,
167
                base64_decode($text),
168
                MCRYPT_MODE_ECB,
169
                mcrypt_create_iv(
0 ignored issues
show
Deprecated Code introduced by
The function mcrypt_create_iv() has been deprecated: 7.1 ( Ignorable by Annotation )

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

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

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

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

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

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

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

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

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

Loading history...
171
                    MCRYPT_RAND
172
                )
173
            )
174
        );
175
    }
176
177
    // No personal SK
178
    return trim(
179
        mcrypt_decrypt(
0 ignored issues
show
Deprecated Code introduced by
The function mcrypt_decrypt() has been deprecated: 7.1 ( Ignorable by Annotation )

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

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

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

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

Loading history...
180
            MCRYPT_RIJNDAEL_256,
181
            SALT,
182
            base64_decode($text),
183
            MCRYPT_MODE_ECB,
184
            mcrypt_create_iv(
0 ignored issues
show
Deprecated Code introduced by
The function mcrypt_create_iv() has been deprecated: 7.1 ( Ignorable by Annotation )

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

635
            /** @scrutinizer ignore-type */ $tree
Loading history...
636
        );
637
    } else {
638
        identUser(
639
            $groupesVisiblesUser,
640
            $groupesInterditsUser,
641
            $idFonctions,
642
            $SETTINGS,
643
            $tree
0 ignored issues
show
Bug introduced by
$tree of type Tree\NestedTree\NestedTree is incompatible with the type array expected by parameter $tree of identUser(). ( Ignorable by Annotation )

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

3273
                'share_key' => encryptUserObjectKey(/** @scrutinizer ignore-type */ $objectKey, $_SESSION['user']['public_key']),
Loading history...
3274
            )
3275
        );
3276
    } else {
3277
        // This is a public object
3278
        // Create sharekey for each user
3279
        $users = DB::query(
3280
            'SELECT id, public_key
3281
            FROM '.prefixTable('users').'
3282
            WHERE id NOT IN ("'.OTV_USER_ID.'","'.SSH_USER_ID.'","'.API_USER_ID.'")
3283
            AND public_key != ""'
3284
        );
3285
        foreach ($users as $user) {
3286
            // Insert in DB the new object key for this item by user
3287
            DB::insert(
3288
                $object_name,
3289
                array(
3290
                    'object_id' => $post_object_id,
3291
                    'user_id' => $user['id'],
3292
                    'share_key' => encryptUserObjectKey(
3293
                        $objectKey,
3294
                        $user['public_key']
3295
                    ),
3296
                )
3297
            );
3298
        }
3299
    }
3300
}
3301