Passed
Push — teampass_3.0 ( 809c84...b40c4c )
by Nils
05:51
created

storeUsersShareKey()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 68
Code Lines 40

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
cc 5
eloc 40
c 4
b 0
f 0
nc 6
nop 6
dl 0
loc 68
rs 8.9688

How to fix   Long Method   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
/**
3
 * 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
 * @project   Teampass
10
 * @file      main.functions.php
11
 * ---
12
 * @author    Nils Laumaillé ([email protected])
13
 * @copyright 2009-2019 Teampass.net
14
 * @license   https://spdx.org/licenses/GPL-3.0-only.html#licenseText GPL-3.0
15
 * ---
16
 * @see       https://www.teampass.net
17
 */
18
19
20
if (isset($_SESSION['CPM']) === false || (int)$_SESSION['CPM'] !== 1) {
21
    die('Hacking attempt...');
22
}
23
24
// Load config if $SETTINGS not defined
25
if (isset($SETTINGS['cpassman_dir']) === false || empty($SETTINGS['cpassman_dir']) === true) {
26
    if (file_exists('../includes/config/tp.config.php')) {
27
        include_once '../includes/config/tp.config.php';
28
    } elseif (file_exists('./includes/config/tp.config.php')) {
29
        include_once './includes/config/tp.config.php';
30
    } elseif (file_exists('../../includes/config/tp.config.php')) {
31
        include_once '../../includes/config/tp.config.php';
32
    } else {
33
        throw new Exception("Error file '/includes/config/tp.config.php' not exists", 1);
34
    }
35
}
36
37
header('Content-type: text/html; charset=utf-8');
38
header('Cache-Control: no-cache, must-revalidate');
39
40
/**
41
 * Convert language code to string.
42
 *
43
 * @param string $string String to get
44
 *
45
 * @return string
46
 */
47
function langHdl($string)
48
{
49
    if (empty($string) === true) {
50
        // Manage error
51
        return 'ERROR in language strings!';
52
    }
53
54
    // Load superglobal
55
    if (file_exists('../includes/libraries/protect/SuperGlobal/SuperGlobal.php')) {
56
        include_once '../includes/libraries/protect/SuperGlobal/SuperGlobal.php';
57
    } elseif (file_exists('./includes/libraries/protect/SuperGlobal/SuperGlobal.php')) {
58
        include_once './includes/libraries/protect/SuperGlobal/SuperGlobal.php';
59
    } elseif (file_exists('../../includes/libraries/protect/SuperGlobal/SuperGlobal.php')) {
60
        include_once '../../includes/libraries/protect/SuperGlobal/SuperGlobal.php';
61
    } else {
62
        throw new Exception("Error file '/includes/libraries/protect/SuperGlobal/SuperGlobal.php' not exists", 1);
63
    }
64
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
65
66
    // Get language string
67
    $session_language = $superGlobal->get(trim($string), 'SESSION', true);
68
69
    if (isset($session_language) === false) {
70
        // Manage error
71
        return 'ERROR in language strings!';
72
    } else {
73
        return str_replace(
74
            array('"', "'"),
75
            array('&quot;', '&apos;'),
76
            $session_language
77
        );
78
    }
79
}
80
81
//Generate N# of random bits for use as salt
82
/**
83
 * Undocumented function.
84
 *
85
 * @param int $size Length
86
 *
87
 * @return array
88
 */
89
function getBits($size)
90
{
91
    $str = '';
92
    $var_x = $size + 10;
93
    for ($var_i = 0; $var_i < $var_x; ++$var_i) {
94
        $str .= base_convert(mt_rand(1, 36), 10, 36);
95
    }
96
97
    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...
98
}
99
100
//generate pbkdf2 compliant hash
101
function strHashPbkdf2($var_p, $var_s, $var_c, $var_kl, $var_a = 'sha256', $var_st = 0)
102
{
103
    $var_kb = $var_st + $var_kl; // Key blocks to compute
104
    $var_dk = ''; // Derived key
105
106
    for ($block = 1; $block <= $var_kb; ++$block) { // Create key
107
        $var_ib = $var_h = hash_hmac($var_a, $var_s . pack('N', $block), $var_p, true); // Initial hash for this block
108
        for ($var_i = 1; $var_i < $var_c; ++$var_i) { // Perform block iterations
109
            $var_ib ^= ($var_h = hash_hmac($var_a, $var_h, $var_p, true)); // XOR each iterate
110
        }
111
        $var_dk .= $var_ib; // Append iterated block
112
    }
113
114
    return substr($var_dk, $var_st, $var_kl); // Return derived key of correct length
115
}
116
117
/**
118
 * stringUtf8Decode().
119
 *
120
 * utf8_decode
121
 */
122
function stringUtf8Decode($string)
123
{
124
    return str_replace(' ', '+', utf8_decode($string));
125
}
126
127
/**
128
 * encryptOld().
129
 *
130
 * crypt a string
131
 *
132
 * @param string $text
133
 */
