Passed
Push — teampass_3.0 ( 9a4138...c4a889 )
by Nils
03:02
created

storeUsersShareKey()   A

Complexity

Conditions 5
Paths 6

Size

Total Lines 60
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
cc 5
eloc 36
c 4
b 0
f 0
nc 6
nop 6
dl 0
loc 60
rs 9.0328

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

598
            /** @scrutinizer ignore-type */ $tree
Loading history...
599
        );
600
    } else {
601
        identUser(
602
            $groupesVisiblesUser,
603
            $groupesInterditsUser,
604
            $idFonctions,
605
            $SETTINGS,
606
            $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

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

1626
                /** @scrutinizer ignore-type */ $data,
Loading history...
1627
                true
1628
            );
1629
        } else {
1630
            return json_decode(
1631
                Encryption\Crypt\aesctr::decrypt(
1632
                    $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

1632
                    /** @scrutinizer ignore-type */ $data,
Loading history...
1633
                    $_SESSION['key'],
1634
                    256
1635
                ),
1636
                true
1637
            );
1638
        }
1639
    }
1640
}
1641
1642
/**
1643
 * Create a thumbnail.
1644
 *
1645
 * @param string $src           Source
1646
 * @param string $dest          Destination
1647
 * @param float  $desired_width Size of width
1648
 */
1649
function makeThumbnail($src, $dest, $desired_width)
1650
{
1651
    /* read the source image */
1652
    $source_image = imagecreatefrompng($src);
1653
    $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

1653
    $width = imagesx(/** @scrutinizer ignore-type */ $source_image);
Loading history...
1654
    $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

1654
    $height = imagesy(/** @scrutinizer ignore-type */ $source_image);
Loading history...
1655
1656
    /* find the "desired height" of this thumbnail, relative to the desired width  */
1657
    $desired_height = floor($height * ($desired_width / $width));
1658
1659
    /* create a new, "virtual" image */
1660
    $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

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

1660
    $virtual_image = imagecreatetruecolor($desired_width, /** @scrutinizer ignore-type */ $desired_height);
Loading history...
1661
1662
    /* copy source image at a resized size */
1663
    imagecopyresampled($virtual_image, $source_image, 0, 0, 0, 0, $desired_width, $desired_height, $width, $height);
0 ignored issues
show
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

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

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

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

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

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

1663
    imagecopyresampled(/** @scrutinizer ignore-type */ $virtual_image, $source_image, 0, 0, 0, 0, $desired_width, $desired_height, $width, $height);
Loading history...
1664
1665
    /* create the physical thumbnail image to its destination */
1666
    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

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

2274
                /** @scrutinizer ignore-type */ \Defuse\Crypto\Key::loadFromAsciiSafeString($ascii_key),
Loading history...
2275
                $SETTINGS
0 ignored issues
show
Bug introduced by
$SETTINGS of type array 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

2275
                /** @scrutinizer ignore-type */ $SETTINGS
Loading history...
2276
            );
2277
        // ---
2278
        } elseif ($type === 'encrypt') {
2279
            // Encrypt file
2280
            $err = defuseFileEncrypt(
2281
                $source_file,
2282
                $target_file,
2283
                \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 array expected by parameter $SETTINGS 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

2283
                /** @scrutinizer ignore-type */ \Defuse\Crypto\Key::loadFromAsciiSafeString($ascii_key),
Loading history...
2284
                $SETTINGS
0 ignored issues
show
Bug introduced by
$SETTINGS of type array 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

2284
                /** @scrutinizer ignore-type */ $SETTINGS
Loading history...
2285
            );
2286
        }
