Passed
Push — teampass_3.0 ( 7e11bc...8b1532 )
by Nils
03:57
created

cacheTableAdd()   D

Complexity

Conditions 18
Paths 30

Size

Total Lines 70
Code Lines 48

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 18
eloc 48
c 1
b 0
f 0
nc 30
nop 2
dl 0
loc 70
rs 4.8666

How to fix   Long Method    Complexity   

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

575
            /** @scrutinizer ignore-type */ $tree
Loading history...
576
        );
577
    } else {
578
        identUser(
579
            $groupesVisiblesUser,
580
            $groupesInterditsUser,
581
            $idFonctions,
582
            $SETTINGS,
583
            $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

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

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

1599
                    /** @scrutinizer ignore-type */ $data,
Loading history...
1600
                    $_SESSION['key'],
1601
                    256
1602
                ),
1603
                true
1604
            );
1605
        }
1606
    }
1607
}
1608
1609
/**
1610
 * Create a thumbnail.
1611
 *
1612
 * @param string $src           Source
1613
 * @param string $dest          Destination
1614
 * @param float  $desired_width Size of width
1615
 */
1616
function makeThumbnail($src, $dest, $desired_width)
1617
{
1618
    /* read the source image */
1619
    $source_image = imagecreatefrompng($src);
1620
    $width = imagesx($source_image);
1621
    $height = imagesy($source_image);
1622
1623
    /* find the "desired height" of this thumbnail, relative to the desired width  */
1624
    $desired_height = floor($height * ($desired_width / $width));
1625
1626
    /* create a new, "virtual" image */
1627
    $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

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

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

1630
    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

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

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

2405
    fclose(/** @scrutinizer ignore-type */ $debugFile);
Loading history...
2406
}
2407
2408
/**
2409
 * DELETE the file with expected command depending on server type.
2410
 *
2411
 * @param string $file     Path to file
2412
 * @param array  $SETTINGS Teampass settings
2413
 */
2414
function fileDelete($file, $SETTINGS)
2415
{
2416
    // Load AntiXSS
2417
    include_once $SETTINGS['cpassman_dir'].'/includes/libraries/protect/AntiXSS/AntiXSS.php';
2418
    $antiXss = new protect\AntiXSS\AntiXSS();
2419
2420
    $file = $antiXss->xss_clean($file);
2421
    if (is_file($file)) {
2422
        unlink($file);
2423
    }
2424
}
2425
2426
/**
2427
 * Permits to extract the file extension.
2428
 *
2429
 * @param string $file File name
2430
 *
2431
 * @return string
2432
 */
2433
function getFileExtension($file)
2434
{
2435
    if (strpos($file, '.') === false) {
2436
        return $file;
2437
    }
2438
2439
    return substr($file, strrpos($file, '.') + 1);
2440
}
2441
2442
/**
2443
 * Performs chmod operation on subfolders.
2444
 *
2445
 * @param string $dir             Parent folder
2446
 * @param int    $dirPermissions  New permission on folders
2447
 * @param int    $filePermissions New permission on files
2448
 *
2449
 * @return bool
2450
 */
2451
function chmodRecursive($dir, $dirPermissions, $filePermissions)
2452
{
2453
    $pointer_dir = opendir($dir);
2454
    $res = true;
2455
    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

2455
    while (false !== ($file = readdir(/** @scrutinizer ignore-type */ $pointer_dir))) {
Loading history...
2456
        if (($file === '.') || ($file === '..')) {
2457
            continue;
2458
        }
2459
2460
        $fullPath = $dir.'/'.$file;
2461
2462
        if (is_dir($fullPath)) {
2463
            if ($res = @chmod($fullPath, $dirPermissions)) {
2464
                $res = @chmodRecursive($fullPath, $dirPermissions, $filePermissions);
2465
            }
2466
        } else {
2467
            $res = chmod($fullPath, $filePermissions);
2468
        }
2469
        if (!$res) {
2470
            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

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

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

2535
        $bytes = openssl_random_pseudo_bytes(/** @scrutinizer ignore-type */ ceil($lenght / 2));
Loading history...
2536
    } else {
2537
        throw new Exception('no cryptographically secure random function available');
2538
    }
2539
2540
    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

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