134
function encryptOld($text, $personalSalt = '')
135
{
136
    if (empty($personalSalt) === false) {
137
        return trim(
138
            base64_encode(
139
                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

139
                /** @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...
140
                    MCRYPT_RIJNDAEL_256,
141
                    $personalSalt,
142
                    $text,
143
                    MCRYPT_MODE_ECB,
144
                    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

144
                    /** @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...
145
                        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

145
                        /** @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...
146
                        MCRYPT_RAND
147
                    )
148
                )
149
            )
150
        );
151
    }
152
153
    // If $personalSalt is not empty
154
    return trim(
155
        base64_encode(
156
            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

156
            /** @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...
157
                MCRYPT_RIJNDAEL_256,
158
                SALT,
0 ignored issues
show
Bug introduced by
The constant SALT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
159
                $text,
160
                MCRYPT_MODE_ECB,
161
                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

161
                /** @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...
162
                    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

162
                    /** @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...
163
                    MCRYPT_RAND
164
                )
165
            )
166
        )
167
    );
168
}
169
170
/**
171
 * decryptOld().
172
 *
173
 * decrypt a crypted string
174
 */
175
function decryptOld($text, $personalSalt = '')
176
{
177
    if (!empty($personalSalt)) {
178
        return trim(
179
            mcrypt_decrypt(
0 ignored issues
show
Deprecated Code introduced by
The function mcrypt_decrypt() has been deprecated: 7.1 ( Ignorable by Annotation )

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Loading history...
186
                    MCRYPT_RAND
187
                )
188
            )
189
        );
190
    }
191
192
    // No personal SK
193
    return trim(
194
        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

194
        /** @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...
195
            MCRYPT_RIJNDAEL_256,
196
            SALT,
0 ignored issues
show
Bug introduced by
The constant SALT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
197
            base64_decode($text),
198
            MCRYPT_MODE_ECB,
199
            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

199
            /** @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...
200
                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

200
                /** @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...
201
                MCRYPT_RAND
202
            )
203
        )
204
    );
205
}
206
207
/**
208
 * encrypt().
209
 *
210
 * crypt a string
211
 *
212
 * @param string $decrypted
213
 */
214
function encrypt($decrypted, $personalSalt = '')
215
{
216
    global $SETTINGS;
217
218
    if (isset($SETTINGS['cpassman_dir']) === false || empty($SETTINGS['cpassman_dir']) === true) {
219
        require_once '../includes/libraries/Encryption/PBKDF2/PasswordHash.php';
220
    } else {
221
        require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Encryption/PBKDF2/PasswordHash.php';
222
    }
223
224
    if (!empty($personalSalt)) {
225
        $staticSalt = $personalSalt;
226
    } else {
227
        $staticSalt = SALT;
0 ignored issues
show
Bug introduced by
The constant SALT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
228
    }
229
230
    //set our salt to a variable
231
    // Get 64 random bits for the salt for pbkdf2
232
    $pbkdf2Salt = getBits(64);
233
    // generate a pbkdf2 key to use for the encryption.
234
    $key = substr(pbkdf2('sha256', $staticSalt, $pbkdf2Salt, ITCOUNT, 16 + 32, true), 32, 16);
235
    // Build $init_vect and $ivBase64.  We use a block size of 256 bits (AES compliant)
236
    // and CTR mode.  (Note: ECB mode is inadequate as IV is not used.)
237
    $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_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

237
    $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...
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

237
    $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...
238
239
    //base64 trim
240
    if (strlen($ivBase64 = rtrim(base64_encode($init_vect), '=')) != 43) {
241
        return false;
242
    }
243
    // Encrypt $decrypted
244
    $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

244
    $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...
245
    // MAC the encrypted text
246
    $mac = hash_hmac('sha256', $encrypted, $staticSalt);
247
    // We're done!
248
    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

248
    return base64_encode($ivBase64 . $encrypted . $mac . /** @scrutinizer ignore-type */ $pbkdf2Salt);
Loading history...
249
}
250
251
/**
252
 * decrypt().
253
 *
254
 * decrypt a crypted string
255
 */
256
function decrypt($encrypted, $personalSalt = '')
257
{
258
    global $SETTINGS;
259
260
    if (isset($SETTINGS['cpassman_dir']) === false || empty($SETTINGS['cpassman_dir']) === true) {
261
        include_once '../includes/libraries/Encryption/PBKDF2/PasswordHash.php';
262
    } else {
263
        include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Encryption/PBKDF2/PasswordHash.php';
264
    }
265
266
    if (!empty($personalSalt)) {
267
        $staticSalt = $personalSalt;
268
    } else {
269
        $staticSalt = file_get_contents(SECUREPATH . '/teampass-seckey.txt');
0 ignored issues
show
Bug introduced by
The constant SECUREPATH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
270
    }
271
    //base64 decode the entire payload
272
    $encrypted = base64_decode($encrypted);
273
    // get the salt
274
    $pbkdf2Salt = substr($encrypted, -64);
275
    //remove the salt from the string
276
    $encrypted = substr($encrypted, 0, -64);
277
    $key = substr(pbkdf2('sha256', $staticSalt, $pbkdf2Salt, ITCOUNT, 16 + 32, true), 32, 16);
278
    // Retrieve $init_vect which is the first 22 characters plus ==, base64_decoded.
279
    $init_vect = base64_decode(substr($encrypted, 0, 43) . '==');
280
    // Remove $init_vect from $encrypted.
281
    $encrypted = substr($encrypted, 43);
282
    // Retrieve $mac which is the last 64 characters of $encrypted.
283
    $mac = substr($encrypted, -64);
284
    // Remove the last 64 chars from encrypted (remove MAC)
285
    $encrypted = substr($encrypted, 0, -64);
286
    //verify the sha256hmac from the encrypted data before even trying to decrypt it
287
    if (hash_hmac('sha256', $encrypted, $staticSalt) != $mac) {
288
        return false;
289
    }
290
    // Decrypt the data.
291
    $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

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

617
            /** @scrutinizer ignore-type */ $tree
Loading history...
618
        );
619
    } else {
620
        identUser(
621
            $groupesVisiblesUser,
622
            $groupesInterditsUser,
623
            $idFonctions,
624
            $SETTINGS,
625
            $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

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

1679
                /** @scrutinizer ignore-type */ $data,
Loading history...
1680
                true
1681
            );
1682
        } else {
1683
            return json_decode(
1684
                Encryption\Crypt\aesctr::decrypt(
1685
                    $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

1685
                    /** @scrutinizer ignore-type */ $data,
Loading history...
1686
                    $globalsKey,
1687
                    256
1688
                ),
1689
                true
1690
            );
1691
        }
1692
    }
1693
}
1694
1695
/**
1696
 * Create a thumbnail.
1697
 *
1698
 * @param string $src           Source
1699
 * @param string $dest          Destination
1700
 * @param float  $desired_width Size of width
1701
 */
1702
function makeThumbnail($src, $dest, $desired_width)
1703
{
1704
    /* read the source image */
1705
    $source_image = imagecreatefrompng($src);
1706
    $width = imagesx($source_image);
0 ignored issues
show
Bug introduced by
It seems like $source_image can also be of type false; however, parameter $image of imagesx() 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

1706
    $width = imagesx(/** @scrutinizer ignore-type */ $source_image);
Loading history...
1707
    $height = imagesy($source_image);
0 ignored issues
show
Bug introduced by
It seems like $source_image can also be of type false; however, parameter $image of imagesy() 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

1707
    $height = imagesy(/** @scrutinizer ignore-type */ $source_image);
Loading history...
1708
1709
    /* find the "desired height" of this thumbnail, relative to the desired width  */
1710
    $desired_height = floor($height * ($desired_width / $width));
1711
1712
    /* create a new, "virtual" image */
1713
    $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

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

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

1716
    imagecopyresampled($virtual_image, $source_image, 0, 0, 0, 0, /** @scrutinizer ignore-type */ $desired_width, $desired_height, $width, $height);
Loading history...
Bug introduced by
It seems like $source_image can also be of type false; however, parameter $src_image of imagecopyresampled() 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

1716
    imagecopyresampled($virtual_image, /** @scrutinizer ignore-type */ $source_image, 0, 0, 0, 0, $desired_width, $desired_height, $width, $height);
Loading history...
Bug introduced by
It seems like $virtual_image can also be of type false; however, parameter $dst_image of imagecopyresampled() 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

1716
    imagecopyresampled(/** @scrutinizer ignore-type */ $virtual_image, $source_image, 0, 0, 0, 0, $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

1716
    imagecopyresampled($virtual_image, $source_image, 0, 0, 0, 0, $desired_width, /** @scrutinizer ignore-type */ $desired_height, $width, $height);
Loading history...
1717
1718
    /* create the physical thumbnail image to its destination */
1719
    imagejpeg($virtual_image, $dest);
0 ignored issues
show
Bug introduced by
It seems like $virtual_image can also be of type false; however, parameter $image of imagejpeg() 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

1719
    imagejpeg(/** @scrutinizer ignore-type */ $virtual_image, $dest);
Loading history...
1720
}
1721
1722
/**
1723
 * Check table prefix in SQL query.
1724
 *
1725
 * @param string $table Table name
1726
 *
1727
 * @return string
1728
 */
1729
function prefixTable($table)
1730
{
1731
    $safeTable = htmlspecialchars(DB_PREFIX . $table);
0 ignored issues
show
Bug introduced by
The constant DB_PREFIX was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1732
    if (!empty($safeTable)) {
1733
        // sanitize string
1734
        return $safeTable;
1735
    } else {
1736
        // stop error no table
1737
        return 'table_not_exists';
1738
    }
1739
}
1740
1741
1742
/**
1743
 * GenerateCryptKey
1744
 *
1745
 * @param int     $size      Length
1746
 * @param boolean $secure    Secure
1747
 * @param boolean $numerals  Numerics
1748
 * @param boolean $uppercase Uppercase letters
1749
 * @param boolean $symbols   Symbols
1750
 * @param boolean $lowercase Lowercase
1751
 * @param array   $SETTINGS  SETTINGS
1752
 * @return void
1753
 */
1754
function GenerateCryptKey(
1755
    $size = null,
1756
    $secure = false,
1757
    $numerals = false,
1758
    $uppercase = false,
1759
    $symbols = false,
1760
    $lowercase = false,
1761
    $SETTINGS
1762
) {
1763
    include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php';
1764
    $generator = new SplClassLoader('PasswordGenerator\Generator', $SETTINGS['cpassman_dir'] . '/includes/libraries');
1765
    $generator->register();
1766
    $generator = new PasswordGenerator\Generator\ComputerPasswordGenerator();
1767
1768
    // Is PHP7 being used?
1769
    if (version_compare(PHP_VERSION, '7.0.0', '>=')) {
1770
        $php7generator = new SplClassLoader('PasswordGenerator\RandomGenerator', $SETTINGS['cpassman_dir'] . '/includes/libraries');
1771
        $php7generator->register();
1772
        $generator->setRandomGenerator(new PasswordGenerator\RandomGenerator\Php7RandomGenerator());
1773
    }
1774
1775
    // Manage size
1776
    $generator->setLength(($size === null) ? 10 : (int) $size);
1777
1778
    if ($secure === true) {
1779
        $generator->setSymbols(true);
1780
        $generator->setLowercase(true);
1781
        $generator->setUppercase(true);
1782
        $generator->setNumbers(true);
1783
    } else {
1784
        $generator->setLowercase($lowercase);
1785
        $generator->setUppercase($uppercase);
1786
        $generator->setNumbers($numerals);
1787
        $generator->setSymbols($symbols);
1788
    }
1789
1790
    return $generator->generatePasswords()[0];
1791
}
1792
1793
/*
1794
* Send sysLOG message
1795
* @param string $message
1796
* @param string $host
1797
*/
1798
function send_syslog($message, $host, $port, $component = 'teampass')
1799
{
1800
    $sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
1801
    $syslog_message = '<123>' . date('M d H:i:s ') . $component . ': ' . $message;
1802
    socket_sendto($sock, $syslog_message, strlen($syslog_message), 0, $host, $port);
1803
    socket_close($sock);
1804
}
1805
1806
/**
1807
 * Permits to log events into DB
1808
 *
1809
 * @param array  $SETTINGS Teampass settings
1810
 * @param string $type     Type
1811
 * @param string $label    Label
1812
 * @param string $who      Who
1813
 * @param string $login    Login
1814
 * @param string $field_1  Field
1815
 *
1816
 * @return void
1817
 */
1818
function logEvents($SETTINGS, $type, $label, $who, $login = null, $field_1 = null)
1819
{
1820
    if (empty($who)) {
1821
        $who = getClientIpServer();
1822
    }
1823
1824
    // include librairies & connect to DB
1825
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
1826
    if (defined('DB_PASSWD_CLEAR') === false) {
1827
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
0 ignored issues
show
Bug introduced by
The constant DB_PASSWD was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1828
    }
1829
    DB::$host = DB_HOST;
0 ignored issues
show
Bug introduced by
The constant DB_HOST was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1830
    DB::$user = DB_USER;
0 ignored issues
show
Bug introduced by
The constant DB_USER was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1831
    DB::$password = DB_PASSWD_CLEAR;
1832
    DB::$dbName = DB_NAME;
0 ignored issues
show
Bug introduced by
The constant DB_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1833
    DB::$port = DB_PORT;
0 ignored issues
show
Bug introduced by
The constant DB_PORT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1834
    DB::$encoding = DB_ENCODING;
1835
1836
    DB::insert(
1837
        prefixTable('log_system'),
1838
        array(
1839
            'type' => $type,
1840
            'date' => time(),
1841
            'label' => $label,
1842
            'qui' => $who,
1843
            'field_1' => $field_1 === null ? '' : $field_1,
1844
        )
1845
    );
1846
1847
    // If SYSLOG
1848
    if (isset($SETTINGS['syslog_enable']) === true && (int) $SETTINGS['syslog_enable'] === 1) {
1849
        if ($type === 'user_mngt') {
1850
            send_syslog(
1851
                'action=' . str_replace('at_', '', $label) . ' attribute=user user=' . $who . ' userid="' . $login . '" change="' . $field_1 . '" ',
1852
                $SETTINGS['syslog_host'],
1853
                $SETTINGS['syslog_port'],
1854
                'teampass'
1855
            );
1856
        } else {
1857
            send_syslog(
1858
                'action=' . $type . ' attribute=' . $label . ' user=' . $who . ' userid="' . $login . '" ',
1859
                $SETTINGS['syslog_host'],
1860
                $SETTINGS['syslog_port'],
1861
                'teampass'
1862
            );
1863
        }
1864
    }
1865
}
1866
1867
/**
1868
 * Log events.
1869
 *
1870
 * @param array  $SETTINGS        Teampass settings
1871
 * @param int    $item_id         Item id
1872
 * @param string $item_label      Item label
1873
 * @param int    $id_user         User id
1874
 * @param string $action          Code for reason
1875
 * @param string $login           User login
1876
 * @param string $raison          Code for reason
1877
 * @param string $encryption_type Encryption on
1878
 */
1879
function logItems(
1880
    $SETTINGS,
1881
    $item_id,
1882
    $item_label,
1883
    $id_user,
1884
    $action,
1885
    $login = null,
1886
    $raison = null,
1887
    $encryption_type = null
1888
) {
1889
    // include librairies & connect to DB
1890
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
1891
    if (defined('DB_PASSWD_CLEAR') === false) {
1892
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
0 ignored issues
show
Bug introduced by
The constant DB_PASSWD was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1893
    }
1894
    DB::$host = DB_HOST;
0 ignored issues
show
Bug introduced by
The constant DB_HOST was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1895
    DB::$user = DB_USER;
0 ignored issues
show
Bug introduced by
The constant DB_USER was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1896
    DB::$password = DB_PASSWD_CLEAR;
1897
    DB::$dbName = DB_NAME;
0 ignored issues
show
Bug introduced by
The constant DB_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1898
    DB::$port = DB_PORT;
0 ignored issues
show
Bug introduced by
The constant DB_PORT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1899
    DB::$encoding = DB_ENCODING;
1900
1901
    // Insert log in DB
1902
    DB::insert(
1903
        prefixTable('log_items'),
1904
        array(
1905
            'id_item' => $item_id,
1906
            'date' => time(),
1907
            'id_user' => $id_user,
1908
            'action' => $action,
1909
            'raison' => $raison,
1910
            'raison_iv' => '',
1911
            'encryption_type' => is_null($encryption_type) === true ? TP_ENCRYPTION_NAME : $encryption_type,
1912
        )
1913
    );
1914
    // Timestamp the last change
1915
    if ($action === 'at_creation' || $action === 'at_modifiation' || $action === 'at_delete' || $action === 'at_import') {
1916
        DB::update(
1917
            prefixTable('misc'),
1918
            array(
1919
                'valeur' => time(),
1920
            ),
1921
            'type = %s AND intitule = %s',
1922
            'timestamp',
1923
            'last_item_change'
1924
        );
1925
    }
1926
1927
    // SYSLOG
1928
    if (isset($SETTINGS['syslog_enable']) === true && $SETTINGS['syslog_enable'] === '1') {
1929
        // Extract reason
1930
        $attribute = explode(' : ', $raison);
1931
1932
        // Get item info if not known
1933
        if (empty($item_label) === true) {
1934
            $dataItem = DB::queryfirstrow(
1935
                'SELECT id, id_tree, label
1936
                FROM ' . prefixTable('items') . '
1937
                WHERE id = %i',
1938
                $item_id
1939
            );
1940
1941
            $item_label = $dataItem['label'];
1942
        }
1943
1944
        send_syslog(
1945
            'action=' . str_replace('at_', '', $action) . ' attribute=' . str_replace('at_', '', $attribute[0]) . ' itemno=' . $item_id . ' user=' . addslashes($login) . ' itemname="' . addslashes($item_label) . '"',
1946
            $SETTINGS['syslog_host'],
1947
            $SETTINGS['syslog_port'],
1948
            'teampass'
1949
        );
1950
    }
1951
1952
    // send notification if enabled
1953
    notifyOnChange($item_id, $action, $SETTINGS);
1954
}
1955
1956
/**
1957
 * If enabled, then notify admin/manager.
1958
 *
1959
 * @param int    $item_id  Item id
1960
 * @param string $action   Action to do
1961
 * @param array  $SETTINGS Teampass settings
1962
 */
1963
function notifyOnChange($item_id, $action, $SETTINGS)
1964
{
1965
    if (isset($SETTINGS['enable_email_notification_on_item_shown']) === true
1966
        && (int) $SETTINGS['enable_email_notification_on_item_shown'] === 1
1967
        && $action === 'at_shown'
1968
    ) {
1969
        // Load superglobal
1970
        include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
1971
        $superGlobal = new protect\SuperGlobal\SuperGlobal();
1972
    
1973
        // Get superglobals
1974
        $globalsLastname = $superGlobal->get('lastname', 'SESSION');
1975
        $globalsName = $superGlobal->get('name', 'SESSION');
1976
        $globalsNotifiedEmails = $superGlobal->get('listNotificationEmails', 'SESSION');
0 ignored issues
show
Unused Code introduced by
The assignment to $globalsNotifiedEmails is dead and can be removed.
Loading history...
1977
1978
        // Get info about item
1979
        $dataItem = DB::queryfirstrow(
1980
            'SELECT id, id_tree, label
1981
            FROM ' . prefixTable('items') . '
1982
            WHERE id = %i',
1983
            $item_id
1984
        );
1985
        $item_label = $dataItem['label'];
1986
1987
        // send back infos
1988
        DB::insert(
1989
            prefixTable('emails'),
1990
            array(
1991
                'timestamp' => time(),
1992
                'subject' => langHdl('email_on_open_notification_subject'),
1993
                'body' => str_replace(
1994
                    array('#tp_user#', '#tp_item#', '#tp_link#'),
1995
                    array(
1996
                        addslashes($globalsName . ' ' . $globalsLastname),
1997
                        addslashes($item_label),
1998
                        $SETTINGS['cpassman_url'] . '/index.php?page=items&group=' . $dataItem['id_tree'] . '&id=' . $item_id,
1999
                    ),
2000
                    langHdl('email_on_open_notification_mail')
2001
                ),
2002
                'receivers' => $_globalsNotifiedEmails,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $_globalsNotifiedEmails does not exist. Did you maybe mean $globalsNotifiedEmails?
Loading history...
2003
                'status' => '',
2004
            )
2005
        );
2006
    }
2007
}
2008
2009
/**
2010
 * Prepare notification email to subscribers.
2011
 *
2012
 * @param int    $item_id  Item id
2013
 * @param string $label    Item label
2014
 * @param array  $changes  List of changes
2015
 * @param array  $SETTINGS Teampass settings
2016
 */
2017
function notifyChangesToSubscribers($item_id, $label, $changes, $SETTINGS)
2018
{
2019
    // Load superglobal
2020
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
2021
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
2022
2023
    // Get superglobals
2024
    $globalsUserId = $superGlobal->get('user_id', 'SESSION');
2025
    $globalsLastname = $superGlobal->get('lastname', 'SESSION');
2026
    $globalsName = $superGlobal->get('name', 'SESSION');
2027
2028
    // send email to user that what to be notified
2029
    $notification = DB::queryOneColumn(
2030
        'email',
2031
        'SELECT *
2032
        FROM ' . prefixTable('notification') . ' AS n
2033
        INNER JOIN ' . prefixTable('users') . ' AS u ON (n.user_id = u.id)
2034
        WHERE n.item_id = %i AND n.user_id != %i',
2035
        $item_id,
2036
        $globalsUserId
2037
    );
2038
2039
    if (DB::count() > 0) {
2040
        // Prepare path
2041
        $path = geItemReadablePath($item_id, '', $SETTINGS);
2042
2043
        // Get list of changes
2044
        $htmlChanges = '<ul>';
2045
        foreach ($changes as $change) {
2046
            $htmlChanges .= '<li>' . $change . '</li>';
2047
        }
2048
        $htmlChanges .= '</ul>';
2049
2050
        // send email
2051
        DB::insert(
2052
            prefixTable('emails'),
2053
            array(
2054
                'timestamp' => time(),
2055
                'subject' => langHdl('email_subject_item_updated'),
2056
                'body' => str_replace(
2057
                    array('#item_label#', '#folder_name#', '#item_id#', '#url#', '#name#', '#lastname#', '#changes#'),
2058
                    array($label, $path, $item_id, $SETTINGS['cpassman_url'], $globalsName, $globalsLastname, $htmlChanges),
2059
                    langHdl('email_body_item_updated')
2060
                ),
2061
                'receivers' => implode(',', $notification),
2062
                'status' => '',
2063
            )
2064
        );
2065
    }
2066
}
2067
2068
/**
2069
 * Returns the Item + path.
2070
 *
2071
 * @param int    $id_tree
2072
 * @param string $label
2073
 * @param array  $SETTINGS
2074
 *
2075
 * @return string
2076
 */
2077
function geItemReadablePath($id_tree, $label, $SETTINGS)
2078
{
2079
    // Class loader
2080
    require_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php';
2081
2082
    //Load Tree
2083
    $tree = new SplClassLoader('Tree\NestedTree', '../includes/libraries');
2084
    $tree->register();
2085
    $tree = new Tree\NestedTree\NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
2086
2087
    $arbo = $tree->getPath($id_tree, true);
2088
    $path = '';
2089
    foreach ($arbo as $elem) {
2090
        if (empty($path) === true) {
2091
            $path = htmlspecialchars(stripslashes(htmlspecialchars_decode($elem->title, ENT_QUOTES)), ENT_QUOTES) . ' ';
2092
        } else {
2093
            $path .= '&#8594; ' . htmlspecialchars(stripslashes(htmlspecialchars_decode($elem->title, ENT_QUOTES)), ENT_QUOTES);
2094
        }
2095
    }
2096
2097
    // Build text to show user
2098
    if (empty($label) === false) {
2099
        return empty($path) === true ? addslashes($label) : addslashes($label) . ' (' . $path . ')';
2100
    } else {
2101
        return empty($path) === true ? '' : $path;
2102
    }
2103
}
2104
2105
/**
2106
 * Get the client ip address.
2107
 *
2108
 * @return string IP address
2109
 */
2110
function getClientIpServer()
2111
{
2112
    if (getenv('HTTP_CLIENT_IP')) {
2113
        $ipaddress = getenv('HTTP_CLIENT_IP');
2114
    } elseif (getenv('HTTP_X_FORWARDED_FOR')) {
2115
        $ipaddress = getenv('HTTP_X_FORWARDED_FOR');
2116
    } elseif (getenv('HTTP_X_FORWARDED')) {
2117
        $ipaddress = getenv('HTTP_X_FORWARDED');
2118
    } elseif (getenv('HTTP_FORWARDED_FOR')) {
2119
        $ipaddress = getenv('HTTP_FORWARDED_FOR');
2120
    } elseif (getenv('HTTP_FORWARDED')) {
2121
        $ipaddress = getenv('HTTP_FORWARDED');
2122
    } elseif (getenv('REMOTE_ADDR')) {
2123
        $ipaddress = getenv('REMOTE_ADDR');
2124
    } else {
2125
        $ipaddress = 'UNKNOWN';
2126
    }
2127
2128
    return $ipaddress;
2129
}
2130
2131
/**
2132
 * Escape all HTML, JavaScript, and CSS.
2133
 *
2134
 * @param string $input    The input string
2135
 * @param string $encoding Which character encoding are we using?
2136
 *
2137
 * @return string
2138
 */
2139
function noHTML($input, $encoding = 'UTF-8')
2140
{
2141
    return htmlspecialchars($input, ENT_QUOTES | ENT_XHTML, $encoding, false);
2142
}
2143
2144
/**
2145
 * handleConfigFile().
2146
 * permits to handle the Teampass config file
2147
 * $action accepts "rebuild" and "update"
2148
 *
2149
 * @param string $action   Action to perform
2150
 * @param array  $SETTINGS Teampass settings
2151
 * @param string $field    Field to refresh
2152
 * @param string $value    Value to set
2153
 * @return void
2154
 */
2155
function handleConfigFile($action, $SETTINGS, $field = null, $value = null)
2156
{
2157
    $tp_config_file = $SETTINGS['cpassman_dir'] . '/includes/config/tp.config.php';
2158
2159
    // include librairies & connect to DB
2160
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
2161
    if (defined('DB_PASSWD_CLEAR') === false) {
2162
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
0 ignored issues
show
Bug introduced by
The constant DB_PASSWD was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2163
    }
2164
    DB::$host = DB_HOST;
0 ignored issues
show
Bug introduced by
The constant DB_HOST was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2165
    DB::$user = DB_USER;
0 ignored issues
show
Bug introduced by
The constant DB_USER was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2166
    DB::$password = DB_PASSWD_CLEAR;
2167
    DB::$dbName = DB_NAME;
0 ignored issues
show
Bug introduced by
The constant DB_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2168
    DB::$port = DB_PORT;
0 ignored issues
show
Bug introduced by
The constant DB_PORT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2169
    DB::$encoding = DB_ENCODING;
2170
2171
    if (file_exists($tp_config_file) === false || $action === 'rebuild') {
2172
        // perform a copy
2173
        if (file_exists($tp_config_file)) {
2174
            if (!copy($tp_config_file, $tp_config_file . '.' . date('Y_m_d_His', time()))) {
2175
                return "ERROR: Could not copy file '" . $tp_config_file . "'";
0 ignored issues
show
Bug Best Practice introduced by
The expression return 'ERROR: Could not.... $tp_config_file . ''' returns the type string which is incompatible with the documented return type void.
Loading history...
2176
            }
2177
        }
2178
2179
2180
        // regenerate
2181
        $data = array();
2182
        $data[0] = "<?php\n";
2183
        $data[1] = "global \$SETTINGS;\n";
2184
        $data[2] = "\$SETTINGS = array (\n";
2185
        $rows = DB::query(
2186
            'SELECT * FROM ' . prefixTable('misc') . ' WHERE type=%s',
2187
            'admin'
2188
        );
2189
        foreach ($rows as $record) {
2190
            array_push($data, "    '" . $record['intitule'] . "' => '" . $record['valeur'] . "',\n");
2191
        }
2192
        array_push($data, ");\n");
2193
        $data = array_unique($data);
2194
    } elseif ($action === 'update' && empty($field) === false) {
2195
        $data = file($tp_config_file);
2196
        $inc = 0;
2197
        $bFound = false;
2198
        foreach ($data as $line) {
2199
            if (stristr($line, ');')) {
2200
                break;
2201
            }
2202
2203
            if (stristr($line, "'" . $field . "' => '")) {
2204
                $data[$inc] = "    '" . $field . "' => '" . filter_var($value, FILTER_SANITIZE_STRING) . "',\n";
2205
                $bFound = true;
2206
                break;
2207
            }
2208
            ++$inc;
2209
        }
2210
        if ($bFound === false) {
2211
            $data[($inc)] = "    '" . $field . "' => '" . filter_var($value, FILTER_SANITIZE_STRING) . "',\n);\n";
2212
        }
2213
    }
2214
2215
    // update file
2216
    file_put_contents($tp_config_file, implode('', isset($data) ? $data : array()));
2217
2218
    return true;
0 ignored issues
show
Bug Best Practice introduced by
The expression return true returns the type true which is incompatible with the documented return type void.
Loading history...
2219
}
2220
2221
/*
2222
** Permits to replace &#92; to permit correct display
2223
*/
2224
/**
2225
 * @param string $input
2226
 */
2227
function handleBackslash($input)
2228
{
2229
    return str_replace('&amp;#92;', '&#92;', $input);
2230
}
2231
2232
/*
2233
** Permits to loas settings
2234
*/
2235
function loadSettings()
2236
{
2237
    global $SETTINGS;
2238
2239
    /* LOAD CPASSMAN SETTINGS */
2240
    if (!isset($SETTINGS['loaded']) || $SETTINGS['loaded'] != 1) {
2241
        $SETTINGS['duplicate_folder'] = 0; //by default, this is set to 0;
2242
        $SETTINGS['duplicate_item'] = 0; //by default, this is set to 0;
2243
        $SETTINGS['number_of_used_pw'] = 5; //by default, this value is set to 5;
2244
        $settings = array();
2245
2246
        $rows = DB::query(
2247
            'SELECT * FROM ' . prefixTable('misc') . ' WHERE type=%s_type OR type=%s_type2',
2248
            array(
2249
                'type' => 'admin',
2250
                'type2' => 'settings',
2251
            )
2252
        );
2253
        foreach ($rows as $record) {
2254
            if ($record['type'] === 'admin') {
2255
                $SETTINGS[$record['intitule']] = $record['valeur'];
2256
            } else {
2257
                $settings[$record['intitule']] = $record['valeur'];
2258
            }
2259
        }
2260
        $SETTINGS['loaded'] = 1;
2261
        $SETTINGS['default_session_expiration_time'] = 5;
2262
    }
2263
}
2264
2265
/*
2266
** check if folder has custom fields.
2267
** Ensure that target one also has same custom fields
2268
*/
2269
function checkCFconsistency($source_id, $target_id)
2270
{
2271
    $source_cf = array();
2272
    $rows = DB::QUERY(
2273
        'SELECT id_category
2274
        FROM ' . prefixTable('categories_folders') . '
2275
        WHERE id_folder = %i',
2276
        $source_id
2277
    );
2278
    foreach ($rows as $record) {
2279
        array_push($source_cf, $record['id_category']);
2280
    }
2281
2282
    $target_cf = array();
2283
    $rows = DB::QUERY(
2284
        'SELECT id_category
2285
        FROM ' . prefixTable('categories_folders') . '
2286
        WHERE id_folder = %i',
2287
        $target_id
2288
    );
2289
    foreach ($rows as $record) {
2290
        array_push($target_cf, $record['id_category']);
2291
    }
2292
2293
    $cf_diff = array_diff($source_cf, $target_cf);
2294
    if (count($cf_diff) > 0) {
2295
        return false;
2296
    }
2297
2298
    return true;
2299
}
2300
2301
/**
2302
 * Will encrypte/decrypt a fil eusing Defuse.
2303
 *
2304
 * @param string $type        can be either encrypt or decrypt
2305
 * @param string $source_file path to source file
2306
 * @param string $target_file path to target file
2307
 * @param array  $SETTINGS    Settings
2308
 * @param string $password    A password
2309
 *
2310
 * @return string|bool
2311
 */
2312
function prepareFileWithDefuse(
2313
    $type,
2314
    $source_file,
2315
    $target_file,
2316
    $SETTINGS,
2317
    $password = null
2318
) {
2319
    // Load AntiXSS
2320
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/AntiXSS/AntiXSS.php';
2321
    $antiXss = new voku\helper\AntiXSS();
2322
2323
    // Protect against bad inputs
2324
    if (is_array($source_file) === true || is_array($target_file) === true) {
1 ignored issue
show
introduced by
The condition is_array($target_file) === true is always false.
Loading history...
2325
        return 'error_cannot_be_array';
2326
    }
2327
2328
    // Sanitize
2329
    $source_file = $antiXss->xss_clean($source_file);
2330
    $target_file = $antiXss->xss_clean($target_file);
2331
2332
    if (empty($password) === true || is_null($password) === true) {
2333
        /*
2334
        File encryption/decryption is done with the SALTKEY
2335
         */
2336
2337
        // get KEY
2338
        $ascii_key = file_get_contents(SECUREPATH . '/teampass-seckey.txt');
0 ignored issues
show
Bug introduced by
The constant SECUREPATH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2339
2340
        // Now perform action on the file
2341
        $err = '';
2342
        if ($type === 'decrypt') {
2343
            // Decrypt file
2344
            $err = defuseFileDecrypt(
2345
                $source_file,
2346
                $target_file,
2347
                $SETTINGS,
2348
                \Defuse\Crypto\Key::loadFromAsciiSafeString($ascii_key)
0 ignored issues
show
Bug introduced by
Defuse\Crypto\Key::loadF...iSafeString($ascii_key) of type Defuse\Crypto\Key is incompatible with the type string expected by parameter $password of defuseFileDecrypt(). ( Ignorable by Annotation )

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

2348
                /** @scrutinizer ignore-type */ \Defuse\Crypto\Key::loadFromAsciiSafeString($ascii_key)
Loading history...
2349
            );
2350
            // ---
2351
        } elseif ($type === 'encrypt') {
2352
            // Encrypt file
2353
            $err = defuseFileEncrypt(
2354
                $source_file,
2355
                $target_file,
2356
                $SETTINGS,
2357
                \Defuse\Crypto\Key::loadFromAsciiSafeString($ascii_key)
0 ignored issues
show
Bug introduced by
Defuse\Crypto\Key::loadF...iSafeString($ascii_key) of type Defuse\Crypto\Key is incompatible with the type string expected by parameter $password of defuseFileEncrypt(). ( Ignorable by Annotation )

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

2357
                /** @scrutinizer ignore-type */ \Defuse\Crypto\Key::loadFromAsciiSafeString($ascii_key)
Loading history...
2358
            );
2359
        }
2360
    } else {
2361
        /*
2362
        File encryption/decryption is done with special password and not the SALTKEY
2363
         */
2364
2365
        $err = '';
2366
        if ($type === 'decrypt') {
2367
            // Decrypt file
2368
            $err = defuseFileDecrypt(
2369
                $source_file,
2370
                $target_file,
2371
                $SETTINGS,
2372
                $password
2373
            );
2374
            // ---
2375
        } elseif ($type === 'encrypt') {
2376
            // Encrypt file
2377
            $err = defuseFileEncrypt(
2378
                $source_file,
2379
                $target_file,
2380
                $SETTINGS,
2381
                $password
2382
            );
2383
        }
2384
    }
2385
2386
    // return error
2387
    return empty($err) === false ? $err : true;
2388
}
2389
2390
/**
2391
 * Encrypt a file with Defuse.
2392
 *
2393
 * @param string $source_file path to source file
2394
 * @param string $target_file path to target file
2395
 * @param array  $SETTINGS    Settings
2396
 * @param string $password    A password
2397
 *
2398
 * @return string|bool
2399
 */
2400
function defuseFileEncrypt(
2401
    $source_file,
2402
    $target_file,
2403
    $SETTINGS,
2404
    $password = null
2405
) {
2406
    // load PhpEncryption library
2407
    $path_to_encryption = '/includes/libraries/Encryption/Encryption/';
2408
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Crypto.php';
2409
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Encoding.php';
2410
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'DerivedKeys.php';
2411
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Key.php';
2412
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'KeyOrPassword.php';
2413
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'File.php';
2414
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'RuntimeTests.php';
2415
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'KeyProtectedByPassword.php';
2416
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Core.php';
2417
2418
    try {
2419
        \Defuse\Crypto\File::encryptFileWithPassword(
2420
            $source_file,
2421
            $target_file,
2422
            $password
2423
        );
2424
    } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
2425
        $err = 'wrong_key';
2426
    } catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) {
2427
        $err = $ex;
2428
    } catch (Defuse\Crypto\Exception\IOException $ex) {
2429
        $err = $ex;
2430
    }
2431
2432
    // return error
2433
    return empty($err) === false ? $err : true;
2434
}
2435
2436
/**
2437
 * Decrypt a file with Defuse.
2438
 *
2439
 * @param string $source_file path to source file
2440
 * @param string $target_file path to target file
2441
 * @param array  $SETTINGS    Settings
2442
 * @param string $password    A password
2443
 *
2444
 * @return string|bool
2445
 */
2446
function defuseFileDecrypt(
2447
    $source_file,
2448
    $target_file,
2449
    $SETTINGS,
2450
    $password = null
2451
) {
2452
    // load PhpEncryption library
2453
    $path_to_encryption = '/includes/libraries/Encryption/Encryption/';
2454
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Crypto.php';
2455
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Encoding.php';
2456
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'DerivedKeys.php';
2457
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Key.php';
2458
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'KeyOrPassword.php';
2459
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'File.php';
2460
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'RuntimeTests.php';
2461
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'KeyProtectedByPassword.php';
2462
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Core.php';
2463
2464
    try {
2465
        \Defuse\Crypto\File::decryptFileWithPassword(
2466
            $source_file,
2467
            $target_file,
2468
            $password
2469
        );
2470
    } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
2471
        $err = 'wrong_key';
2472
    } catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) {
2473
        $err = $ex;
2474
    } catch (Defuse\Crypto\Exception\IOException $ex) {
2475
        $err = $ex;
2476
    }