2287
    } else {
2288
        /*
2289
        File encryption/decryption is done with special password and not the SALTKEY
2290
         */
2291
2292
        $err = '';
2293
        if ($type === 'decrypt') {
2294
            // Decrypt file
2295
            $err = defuseFileDecrypt(
2296
                $source_file,
2297
                $target_file,
2298
                $password,
0 ignored issues
show
Bug introduced by
$password of type string is incompatible with the type array expected by parameter $SETTINGS 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

2298
                /** @scrutinizer ignore-type */ $password,
Loading history...
2299
                $SETTINGS
2300
            );
2301
        // ---
2302
        } elseif ($type === 'encrypt') {
2303
            // Encrypt file
2304
            $err = defuseFileEncrypt(
2305
                $source_file,
2306
                $target_file,
2307
                $password,
0 ignored issues
show
Bug introduced by
$password of type string is incompatible with the type array expected by parameter $SETTINGS 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

2307
                /** @scrutinizer ignore-type */ $password,
Loading history...
2308
                $SETTINGS
2309
            );
2310
        }
2311
    }
2312
2313
    // return error
2314
    return empty($err) === false ? $err : true;
2315
}
2316
2317
/**
2318
 * Encrypt a file with Defuse.
2319
 *
2320
 * @param string $source_file path to source file
2321
 * @param string $target_file path to target file
2322
 * @param array  $SETTINGS    Settings
2323
 * @param string $password    A password
2324
 *
2325
 * @return string|bool
2326
 */
2327
function defuseFileEncrypt(
2328
    $source_file,
2329
    $target_file,
2330
    $SETTINGS,
2331
    $password = null
2332
) {
2333
    // load PhpEncryption library
2334
    $path_to_encryption = '/includes/libraries/Encryption/Encryption/';
2335
    include_once $SETTINGS['cpassman_dir'].$path_to_encryption.'Crypto.php';
2336
    include_once $SETTINGS['cpassman_dir'].$path_to_encryption.'Encoding.php';
2337
    include_once $SETTINGS['cpassman_dir'].$path_to_encryption.'DerivedKeys.php';
2338
    include_once $SETTINGS['cpassman_dir'].$path_to_encryption.'Key.php';
2339
    include_once $SETTINGS['cpassman_dir'].$path_to_encryption.'KeyOrPassword.php';
2340
    include_once $SETTINGS['cpassman_dir'].$path_to_encryption.'File.php';
2341
    include_once $SETTINGS['cpassman_dir'].$path_to_encryption.'RuntimeTests.php';
2342
    include_once $SETTINGS['cpassman_dir'].$path_to_encryption.'KeyProtectedByPassword.php';
2343
    include_once $SETTINGS['cpassman_dir'].$path_to_encryption.'Core.php';
2344
2345
    try {
2346
        \Defuse\Crypto\File::encryptFileWithPassword(
2347
            $source_file,
2348
            $target_file,
2349
            $password
2350
        );
2351
    } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
2352
        $err = 'wrong_key';
2353
    } catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) {
2354
        $err = $ex;
2355
    } catch (Defuse\Crypto\Exception\IOException $ex) {
2356
        $err = $ex;
2357
    }
2358
2359
    // return error
2360
    return empty($err) === false ? $err : true;
2361
}
2362
2363
/**
2364
 * Decrypt a file with Defuse.
2365
 *
2366
 * @param string $source_file path to source file
2367
 * @param string $target_file path to target file
2368
 * @param array  $SETTINGS    Settings
2369
 * @param string $password    A password
2370
 *
2371
 * @return string|bool
2372
 */
2373
function defuseFileDecrypt(
2374
    $source_file,
2375
    $target_file,
2376
    $SETTINGS,
2377
    $password = null
2378
) {
2379
    // load PhpEncryption library
2380
    $path_to_encryption = '/includes/libraries/Encryption/Encryption/';
2381
    include_once $SETTINGS['cpassman_dir'].$path_to_encryption.'Crypto.php';
2382
    include_once $SETTINGS['cpassman_dir'].$path_to_encryption.'Encoding.php';
2383
    include_once $SETTINGS['cpassman_dir'].$path_to_encryption.'DerivedKeys.php';
2384
    include_once $SETTINGS['cpassman_dir'].$path_to_encryption.'Key.php';
2385
    include_once $SETTINGS['cpassman_dir'].$path_to_encryption.'KeyOrPassword.php';
2386
    include_once $SETTINGS['cpassman_dir'].$path_to_encryption.'File.php';
2387
    include_once $SETTINGS['cpassman_dir'].$path_to_encryption.'RuntimeTests.php';
2388
    include_once $SETTINGS['cpassman_dir'].$path_to_encryption.'KeyProtectedByPassword.php';
2389
    include_once $SETTINGS['cpassman_dir'].$path_to_encryption.'Core.php';
2390
2391
    try {
2392
        \Defuse\Crypto\File::decryptFileWithPassword(
2393
            $source_file,
2394
            $target_file,
2395
            $password
2396
        );
2397
    } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
2398
        $err = 'wrong_key';
2399
    } catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) {
2400
        $err = $ex;
2401
    } catch (Defuse\Crypto\Exception\IOException $ex) {
2402
        $err = $ex;
2403
    }