2477
2478
    // return error
2479
    return empty($err) === false ? $err : true;
2480
}
2481
2482
/*
2483
* NOT TO BE USED
2484
*/
2485
/**
2486
 * Undocumented function.
2487
 *
2488
 * @param string $text Text to debug
2489
 */
2490
function debugTeampass($text)
2491
{
2492
    $debugFile = fopen('D:/wamp64/www/TeamPass/debug.txt', 'r+');
2493
    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

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

2494
    fclose(/** @scrutinizer ignore-type */ $debugFile);
Loading history...
2495
}
2496
2497
/**
2498
 * DELETE the file with expected command depending on server type.
2499
 *
2500
 * @param string $file     Path to file
2501
 * @param array  $SETTINGS Teampass settings
2502
 */
2503
function fileDelete($file, $SETTINGS)
2504
{
2505
    // Load AntiXSS
2506
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/AntiXSS/AntiXSS.php';
2507
    $antiXss = new voku\helper\AntiXSS();
2508
2509
    $file = $antiXss->xss_clean($file);
2510
    if (is_file($file)) {
2511
        unlink($file);
2512
    }
2513
}
2514
2515
/**
2516
 * Permits to extract the file extension.
2517
 *
2518
 * @param string $file File name
2519
 *
2520
 * @return string
2521
 */