2404
2405
    // return error
2406
    return empty($err) === false ? $err : true;
2407
}
2408
2409
/*
2410
* NOT TO BE USED
2411
*/
2412
/**
2413
 * Undocumented function.
2414
 *
2415
 * @param string $text Text to debug
2416
 */
2417
function debugTeampass($text)
2418
{
2419
    $debugFile = fopen('D:/wamp64/www/TeamPass/debug.txt', 'r+');
2420
    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

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

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

2471
    while (false !== ($file = readdir(/** @scrutinizer ignore-type */ $pointer_dir))) {
Loading history...
2472
        if (($file === '.') || ($file === '..')) {
2473
            continue;
2474
        }
2475
2476
        $fullPath = $dir.'/'.$file;
2477
2478
        if (is_dir($fullPath)) {
2479
            if ($res = @chmod($fullPath, $dirPermissions)) {
2480
                $res = @chmodRecursive($fullPath, $dirPermissions, $filePermissions);
2481
            }
2482
        } else {
2483
            $res = chmod($fullPath, $filePermissions);
2484
        }
2485
        if (!$res) {
2486
            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

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

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

2551
        $bytes = openssl_random_pseudo_bytes(/** @scrutinizer ignore-type */ ceil($lenght / 2));
Loading history...
2552
    } else {
2553
        throw new Exception('no cryptographically secure random function available');
2554
    }
2555
2556
    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

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

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...
3161
    unlink($fileInPath.'/'.$fileInName);
3162
3163
    return array(
3164
        'fileHash' => base64_encode($hash),
3165
        'objectKey' => base64_encode($objectKey),
3166
    );
3167
}
3168
3169
/**
3170
 * Decrypt a file.
3171
 *
3172
 * @param string $fileName File name
3173
 * @param string $filePath Path to file
3174
 * @param string $key      Key to use
3175
 *
3176
 * @return string
3177
 */
3178
function decryptFile($fileName, $filePath, $key)
3179
{
3180
    define('FILE_BUFFER_SIZE', 128 * 1024);
3181
3182
    // Includes
3183
    include_once '../includes/config/include.php';
3184
    include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php';
3185
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php';
3186
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
3187
3188
    // Get file name
3189
    $fileName = base64_decode($fileName);
3190
3191
    // Load classes
3192
    $cipher = new Crypt_AES();
3193
3194
    // Set the object key
3195
    $cipher->setPassword(base64_decode($key));
3196
3197
    // Prevent against out of memory
3198
    $cipher->enableContinuousBuffer();
3199
    $cipher->disablePadding();
3200
3201
    // Get file content
3202
    $ciphertext = file_get_contents($filePath.'/'.TP_FILE_PREFIX.$fileName);
3203
3204
    // Decrypt file content and return
3205
    return base64_encode($cipher->decrypt($ciphertext));
3206
}
3207
3208
/**
3209
 * Undocumented function.
3210
 *
3211
 * @param int $length Length of password
3212
 *
3213
 * @return string
3214
 */
3215
function generateQuickPassword($length = 16, $symbolsincluded = true)
3216
{
3217
    // Generate new user password
3218
    $small_letters = range('a', 'z');
3219
    $big_letters = range('A', 'Z');
3220
    $digits = range(0, 9);
3221
    $symbols = $symbolsincluded === true ?
3222
        array('#', '_', '-', '@', '$', '+', '&') :
3223
        array();
3224
3225
    $res = array_merge($small_letters, $big_letters, $digits, $symbols);
3226
    $c = count($res);
3227
    // first variant
3228
3229
    $random_string = '';
3230
    for ($i = 0; $i < $length; ++$i) {
3231
        $random_string .= $res[random_int(0, $c - 1)];
3232
    }
3233
3234
    return $random_string;
3235
}
3236
3237
/**
3238
 * Permit to store the sharekey of an object for users.
3239
 *
3240
 * @param string $object_name             Type for table selection
3241
 * @param int    $post_folder_is_personal Personal
3242
 * @param int    $post_folder_id          Folder
3243
 * @param int    $post_object_id          Object
3244
 * @param string $objectKey               Object key
3245
 * @param array  $SETTINGS                Teampass settings
3246
 */
3247
function storeUsersShareKey(
3248
    $object_name,
3249
    $post_folder_is_personal,
3250
    $post_folder_id,
3251
    $post_object_id,
3252
    $objectKey,
3253
    $SETTINGS
3254
) {
3255
    // include librairies & connect to DB
3256
    include_once $SETTINGS['cpassman_dir'].'/includes/config/settings.php';
3257
    include_once $SETTINGS['cpassman_dir'].'/includes/libraries/Database/Meekrodb/db.class.php';
3258
    if (defined('DB_PASSWD_CLEAR') === false) {
3259
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
3260
    }
3261
    DB::$host = DB_HOST;
3262
    DB::$user = DB_USER;
3263
    DB::$password = DB_PASSWD_CLEAR;
3264
    DB::$dbName = DB_NAME;
3265
    DB::$port = DB_PORT;
3266
    DB::$encoding = DB_ENCODING;
3267
3268
    // Delete existing entries for this object
3269
    DB::delete(
3270
        $object_name,
3271
        'object_id = %i',
3272
        $post_object_id
3273
    );
3274
3275
    if ((int) $post_folder_is_personal === 1
3276
        && in_array($post_folder_id, $_SESSION['personal_folders']) === true
3277
    ) {
3278
        // If this is a personal object
3279
        // Only create the sharekey for user
3280
        DB::insert(
3281
            $object_name,
3282
            array(
3283
                'object_id' => $post_object_id,
3284
                'user_id' => $_SESSION['user_id'],
3285
                'share_key' => encryptUserObjectKey($objectKey, $_SESSION['user']['public_key']),
3286
            )
3287
        );
3288
    } else {
3289
        // This is a public object
3290
        // Create sharekey for each user
3291
        $users = DB::query(
3292
            'SELECT id, public_key
3293
            FROM '.prefixTable('users').'
3294
            WHERE id NOT IN ("'.OTV_USER_ID.'","'.SSH_USER_ID.'","'.API_USER_ID.'")
3295
            AND public_key != ""'
3296
        );
3297
        foreach ($users as $user) {
3298
            // Insert in DB the new object key for this item by user
3299
            DB::insert(
3300
                $object_name,
3301
                array(
3302
                    'object_id' => $post_object_id,
3303
                    'user_id' => $user['id'],
3304
                    'share_key' => encryptUserObjectKey(
3305
                        $objectKey,
3306
                        $user['public_key']
3307
                    ),
3308
                )
3309
            );
3310
        }
3311
    }
3312
}
3313
3314
/**
3315
 * Is this string base64 encoded?
3316
 *
3317
 * @param string $str Encoded string?
3318
 *
3319
 * @return bool
3320
 */
3321
function isBase64($str)
3322
{
3323
    $str = (string) trim($str);
3324
3325
    if (!isset($str[0])) {
3326
        return false;
3327
    }
3328
3329
    $base64String = (string) base64_decode($str, true);
3330
    if ($base64String && base64_encode($base64String) === $str) {
3331
        return true;
3332
    }
3333
3334
    return false;
3335
}
3336