2522
function getFileExtension($file)
2523
{
2524
    if (strpos($file, '.') === false) {
2525
        return $file;
2526
    }
2527
2528
    return substr($file, strrpos($file, '.') + 1);
2529
}
2530
2531
/**
2532
 * Performs chmod operation on subfolders.
2533
 *
2534
 * @param string $dir             Parent folder
2535
 * @param int    $dirPermissions  New permission on folders
2536
 * @param int    $filePermissions New permission on files
2537
 *
2538
 * @return bool
2539
 */
2540
function chmodRecursive($dir, $dirPermissions, $filePermissions)
2541
{
2542
    $pointer_dir = opendir($dir);
2543
    $res = true;
2544
    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

2544
    while (false !== ($file = readdir(/** @scrutinizer ignore-type */ $pointer_dir))) {
Loading history...
2545
        if (($file === '.') || ($file === '..')) {
2546
            continue;
2547
        }
2548
2549
        $fullPath = $dir . '/' . $file;
2550
2551
        if (is_dir($fullPath)) {
2552
            if ($res = @chmod($fullPath, $dirPermissions)) {
2553
                $res = @chmodRecursive($fullPath, $dirPermissions, $filePermissions);
2554
            }
2555
        } else {
2556
            $res = chmod($fullPath, $filePermissions);
2557
        }
2558
        if (!$res) {
2559
            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

2559
            closedir(/** @scrutinizer ignore-type */ $pointer_dir);
Loading history...
2560
2561
            return false;
2562
        }
2563
    }
2564
    closedir($pointer_dir);
2565
    if (is_dir($dir) && $res) {
2566
        $res = @chmod($dir, $dirPermissions);
2567
    }
2568
2569
    return $res;
2570
}
2571
2572
/**
2573
 * Check if user can access to this item.
2574
 *
2575
 * @param int $item_id ID of item
2576
 */
2577
function accessToItemIsGranted($item_id, $SETTINGS)
2578
{
2579
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
2580
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
2581
2582
    // Prepare superGlobal variables
2583
    $session_groupes_visibles = $superGlobal->get('groupes_visibles', 'SESSION');
2584
    $session_list_restricted_folders_for_items = $superGlobal->get('list_restricted_folders_for_items', 'SESSION');
2585
2586
    // Load item data
2587
    $data = DB::queryFirstRow(
2588
        'SELECT id_tree
2589
        FROM ' . prefixTable('items') . '
2590
        WHERE id = %i',
2591
        $item_id
2592
    );
2593
2594
    // Check if user can access this folder
2595
    if (in_array($data['id_tree'], $session_groupes_visibles) === false) {
2596
        // Now check if this folder is restricted to user
2597
        if (isset($session_list_restricted_folders_for_items[$data['id_tree']])
2598
            && !in_array($item_id, $session_list_restricted_folders_for_items[$data['id_tree']])
2599
        ) {
2600
            return 'ERR_FOLDER_NOT_ALLOWED';
2601
        } else {
2602
            return 'ERR_FOLDER_NOT_ALLOWED';
2603
        }
2604
    }
2605
2606
    return true;
2607
}
2608
2609
/**
2610
 * Creates a unique key.
2611
 *
2612
 * @param float $lenght Key lenght
2613
 *
2614
 * @return string
2615
 */
2616
function uniqidReal($lenght = 13)
2617
{
2618
    // uniqid gives 13 chars, but you could adjust it to your needs.
2619
    if (function_exists('random_bytes')) {
2620
        $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

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

2622
        $bytes = openssl_random_pseudo_bytes(/** @scrutinizer ignore-type */ ceil($lenght / 2));
Loading history...
2623
    } else {
2624
        throw new Exception('no cryptographically secure random function available');
2625
    }
2626
2627
    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

2627
    return substr(bin2hex($bytes), 0, /** @scrutinizer ignore-type */ $lenght);
Loading history...
2628
}
2629
2630
/**
2631
 * Obfuscate an email.
2632
 *
2633
 * @param string $email Email address
2634
 *
2635
 * @return string
2636
 */
2637
function obfuscateEmail($email)
2638
{
2639
    $prop = 2;
2640
    $start = '';
2641
    $end = '';
2642
    $domain = substr(strrchr($email, '@'), 1);
2643
    $mailname = str_replace($domain, '', $email);
2644
    $name_l = strlen($mailname);
2645
    $domain_l = strlen($domain);
2646
    for ($i = 0; $i <= $name_l / $prop - 1; ++$i) {
2647
        $start .= 'x';
2648
    }
2649
2650
    for ($i = 0; $i <= $domain_l / $prop - 1; ++$i) {
2651
        $end .= 'x';
2652
    }
2653
2654
    return substr_replace($mailname, $start, 2, $name_l / $prop)
2655
        . substr_replace($domain, $end, 2, $domain_l / $prop);
2656
}
2657
2658
/**
2659
 * Permits to get LDAP information about a user.
2660
 *
2661
 * @param string $username User name
2662
 * @param string $password User password
2663
 * @param array  $SETTINGS Settings
2664
 *
2665
 * @return string
2666
 */
2667
function connectLDAP($username, $password, $SETTINGS)
2668
{
2669
    $ldapInfo = '';
2670
2671
    // Prepare LDAP connection if set up
2672
2673
    if ($SETTINGS['ldap_type'] === 'posix-search') {
2674
        $ldapInfo = ldapPosixSearch(
2675
            $username,
2676
            $password,
2677
            $SETTINGS
2678
        );
2679
    } else {
2680
        $ldapInfo = ldapPosixAndWindows(
2681
            $username,
2682
            $password,
2683
            $SETTINGS
2684
        );
2685
    }
2686
2687
    return json_encode($ldapInfo);
2688
}
2689
2690
/**
2691
 * Undocumented function.
2692
 *
2693
 * @param string $username Username
2694
 * @param string $password Password
2695
 * @param array  $SETTINGS Settings
2696
 *
2697
 * @return array
2698
 */
2699
function ldapPosixSearch($username, $password, $SETTINGS)
2700
{
2701
    $ldapURIs = '';
2702
    $user_email = '';
2703
    $user_found = false;
2704
    $user_lastname = '';
2705
    $user_name = '';
2706
    $ldapConnection = false;
2707
2708
    foreach (explode(',', $SETTINGS['ldap_domain_controler']) as $domainControler) {
2709
        if ($SETTINGS['ldap_ssl'] == 1) {
2710
            $ldapURIs .= 'ldaps://' . $domainControler . ':' . $SETTINGS['ldap_port'] . ' ';
2711
        } else {
2712
            $ldapURIs .= 'ldap://' . $domainControler . ':' . $SETTINGS['ldap_port'] . ' ';
2713
        }
2714
    }
2715
    $ldapconn = ldap_connect($ldapURIs);
2716
2717
    if ($SETTINGS['ldap_tls']) {
2718
        ldap_start_tls($ldapconn);
2719
    }
2720
    ldap_set_option($ldapconn, LDAP_OPT_PROTOCOL_VERSION, 3);
2721
    ldap_set_option($ldapconn, LDAP_OPT_REFERRALS, 0);
2722
2723
    // Is LDAP connection ready?
2724
    if ($ldapconn !== false) {
2725
        // Should we bind the connection?
2726
        if (empty($SETTINGS['ldap_bind_dn']) === false
2727
            && empty($SETTINGS['ldap_bind_passwd']) === false
2728
        ) {
2729
            $ldapbind = ldap_bind($ldapconn, $SETTINGS['ldap_bind_dn'], $SETTINGS['ldap_bind_passwd']);
2730
        } else {
2731
            $ldapbind = false;
2732
        }
2733
        if ((empty($SETTINGS['ldap_bind_dn']) === true && empty($SETTINGS['ldap_bind_passwd']) === true)
2734
            || $ldapbind === true
2735
        ) {
2736
            $filter = '(&(' . $SETTINGS['ldap_user_attribute'] . '=' . $username . ')(objectClass=' . $SETTINGS['ldap_object_class'] . '))';
2737
            $result = ldap_search(
2738
                $ldapconn,
2739
                $SETTINGS['ldap_search_base'],
2740
                $filter,
2741
                array('dn', 'mail', 'givenname', 'sn', 'samaccountname')
2742
            );
2743
2744
            // Check if user was found in AD
2745
            if (ldap_count_entries($ldapconn, $result) > 0) {
2746
                // Get user's info and especially the DN
2747
                $result = ldap_get_entries($ldapconn, $result);
2748
                $user_dn = $result[0]['dn'];
2749
                $user_email = $result[0]['mail'][0];
2750
                $user_lastname = $result[0]['sn'][0];
2751
                $user_name = isset($result[0]['givenname'][0]) === true ? $result[0]['givenname'][0] : '';
2752
                $user_found = true;
2753
2754
                // Should we restrain the search in specified user groups
2755
                $GroupRestrictionEnabled = false;
2756
                if (isset($SETTINGS['ldap_usergroup']) === true
2757
                    && empty($SETTINGS['ldap_usergroup']) === false
2758
                ) {
2759
                    // New way to check User's group membership
2760
                    $filter_group = 'memberUid=' . $username;
2761
                    $result_group = ldap_search(
2762
                        $ldapconn,
2763
                        $SETTINGS['ldap_search_base'],
2764
                        $filter_group,
2765
                        array('dn', 'samaccountname')
2766
                    );
2767
2768
                    if ($result_group) {
0 ignored issues
show
introduced by
$result_group is of type resource, thus it always evaluated to false.
Loading history...
2769
                        $entries = ldap_get_entries($ldapconn, $result_group);
2770
2771
                        if ($entries['count'] > 0) {
2772
                            // Now check if group fits
2773
                            for ($i = 0; $i < $entries['count']; ++$i) {
2774
                                $parsr = ldap_explode_dn($entries[$i]['dn'], 0);
2775
                                if (str_replace(array('CN=', 'cn='), '', $parsr[0]) === $SETTINGS['ldap_usergroup']) {
2776
                                    $GroupRestrictionEnabled = true;
2777
                                    break;
2778
                                }
2779
                            }
2780
                        }
2781
                    }
2782
                }
2783
2784
                // Is user in the LDAP?
2785
                if ($GroupRestrictionEnabled === true
2786
                    || ($GroupRestrictionEnabled === false
2787
                    && (isset($SETTINGS['ldap_usergroup']) === false
2788
                    || (isset($SETTINGS['ldap_usergroup']) === true
2789
                    && empty($SETTINGS['ldap_usergroup']) === true)))
2790
                ) {
2791
                    // Try to auth inside LDAP
2792
                    $ldapbind = ldap_bind($ldapconn, $user_dn, $password);
2793
                    if ($ldapbind === true) {
2794
                        $ldapConnection = true;
2795
                    } else {
2796
                        $ldapConnection = false;
2797
                    }
2798
                }
2799
            } else {
2800
                $ldapConnection = false;
2801
            }
2802
        } else {
2803
            $ldapConnection = false;
2804
        }
2805
    } else {
2806
        $ldapConnection = false;
2807
    }
2808
2809
    return array(
2810
        'lastname' => $user_lastname,
2811
        'name' => $user_name,
2812
        'email' => $user_email,
2813
        'auth_success' => $ldapConnection,
2814
        'user_found' => $user_found,
2815
    );
2816
}
2817
2818
/**
2819
 * Undocumented function.
2820
 *
2821
 * @param string $username Username
2822
 * @param string $password Password
2823
 * @param array  $SETTINGS Settings
2824
 *
2825
 * @return array
2826
 */
2827
function ldapPosixAndWindows($username, $password, $SETTINGS)
2828
{
2829
    $user_email = '';
2830
    $user_found = false;
2831
    $user_lastname = '';
2832
    $user_name = '';
2833
    $ldapConnection = false;
2834
    $ldap_suffix = '';
2835
2836
    //Multiple Domain Names
2837
    if (strpos(html_entity_decode($username), '\\') === true) {
2838
        $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...
2839
        $username = substr(html_entity_decode($username), strpos(html_entity_decode($username), '\\') + 1);
2840
    }
2841
    //load ClassLoader
2842
    include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php';
2843
2844
    $adldap = new SplClassLoader('adLDAP', '../includes/libraries/LDAP');
2845
    $adldap->register();
2846
2847
    // Posix style LDAP handles user searches a bit differently
2848
    if ($SETTINGS['ldap_type'] === 'posix') {
2849
        $ldap_suffix = ',' . $SETTINGS['ldap_suffix'] . ',' . $SETTINGS['ldap_domain_dn'];
2850
    } else {
2851
        // case where $SETTINGS['ldap_type'] equals 'windows'
2852
        //Multiple Domain Names
2853
        $ldap_suffix = $SETTINGS['ldap_suffix'];
2854
    }
2855
2856
    // Ensure no double commas exist in ldap_suffix
2857
    $ldap_suffix = str_replace(',,', ',', $ldap_suffix);
2858
2859
    // Create LDAP connection
2860
    $adldap = new adLDAP\adLDAP(
2861
        array(
2862
            'base_dn' => $SETTINGS['ldap_domain_dn'],
2863
            'account_suffix' => $ldap_suffix,
2864
            'domain_controllers' => explode(',', $SETTINGS['ldap_domain_controler']),
2865
            'ad_port' => $SETTINGS['ldap_port'],
2866
            'use_ssl' => $SETTINGS['ldap_ssl'],
2867
            'use_tls' => $SETTINGS['ldap_tls'],
2868
        )
2869
    );
2870
2871
    // OpenLDAP expects an attribute=value pair
2872
    if ($SETTINGS['ldap_type'] === 'posix') {
2873
        $auth_username = $SETTINGS['ldap_user_attribute'] . '=' . $username;
2874
    } else {
2875
        $auth_username = $username;
2876
    }
2877
2878
    // Authenticate the user
2879
    if ($adldap->authenticate($auth_username, html_entity_decode($password))) {
2880
        // Get user info
2881
        $result = $adldap->user()->info($auth_username, array('mail', 'givenname', 'sn'));
2882
        $user_email = $result[0]['mail'][0];
2883
        $user_lastname = $result[0]['sn'][0];
2884
        $user_name = $result[0]['givenname'][0];
2885
        $user_found = true;
2886
2887
        // Is user in allowed group
2888
        if (isset($SETTINGS['ldap_allowed_usergroup']) === true
2889
            && empty($SETTINGS['ldap_allowed_usergroup']) === false
2890
        ) {
2891
            if ($adldap->user()->inGroup($auth_username, $SETTINGS['ldap_allowed_usergroup']) === true) {
2892
                $ldapConnection = true;
2893
            } else {
2894
                $ldapConnection = false;
2895
            }
2896
        } else {
2897
            $ldapConnection = true;
2898
        }
2899
    } else {
2900
        $ldapConnection = false;
2901
    }
2902
2903
    return array(
2904
        'lastname' => $user_lastname,
2905
        'name' => $user_name,
2906
        'email' => $user_email,
2907
        'auth_success' => $ldapConnection,
2908
        'user_found' => $user_found,
2909
    );
2910
}
2911
2912
2913
/**
2914
 * Perform a Query.
2915
 *
2916
 * @param array  $SETTINGS Teamapss settings
2917
 * @param string $fields   Fields to use
2918
 * @param string $table    Table to use
2919
 *
2920
 * @return array
2921
 */
2922
function performDBQuery($SETTINGS, $fields, $table)
2923
{
2924
    // include librairies & connect to DB
2925
    include_once $SETTINGS['cpassman_dir'] . '/includes/config/settings.php';
2926
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
2927
    if (defined('DB_PASSWD_CLEAR') === false) {
2928
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
0 ignored issues
show
Bug introduced by
The constant DB_PASSWD was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2929
    }
2930
    DB::$host = DB_HOST;
0 ignored issues
show
Bug introduced by
The constant DB_HOST was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2931
    DB::$user = DB_USER;
0 ignored issues
show
Bug introduced by
The constant DB_USER was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2932
    DB::$password = DB_PASSWD_CLEAR;
2933
    DB::$dbName = DB_NAME;
0 ignored issues
show
Bug introduced by
The constant DB_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2934
    DB::$port = DB_PORT;
0 ignored issues
show
Bug introduced by
The constant DB_PORT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2935
    DB::$encoding = DB_ENCODING;
2936
2937
    // Insert log in DB
2938
    return DB::query(
2939
        'SELECT ' . $fields . '
2940
        FROM ' . prefixTable($table)
2941
    );
2942
}
2943
2944
/**
2945
 * Undocumented function.
2946
 *
2947
 * @param int $bytes Size of file
2948
 *
2949
 * @return string
2950
 */
2951
function formatSizeUnits($bytes)
2952
{
2953
    if ($bytes >= 1073741824) {
2954
        $bytes = number_format($bytes / 1073741824, 2) . ' GB';
2955
    } elseif ($bytes >= 1048576) {
2956
        $bytes = number_format($bytes / 1048576, 2) . ' MB';
2957
    } elseif ($bytes >= 1024) {
2958
        $bytes = number_format($bytes / 1024, 2) . ' KB';
2959
    } elseif ($bytes > 1) {
2960
        $bytes = $bytes . ' bytes';
2961
    } elseif ($bytes == 1) {
2962
        $bytes = $bytes . ' byte';
2963
    } else {
2964
        $bytes = '0 bytes';
2965
    }
2966
2967
    return $bytes;
2968
}
2969
2970
/**
2971
 * Generate user pair of keys.
2972
 *
2973
 * @param string $userPwd User password
2974
 *
2975
 * @return array
2976
 */
2977
function generateUserKeys($userPwd)
2978
{
2979
    // include library
2980
    include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php';
2981
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php';
2982
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
2983
2984
    // Load classes
2985
    $rsa = new Crypt_RSA();
2986
    $cipher = new Crypt_AES();
2987
2988
    // Create the private and public key
2989
    $res = $rsa->createKey(4096);
2990
2991
    // Encrypt the privatekey
2992
    $cipher->setPassword($userPwd);
2993
    $privatekey = $cipher->encrypt($res['privatekey']);
2994
2995
    return array(
2996
        'private_key' => base64_encode($privatekey),
2997
        'public_key' => base64_encode($res['publickey']),
2998
        'private_key_clear' => base64_encode($res['privatekey']),
2999
    );
3000
}
3001
3002
/**
3003
 * Permits to decrypt the user's privatekey.
3004
 *
3005
 * @param string $userPwd        User password
3006
 * @param string $userPrivateKey User private key
3007
 *
3008
 * @return string
3009
 */
3010
function decryptPrivateKey($userPwd, $userPrivateKey)
3011
{
3012
    if (empty($userPwd) === false) {
3013
        include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
3014
3015
        // Load classes
3016
        $cipher = new Crypt_AES();
3017
3018
        // Encrypt the privatekey
3019
        $cipher->setPassword($userPwd);
3020
3021
        return base64_encode($cipher->decrypt(base64_decode($userPrivateKey)));
3022
    }
3023
}
3024
3025
/**
3026
 * Permits to encrypt the user's privatekey.
3027
 *
3028
 * @param string $userPwd        User password
3029
 * @param string $userPrivateKey User private key
3030
 *
3031
 * @return string
3032
 */
3033
function encryptPrivateKey($userPwd, $userPrivateKey)
3034
{
3035
    if (empty($userPwd) === false) {
3036
        include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
3037
3038
        // Load classes
3039
        $cipher = new Crypt_AES();
3040
3041
        // Encrypt the privatekey
3042
        $cipher->setPassword($userPwd);
3043
3044
        return base64_encode($cipher->encrypt(base64_decode($userPrivateKey)));
3045
    }
3046
}
3047
3048
3049
/**
3050
 * Encrypts a string using AES.
3051
 *
3052
 * @param string $data String to encrypt
3053
 *
3054
 * @return array
3055
 */
3056
function doDataEncryption($data)
3057
{
3058
    // Includes
3059
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
3060
3061
    // Load classes
3062
    $cipher = new Crypt_AES(CRYPT_AES_MODE_CBC);
3063
3064
    // Generate an object key
3065
    $objectKey = uniqidReal(32);
3066
3067
    // Set it as password
3068
    $cipher->setPassword($objectKey);
3069
3070
    return array(
3071
        'encrypted' => base64_encode($cipher->encrypt($data)),
3072
        'objectKey' => base64_encode($objectKey),
3073
    );
3074
}
3075
3076
/**
3077
 * Decrypts a string using AES.
3078
 *
3079
 * @param string $data Encrypted data
3080
 * @param string $key  Key to uncrypt
3081
 *
3082
 * @return string
3083
 */
3084
function doDataDecryption($data, $key)
3085
{
3086
    // Includes
3087
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
3088
3089
    // Load classes
3090
    $cipher = new Crypt_AES();
3091
3092
    // Set the object key
3093
    $cipher->setPassword(base64_decode($key));
3094
3095
    return base64_encode($cipher->decrypt(base64_decode($data)));
3096
}
3097
3098
/**
3099
 * Encrypts using RSA a string using a public key.
3100
 *
3101
 * @param string $key       Key to be encrypted
3102
 * @param string $publicKey User public key
3103
 *
3104
 * @return string
3105
 */
3106
function encryptUserObjectKey($key, $publicKey)
3107
{
3108
    // Includes
3109
    include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php';
3110
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php';
3111
3112
    // Load classes
3113
    $rsa = new Crypt_RSA();
3114
    $rsa->loadKey(base64_decode($publicKey));
3115
3116
    // Encrypt
3117
    return base64_encode($rsa->encrypt(base64_decode($key)));
3118
}
3119
3120
/**
3121
 * Decrypts using RSA an encrypted string using a private key.
3122
 *
3123
 * @param string $key        Encrypted key
3124
 * @param string $privateKey User private key
3125
 *
3126
 * @return string
3127
 */
3128
function decryptUserObjectKey($key, $privateKey)
3129
{
3130
    // Includes
3131
    include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php';
3132
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php';
3133
3134
    // Load classes
3135
    $rsa = new Crypt_RSA();
3136
    $rsa->loadKey(base64_decode($privateKey));
3137
3138
    // Decrypt
3139
    return base64_encode($rsa->decrypt(base64_decode($key)));
3140
}
3141
3142
/**
3143
 * Encrypts a file.
3144
 *
3145
 * @param string $fileInName File name
3146
 * @param string $fileInPath Path to file
3147
 *
3148
 * @return array
3149
 */
3150
function encryptFile($fileInName, $fileInPath)
3151
{
3152
    if (defined('FILE_BUFFER_SIZE') === false) {
3153
        define('FILE_BUFFER_SIZE', 128 * 1024);
3154
    }
3155
3156
    // Includes
3157
    include_once '../includes/config/include.php';
3158
    include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php';
3159
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php';
3160
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
3161
3162
    // Load classes
3163
    $cipher = new Crypt_AES();
3164
3165
    // Generate an object key
3166
    $objectKey = uniqidReal(32);
3167
3168
    // Set it as password
3169
    $cipher->setPassword($objectKey);
3170
3171
    // Prevent against out of memory
3172
    $cipher->enableContinuousBuffer();
3173
    $cipher->disablePadding();
3174
3175
    // Encrypt the file content
3176
    $plaintext = file_get_contents(
3177
        filter_var($fileInPath . '/' . $fileInName, FILTER_SANITIZE_URL)
3178
    );
3179
    $ciphertext = $cipher->encrypt($plaintext);
3180
3181
    // Save new file
3182
    $hash = md5($plaintext);
3183
    $fileOut = $fileInPath . '/' . TP_FILE_PREFIX . $hash;
3184
    file_put_contents($fileOut, $ciphertext);
0 ignored issues
show
Security File Manipulation introduced by
$ciphertext can contain request data and is used in file manipulation context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read tainted data from array, and $_SERVER['PHP_AUTH_PW'] is assigned to $passwordClear
    in sources/identify.php on line 383
  2. decryptPrivateKey() is called
    in sources/identify.php on line 919
  3. Enters via parameter $userPwd
    in sources/main.functions.php on line 3010
  4. Crypt_Base::setPassword() is called
    in sources/main.functions.php on line 3019
  5. Enters via parameter $password
    in includes/libraries/Encryption/phpseclib/Crypt/Base.php on line 645
  6. $password . $salt is assigned to $t
    in includes/libraries/Encryption/phpseclib/Crypt/Base.php on line 681
  7. Data is passed through substr(), and substr($t, 0, $dkLen) is assigned to $key
    in includes/libraries/Encryption/phpseclib/Crypt/Base.php on line 685
  8. Data is passed through substr(), and Crypt_Base::setIV() is called
    in includes/libraries/Encryption/phpseclib/Crypt/Base.php on line 688
  9. Enters via parameter $iv
    in includes/libraries/Encryption/phpseclib/Crypt/Base.php on line 556
  10. $iv is assigned to property Crypt_AES::$iv
    in includes/libraries/Encryption/phpseclib/Crypt/Base.php on line 562
  11. Read from property Crypt_AES::$iv, and Data is passed through substr(), and Data is passed through str_pad(), and $this->decryptIV = str_pad(substr($this->iv, 0, $this->block_size), $this->block_size, '') is assigned to property Crypt_AES::$encryptIV
    in includes/libraries/Encryption/phpseclib/Crypt/Base.php on line 1944
  12. Read from property Crypt_AES::$encryptIV, and Data is passed through _openssl_ctr_process(), and $this->_openssl_ctr_process($plaintext, $this->encryptIV, $this->enbuffer) is returned
    in includes/libraries/Encryption/phpseclib/Crypt/Base.php on line 769
  13. $cipher->encrypt($plaintext) is assigned to $ciphertext
    in sources/main.functions.php on line 3179

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
3185
    unlink($fileInPath . '/' . $fileInName);
3186
3187
    return array(
3188
        'fileHash' => base64_encode($hash),
3189
        'objectKey' => base64_encode($objectKey),
3190
    );
3191
}
3192
3193
/**
3194
 * Decrypt a file.
3195
 *
3196
 * @param string $fileName File name
3197
 * @param string $filePath Path to file
3198
 * @param string $key      Key to use
3199
 *
3200
 * @return string
3201
 */
3202
function decryptFile($fileName, $filePath, $key)
3203
{
3204
    if (!defined('FILE_BUFFER_SIZE')) {
3205
        define('FILE_BUFFER_SIZE', 128 * 1024);
3206
    }
3207
3208
    // Includes
3209
    include_once '../includes/config/include.php';
3210
    include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php';
3211
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php';
3212
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
3213
3214
    // Get file name
3215
    $fileName = base64_decode($fileName);
3216
3217
    // Load classes
3218
    $cipher = new Crypt_AES();
3219
3220
    // Set the object key
3221
    $cipher->setPassword(base64_decode($key));
3222
3223
    // Prevent against out of memory
3224
    $cipher->enableContinuousBuffer();
3225
    $cipher->disablePadding();
3226
3227
    // Get file content
3228
    $ciphertext = file_get_contents($filePath . '/' . TP_FILE_PREFIX . $fileName);
3229
3230
    // Decrypt file content and return
3231
    return base64_encode($cipher->decrypt($ciphertext));
3232
}
3233
3234
/**
3235
 * Generate a simple password
3236
 *
3237
 * @param integer $length          Length of string
3238
 * @param boolean $symbolsincluded Allow symbols
3239
 *
3240
 * @return string
3241
 */
3242
function generateQuickPassword($length = 16, $symbolsincluded = true)
3243
{
3244
    // Generate new user password
3245
    $small_letters = range('a', 'z');
3246
    $big_letters = range('A', 'Z');
3247
    $digits = range(0, 9);
3248
    $symbols = $symbolsincluded === true ?
3249
        array('#', '_', '-', '@', '$', '+', '&') : array();
3250
3251
    $res = array_merge($small_letters, $big_letters, $digits, $symbols);
3252
    $count = count($res);
3253
    // first variant
3254
3255
    $random_string = '';
3256
    for ($i = 0; $i < $length; ++$i) {
3257
        $random_string .= $res[random_int(0, $count - 1)];
3258
    }
3259
3260
    return $random_string;
3261
}
3262
3263
/**
3264
 * Permit to store the sharekey of an object for users.
3265
 *
3266
 * @param string $object_name             Type for table selection
3267
 * @param int    $post_folder_is_personal Personal
3268
 * @param int    $post_folder_id          Folder
3269
 * @param int    $post_object_id          Object
3270
 * @param string $objectKey               Object key
3271
 * @param array  $SETTINGS                Teampass settings
3272
 *
3273
 * @return coid
0 ignored issues
show
Bug introduced by
The type coid was not found. Maybe you did not declare it correctly or list all dependencies?

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

filter:
    dependency_paths: ["lib/*"]

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

Loading history...
3274
 */
3275
function storeUsersShareKey(
3276
    $object_name,
3277
    $post_folder_is_personal,
3278
    $post_folder_id,
3279
    $post_object_id,
3280
    $objectKey,
3281
    $SETTINGS
3282
) {
3283
    // include librairies & connect to DB
3284
    include_once $SETTINGS['cpassman_dir'] . '/includes/config/settings.php';
3285
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
3286
    if (defined('DB_PASSWD_CLEAR') === false) {
3287
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
0 ignored issues
show
Bug introduced by
The constant DB_PASSWD was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3288
    }
3289
    DB::$host = DB_HOST;
0 ignored issues
show
Bug introduced by
The constant DB_HOST was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3290
    DB::$user = DB_USER;
0 ignored issues
show
Bug introduced by
The constant DB_USER was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3291
    DB::$password = DB_PASSWD_CLEAR;
3292
    DB::$dbName = DB_NAME;
0 ignored issues
show
Bug introduced by
The constant DB_NAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3293
    DB::$port = DB_PORT;
0 ignored issues
show
Bug introduced by
The constant DB_PORT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
3294
    DB::$encoding = DB_ENCODING;
3295
3296
    // Delete existing entries for this object
3297
    DB::delete(
3298
        $object_name,
3299
        'object_id = %i',
3300
        $post_object_id
3301
    );
3302
    
3303
    // Superglobals
3304
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
3305
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
3306
3307
    // Prepare superGlobal variables
3308
    $sessionPpersonaFolders = $superGlobal->get('personal_folders', 'SESSION');
3309
    $sessionUserId = $superGlobal->get('user_id', 'SESSION');
3310
3311
    if ((int) $post_folder_is_personal === 1
3312
        && in_array($post_folder_id, $sessionPpersonaFolders) === true
3313
    ) {
3314
        // If this is a personal object
3315
        // Only create the sharekey for user
3316
        DB::insert(
3317
            $object_name,
3318
            array(
3319
                'object_id' => $post_object_id,
3320
                'user_id' => $sessionUserId,
3321
                'share_key' => encryptUserObjectKey($objectKey, $_SESSION['user']['public_key']),
3322
            )
3323
        );
3324
    } else {
3325
        // This is a public object
3326
        // Create sharekey for each user
3327
        $users = DB::query(
3328
            'SELECT id, public_key
3329
            FROM ' . prefixTable('users') . '
3330
            WHERE id NOT IN ("' . OTV_USER_ID . '","' . SSH_USER_ID . '","' . API_USER_ID . '")
3331
            AND public_key != ""'
3332
        );
3333
        foreach ($users as $user) {
3334
            // Insert in DB the new object key for this item by user
3335
            DB::insert(
3336
                $object_name,
3337
                array(
3338
                    'object_id' => $post_object_id,
3339
                    'user_id' => $user['id'],
3340
                    'share_key' => encryptUserObjectKey(
3341
                        $objectKey,
3342
                        $user['public_key']
3343
                    ),
3344
                )
3345
            );
3346
        }
3347
    }
3348
}
3349
3350
/**
3351
 * Is this string base64 encoded?
3352
 *
3353
 * @param string $str Encoded string?
3354
 *
3355
 * @return bool
3356
 */
3357
function isBase64($str)
3358
{
3359
    $str = (string) trim($str);
3360
3361
    if (!isset($str[0])) {
3362
        return false;
3363
    }
3364
3365
    $base64String = (string) base64_decode($str, true);
3366
    if ($base64String && base64_encode($base64String) === $str) {
3367
        return true;
3368
    }
3369
3370
    return false;
3371
}
3372
3373
/**
3374
 * Undocumented function
3375
 *
3376
 * @param string $field Parameter
3377
 *
3378
 * @return string|boolean
3379
 */
3380
function filterString($field)
3381
{
3382
    // Sanitize string
3383
    $field = filter_var(trim($field), FILTER_SANITIZE_STRING);
3384
    if (empty($field) === false) {
3385
        // Load AntiXSS
3386
        include_once '../includes/libraries/protect/AntiXSS/AntiXSS.php';
3387
        $antiXss = new voku\helper\AntiXSS();
3388
        // Return
3389
        return $antiXss->xss_clean($field);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $antiXss->xss_clean($field) also could return the type array which is incompatible with the documented return type boolean|string.
Loading history...
3390
    } else {
3391
        return false;
3392
    }
3393
}