1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Function related to authentication functions and nonces |
4
|
|
|
*/ |
5
|
|
|
|
6
|
|
|
|
7
|
|
|
/** |
8
|
|
|
* Show login form if required |
9
|
|
|
* |
10
|
|
|
*/ |
11
|
|
|
function yourls_maybe_require_auth() { |
12
|
|
|
if( yourls_is_private() ) { |
13
|
|
|
yourls_do_action( 'require_auth' ); |
14
|
|
|
require_once( YOURLS_INC.'/auth.php' ); |
15
|
|
|
} else { |
16
|
|
|
yourls_do_action( 'require_no_auth' ); |
17
|
|
|
} |
18
|
|
|
} |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* Check for valid user via login form or stored cookie. Returns true or an error message |
22
|
|
|
* |
23
|
|
|
*/ |
24
|
|
|
function yourls_is_valid_user() { |
25
|
|
|
// Allow plugins to short-circuit the whole function |
26
|
23 |
|
$pre = yourls_apply_filter( 'shunt_is_valid_user', null ); |
27
|
23 |
|
if ( null !== $pre ) { |
28
|
4 |
|
return $pre; |
29
|
|
|
} |
30
|
|
|
|
31
|
|
|
// $unfiltered_valid : are credentials valid? Boolean value. It's "unfiltered" to allow plugins to eventually filter it. |
32
|
19 |
|
$unfiltered_valid = false; |
33
|
|
|
|
34
|
|
|
// Logout request |
35
|
19 |
|
if( isset( $_GET['action'] ) && $_GET['action'] == 'logout' ) { |
36
|
1 |
|
yourls_do_action( 'logout' ); |
37
|
1 |
|
yourls_store_cookie( null ); |
38
|
1 |
|
return yourls__( 'Logged out successfully' ); |
39
|
|
|
} |
40
|
|
|
|
41
|
|
|
// Check cookies or login request. Login form has precedence. |
42
|
|
|
|
43
|
19 |
|
yourls_do_action( 'pre_login' ); |
44
|
|
|
|
45
|
|
|
// Determine auth method and check credentials |
46
|
|
|
if |
47
|
|
|
// API only: Secure (no login or pwd) and time limited token |
48
|
|
|
// ?timestamp=12345678&signature=md5(totoblah12345678) |
49
|
19 |
|
( yourls_is_API() && |
50
|
19 |
|
isset( $_REQUEST['timestamp'] ) && !empty($_REQUEST['timestamp'] ) && |
51
|
19 |
|
isset( $_REQUEST['signature'] ) && !empty($_REQUEST['signature'] ) |
52
|
|
|
) |
53
|
|
|
{ |
54
|
1 |
|
yourls_do_action( 'pre_login_signature_timestamp' ); |
55
|
1 |
|
$unfiltered_valid = yourls_check_signature_timestamp(); |
56
|
|
|
} |
57
|
|
|
|
58
|
|
|
elseif |
59
|
|
|
// API only: Secure (no login or pwd) |
60
|
|
|
// ?signature=md5(totoblah) |
61
|
18 |
|
( yourls_is_API() && |
62
|
18 |
|
!isset( $_REQUEST['timestamp'] ) && |
63
|
18 |
|
isset( $_REQUEST['signature'] ) && !empty( $_REQUEST['signature'] ) |
64
|
|
|
) |
65
|
|
|
{ |
66
|
1 |
|
yourls_do_action( 'pre_login_signature' ); |
67
|
1 |
|
$unfiltered_valid = yourls_check_signature(); |
68
|
|
|
} |
69
|
|
|
|
70
|
|
|
elseif |
71
|
|
|
// API or normal: login with username & pwd |
72
|
17 |
|
( isset( $_REQUEST['username'] ) && isset( $_REQUEST['password'] ) |
73
|
17 |
|
&& !empty( $_REQUEST['username'] ) && !empty( $_REQUEST['password'] ) ) |
74
|
|
|
{ |
75
|
7 |
|
yourls_do_action( 'pre_login_username_password' ); |
76
|
7 |
|
$unfiltered_valid = yourls_check_username_password(); |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
elseif |
80
|
|
|
// Normal only: cookies |
81
|
10 |
|
( !yourls_is_API() && |
82
|
10 |
|
isset( $_COOKIE[ yourls_cookie_name() ] ) ) |
83
|
|
|
{ |
84
|
2 |
|
yourls_do_action( 'pre_login_cookie' ); |
85
|
2 |
|
$unfiltered_valid = yourls_check_auth_cookie(); |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
// Regardless of validity, allow plugins to filter the boolean and have final word |
89
|
19 |
|
$valid = yourls_apply_filter( 'is_valid_user', $unfiltered_valid ); |
90
|
|
|
|
91
|
|
|
// Login for the win! |
92
|
19 |
|
if ( $valid ) { |
93
|
6 |
|
yourls_do_action( 'login' ); |
94
|
|
|
|
95
|
|
|
// (Re)store encrypted cookie if needed |
96
|
6 |
|
if ( !yourls_is_API() ) { |
97
|
3 |
|
yourls_store_cookie( YOURLS_USER ); |
98
|
|
|
|
99
|
|
|
// Login form : redirect to requested URL to avoid re-submitting the login form on page reload |
100
|
3 |
|
if( isset( $_REQUEST['username'] ) && isset( $_REQUEST['password'] ) && isset( $_SERVER['REQUEST_URI'] ) ) { |
101
|
|
|
yourls_redirect( yourls_sanitize_url_safe($_SERVER['REQUEST_URI']) ); |
102
|
|
|
} |
103
|
|
|
} |
104
|
|
|
|
105
|
|
|
// Login successful |
106
|
6 |
|
return true; |
107
|
|
|
} |
108
|
|
|
|
109
|
|
|
// Login failed |
110
|
13 |
|
yourls_do_action( 'login_failed' ); |
111
|
|
|
|
112
|
13 |
|
if ( isset( $_REQUEST['username'] ) || isset( $_REQUEST['password'] ) ) { |
113
|
8 |
|
return yourls__( 'Invalid username or password' ); |
114
|
|
|
} else { |
115
|
5 |
|
return yourls__( 'Please log in' ); |
116
|
|
|
} |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
/** |
120
|
|
|
* Check auth against list of login=>pwd. Sets user if applicable, returns bool |
121
|
|
|
* |
122
|
|
|
*/ |
123
|
|
|
function yourls_check_username_password() { |
124
|
7 |
|
global $yourls_user_passwords; |
125
|
7 |
|
if( isset( $yourls_user_passwords[ $_REQUEST['username'] ] ) && yourls_check_password_hash( $_REQUEST['username'], $_REQUEST['password'] ) ) { |
126
|
3 |
|
yourls_set_user( $_REQUEST['username'] ); |
127
|
3 |
|
return true; |
128
|
|
|
} |
129
|
4 |
|
return false; |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
/** |
133
|
|
|
* Check a submitted password sent in plain text against stored password which can be a salted hash |
134
|
|
|
* |
135
|
|
|
*/ |
136
|
|
|
function yourls_check_password_hash( $user, $submitted_password ) { |
137
|
6 |
|
global $yourls_user_passwords; |
138
|
|
|
|
139
|
6 |
|
if( !isset( $yourls_user_passwords[ $user ] ) ) |
140
|
3 |
|
return false; |
141
|
|
|
|
142
|
6 |
|
if ( yourls_has_phpass_password( $user ) ) { |
143
|
|
|
// Stored password is hashed with phpass |
144
|
1 |
|
list( , $hash ) = explode( ':', $yourls_user_passwords[ $user ] ); |
145
|
1 |
|
$hash = str_replace( '!', '$', $hash ); |
146
|
1 |
|
return ( yourls_phpass_check( $submitted_password, $hash ) ); |
147
|
5 |
|
} else if( yourls_has_md5_password( $user ) ) { |
148
|
|
|
// Stored password is a salted md5 hash: "md5:<$r = rand(10000,99999)>:<md5($r.'thepassword')>" |
149
|
1 |
|
list( , $salt, ) = explode( ':', $yourls_user_passwords[ $user ] ); |
150
|
1 |
|
return( $yourls_user_passwords[ $user ] == 'md5:'.$salt.':'.md5( $salt . $submitted_password ) ); |
151
|
|
|
} else { |
152
|
|
|
// Password stored in clear text |
153
|
4 |
|
return( $yourls_user_passwords[ $user ] === $submitted_password ); |
154
|
|
|
} |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
/** |
158
|
|
|
* Overwrite plaintext passwords in config file with phpassed versions. |
159
|
|
|
* |
160
|
|
|
* @since 1.7 |
161
|
|
|
* @param string $config_file Full path to file |
162
|
|
|
* @return true if overwrite was successful, an error message otherwise |
163
|
|
|
*/ |
164
|
|
|
function yourls_hash_passwords_now( $config_file ) { |
165
|
2 |
|
if( !is_readable( $config_file ) ) |
166
|
|
|
return 'cannot read file'; // not sure that can actually happen... |
167
|
|
|
|
168
|
2 |
|
if( !is_writable( $config_file ) ) |
169
|
|
|
return 'cannot write file'; |
170
|
|
|
|
171
|
|
|
// Include file to read value of $yourls_user_passwords |
172
|
|
|
// Temporary suppress error reporting to avoid notices about redeclared constants |
173
|
2 |
|
$errlevel = error_reporting(); |
174
|
2 |
|
error_reporting( 0 ); |
175
|
2 |
|
require $config_file; |
176
|
2 |
|
error_reporting( $errlevel ); |
177
|
|
|
|
178
|
2 |
|
$configdata = file_get_contents( $config_file ); |
179
|
2 |
|
if( $configdata == false ) |
180
|
|
|
return 'could not read file'; |
181
|
|
|
|
182
|
2 |
|
$to_hash = 0; // keep track of number of passwords that need hashing |
183
|
2 |
|
foreach ( $yourls_user_passwords as $user => $password ) { |
184
|
2 |
|
if ( !yourls_has_phpass_password( $user ) && !yourls_has_md5_password( $user ) ) { |
185
|
2 |
|
$to_hash++; |
186
|
2 |
|
$hash = yourls_phpass_hash( $password ); |
187
|
|
|
// PHP would interpret $ as a variable, so replace it in storage. |
188
|
2 |
|
$hash = str_replace( '$', '!', $hash ); |
189
|
2 |
|
$quotes = "'" . '"'; |
190
|
2 |
|
$pattern = "/[$quotes]${user}[$quotes]\s*=>\s*[$quotes]" . preg_quote( $password, '/' ) . "[$quotes]/"; |
|
|
|
|
191
|
2 |
|
$replace = "'$user' => 'phpass:$hash' /* Password encrypted by YOURLS */ "; |
192
|
2 |
|
$count = 0; |
193
|
2 |
|
$configdata = preg_replace( $pattern, $replace, $configdata, -1, $count ); |
194
|
|
|
// There should be exactly one replacement. Otherwise, fast fail. |
195
|
2 |
|
if ( $count != 1 ) { |
196
|
|
|
yourls_debug_log( "Problem with preg_replace for password hash of user $user" ); |
197
|
|
|
return 'preg_replace problem'; |
198
|
|
|
} |
199
|
|
|
} |
200
|
|
|
} |
201
|
|
|
|
202
|
2 |
|
if( $to_hash == 0 ) |
203
|
|
|
return 0; // There was no password to encrypt |
204
|
|
|
|
205
|
2 |
|
$success = file_put_contents( $config_file, $configdata ); |
206
|
2 |
|
if ( $success === FALSE ) { |
207
|
|
|
yourls_debug_log( 'Failed writing to ' . $config_file ); |
208
|
|
|
return 'could not write file'; |
209
|
|
|
} |
210
|
2 |
|
return true; |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
/** |
214
|
|
|
* Hash a password using phpass |
215
|
|
|
* |
216
|
|
|
* @since 1.7 |
217
|
|
|
* @param string $password password to hash |
218
|
|
|
* @return string hashed password |
219
|
|
|
*/ |
220
|
|
|
function yourls_phpass_hash( $password ) { |
221
|
9 |
|
$hasher = yourls_phpass_instance(); |
222
|
9 |
|
return $hasher->HashPassword( $password ); |
223
|
|
|
} |
224
|
|
|
|
225
|
|
|
/** |
226
|
|
|
* Check a clear password against a phpass hash |
227
|
|
|
* |
228
|
|
|
* @since 1.7 |
229
|
|
|
* @param string $password clear (eg submitted in a form) password |
230
|
|
|
* @param string $hash hash supposedly generated by phpass |
231
|
|
|
* @return bool true if the hash matches the password once hashed by phpass, false otherwise |
232
|
|
|
*/ |
233
|
|
|
function yourls_phpass_check( $password, $hash ) { |
234
|
8 |
|
$hasher = yourls_phpass_instance(); |
235
|
8 |
|
return $hasher->CheckPassword( $password, $hash ); |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
/** |
239
|
|
|
* Helper function: create new instance or return existing instance of phpass class |
240
|
|
|
* |
241
|
|
|
* @since 1.7 |
242
|
|
|
* @param int $iteration iteration count - 8 is default in phpass |
243
|
|
|
* @param bool $portable flag to force portable (cross platform and system independant) hashes - false to use whatever the system can do best |
244
|
|
|
* @return object a PasswordHash instance |
245
|
|
|
*/ |
246
|
|
|
function yourls_phpass_instance( $iteration = 8, $portable = false ) { |
247
|
10 |
|
$iteration = yourls_apply_filter( 'phpass_new_instance_iteration', $iteration ); |
248
|
10 |
|
$portable = yourls_apply_filter( 'phpass_new_instance_portable', $portable ); |
249
|
|
|
|
250
|
10 |
|
static $instance = false; |
251
|
10 |
|
if( $instance == false ) { |
252
|
|
|
$instance = new \Ozh\Phpass\PasswordHash( $iteration, $portable ); |
253
|
|
|
} |
254
|
|
|
|
255
|
10 |
|
return $instance; |
256
|
|
|
} |
257
|
|
|
|
258
|
|
|
|
259
|
|
|
/** |
260
|
|
|
* Check to see if any passwords are stored as cleartext. |
261
|
|
|
* |
262
|
|
|
* @since 1.7 |
263
|
|
|
* @return bool true if any passwords are cleartext |
264
|
|
|
*/ |
265
|
|
|
function yourls_has_cleartext_passwords() { |
266
|
2 |
|
global $yourls_user_passwords; |
267
|
2 |
|
foreach ( $yourls_user_passwords as $user => $pwdata ) { |
268
|
2 |
|
if ( !yourls_has_md5_password( $user ) && !yourls_has_phpass_password( $user ) ) { |
269
|
1 |
|
return true; |
270
|
|
|
} |
271
|
|
|
} |
272
|
1 |
|
return false; |
273
|
|
|
} |
274
|
|
|
|
275
|
|
|
/** |
276
|
|
|
* Check if a user has a hashed password |
277
|
|
|
* |
278
|
|
|
* Check if a user password is 'md5:[38 chars]'. |
279
|
|
|
* TODO: deprecate this when/if we have proper user management with password hashes stored in the DB |
280
|
|
|
* |
281
|
|
|
* @since 1.7 |
282
|
|
|
* @param string $user user login |
283
|
|
|
* @return bool true if password hashed, false otherwise |
284
|
|
|
*/ |
285
|
|
|
function yourls_has_md5_password( $user ) { |
286
|
10 |
|
global $yourls_user_passwords; |
287
|
10 |
|
return( isset( $yourls_user_passwords[ $user ] ) |
288
|
10 |
|
&& substr( $yourls_user_passwords[ $user ], 0, 4 ) == 'md5:' |
289
|
10 |
|
&& strlen( $yourls_user_passwords[ $user ] ) == 42 // http://www.google.com/search?q=the+answer+to+life+the+universe+and+everything |
290
|
|
|
); |
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
/** |
294
|
|
|
* Check if a user's password is hashed with PHPASS. |
295
|
|
|
* |
296
|
|
|
* Check if a user password is 'phpass:[lots of chars]'. |
297
|
|
|
* TODO: deprecate this when/if we have proper user management with password hashes stored in the DB |
298
|
|
|
* |
299
|
|
|
* @since 1.7 |
300
|
|
|
* @param string $user user login |
301
|
|
|
* @return bool true if password hashed with PHPASS, otherwise false |
302
|
|
|
*/ |
303
|
|
|
function yourls_has_phpass_password( $user ) { |
304
|
11 |
|
global $yourls_user_passwords; |
305
|
11 |
|
return( isset( $yourls_user_passwords[ $user ] ) |
306
|
11 |
|
&& substr( $yourls_user_passwords[ $user ], 0, 7 ) == 'phpass:' |
307
|
|
|
); |
308
|
|
|
} |
309
|
|
|
|
310
|
|
|
/** |
311
|
|
|
* Check auth against encrypted COOKIE data. Sets user if applicable, returns bool |
312
|
|
|
* |
313
|
|
|
*/ |
314
|
|
|
function yourls_check_auth_cookie() { |
315
|
2 |
|
global $yourls_user_passwords; |
316
|
2 |
|
foreach( $yourls_user_passwords as $valid_user => $valid_password ) { |
317
|
2 |
|
if ( yourls_cookie_value( $valid_user ) === $_COOKIE[ yourls_cookie_name() ] ) { |
318
|
1 |
|
yourls_set_user( $valid_user ); |
319
|
1 |
|
return true; |
320
|
|
|
} |
321
|
|
|
} |
322
|
1 |
|
return false; |
323
|
|
|
} |
324
|
|
|
|
325
|
|
|
/** |
326
|
|
|
* Check auth against signature and timestamp. Sets user if applicable, returns bool |
327
|
|
|
* |
328
|
|
|
* Original usage : |
329
|
|
|
* http://sho.rt/yourls-api.php?timestamp=<timestamp>&signature=<md5 hash>&action=... |
330
|
|
|
* Since 1.7.7 we allow a `hash` parameter and an arbitrary hashed signature, hashed |
331
|
|
|
* with the `hash` function. Examples : |
332
|
|
|
* http://sho.rt/yourls-api.php?timestamp=<timestamp>&signature=<sha512 hash>&hash=sha512&action=... |
333
|
|
|
* http://sho.rt/yourls-api.php?timestamp=<timestamp>&signature=<crc32 hash>&hash=crc32&action=... |
334
|
|
|
* |
335
|
|
|
* @since 1.4.1 |
336
|
|
|
* @return bool False if signature or timestamp missing or invalid, true if valid |
337
|
|
|
*/ |
338
|
|
|
function yourls_check_signature_timestamp() { |
339
|
5 |
|
if( !isset( $_REQUEST['signature'] ) OR empty( $_REQUEST['signature'] ) |
340
|
5 |
|
OR !isset( $_REQUEST['timestamp'] ) OR empty( $_REQUEST['timestamp'] ) |
341
|
|
|
) { |
342
|
1 |
|
return false; |
343
|
|
|
} |
344
|
|
|
|
345
|
|
|
// Exit if the timestamp argument is outdated or invalid |
346
|
4 |
|
if( !yourls_check_timestamp( $_REQUEST['timestamp'] )) { |
347
|
1 |
|
return false; |
348
|
|
|
} |
349
|
|
|
|
350
|
|
|
// if there is a hash argument, make sure it's part of the availables algos |
351
|
3 |
|
$hash_function = isset($_REQUEST['hash']) ? (string)$_REQUEST['hash'] : 'md5'; |
352
|
3 |
|
if( !in_array($hash_function, hash_algos()) ) { |
353
|
1 |
|
return false; |
354
|
|
|
} |
355
|
|
|
|
356
|
|
|
// Check signature & timestamp against all possible users |
357
|
3 |
|
global $yourls_user_passwords; |
358
|
3 |
|
foreach( $yourls_user_passwords as $valid_user => $valid_password ) { |
359
|
|
|
if ( |
360
|
3 |
|
hash( $hash_function, $_REQUEST['timestamp'].yourls_auth_signature( $valid_user ) ) === $_REQUEST['signature'] |
361
|
|
|
or |
362
|
3 |
|
hash( $hash_function, yourls_auth_signature( $valid_user ).$_REQUEST['timestamp'] ) === $_REQUEST['signature'] |
363
|
|
|
) { |
364
|
3 |
|
yourls_set_user( $valid_user ); |
365
|
3 |
|
return true; |
366
|
|
|
} |
367
|
|
|
} |
368
|
|
|
|
369
|
|
|
// Signature doesn't match known user |
370
|
|
|
return false; |
371
|
|
|
} |
372
|
|
|
|
373
|
|
|
/** |
374
|
|
|
* Check auth against signature. Sets user if applicable, returns bool |
375
|
|
|
* |
376
|
|
|
* @since 1.4.1 |
377
|
|
|
* @return bool False if signature missing or invalid, true if valid |
378
|
|
|
*/ |
379
|
|
|
function yourls_check_signature() { |
380
|
3 |
|
if( !isset( $_REQUEST['signature'] ) OR empty( $_REQUEST['signature'] ) ) |
381
|
1 |
|
return false; |
382
|
|
|
|
383
|
|
|
// Check signature against all possible users |
384
|
2 |
|
global $yourls_user_passwords; |
385
|
2 |
|
foreach( $yourls_user_passwords as $valid_user => $valid_password ) { |
386
|
2 |
|
if ( yourls_auth_signature( $valid_user ) === $_REQUEST['signature'] ) { |
387
|
1 |
|
yourls_set_user( $valid_user ); |
388
|
1 |
|
return true; |
389
|
|
|
} |
390
|
|
|
} |
391
|
|
|
|
392
|
|
|
// Signature doesn't match known user |
393
|
1 |
|
return false; |
394
|
|
|
} |
395
|
|
|
|
396
|
|
|
/** |
397
|
|
|
* Generate secret signature hash |
398
|
|
|
* |
399
|
|
|
*/ |
400
|
|
|
function yourls_auth_signature( $username = false ) { |
401
|
5 |
|
if( !$username && defined('YOURLS_USER') ) { |
402
|
|
|
$username = YOURLS_USER; |
403
|
|
|
} |
404
|
5 |
|
return ( $username ? substr( yourls_salt( $username ), 0, 10 ) : 'Cannot generate auth signature: no username' ); |
405
|
|
|
} |
406
|
|
|
|
407
|
|
|
/** |
408
|
|
|
* Check if timestamp is not too old |
409
|
|
|
* |
410
|
|
|
*/ |
411
|
|
|
function yourls_check_timestamp( $time ) { |
412
|
10 |
|
$now = time(); |
413
|
|
|
// Allow timestamp to be a little in the future or the past -- see Issue 766 |
414
|
10 |
|
return yourls_apply_filter( 'check_timestamp', abs( $now - (int)$time ) < yourls_get_nonce_life(), $time ); |
415
|
|
|
} |
416
|
|
|
|
417
|
|
|
/** |
418
|
|
|
* Store new cookie. No $user will delete the cookie. |
419
|
|
|
* |
420
|
|
|
* @param mixed $user String, user login, or null to delete cookie |
421
|
|
|
*/ |
422
|
|
|
function yourls_store_cookie( $user = null ) { |
423
|
|
|
|
424
|
|
|
// No user will delete the cookie with a cookie time from the past |
425
|
3 |
|
if( !$user ) { |
426
|
1 |
|
$time = time() - 3600; |
427
|
|
|
} else { |
428
|
3 |
|
$time = time() + yourls_get_cookie_life(); |
429
|
|
|
} |
430
|
|
|
|
431
|
3 |
|
$path = yourls_apply_filter( 'setcookie_path', '/' ); |
432
|
3 |
|
$domain = yourls_apply_filter( 'setcookie_domain', parse_url( yourls_get_yourls_site(), PHP_URL_HOST ) ); |
433
|
3 |
|
$secure = yourls_apply_filter( 'setcookie_secure', yourls_is_ssl() ); |
434
|
3 |
|
$httponly = yourls_apply_filter( 'setcookie_httponly', true ); |
435
|
|
|
|
436
|
|
|
// Some browsers refuse to store localhost cookie |
437
|
3 |
|
if ( $domain == 'localhost' ) |
438
|
3 |
|
$domain = ''; |
439
|
|
|
|
440
|
3 |
|
if ( !headers_sent( $filename, $linenum ) ) { |
441
|
|
|
yourls_setcookie( yourls_cookie_name(), yourls_cookie_value( $user ), $time, $path, $domain, $secure, $httponly ); |
442
|
|
|
} else { |
443
|
|
|
// For some reason cookies were not stored: action to be able to debug that |
444
|
3 |
|
yourls_do_action( 'setcookie_failed', $user ); |
445
|
3 |
|
yourls_debug_log( "Could not store cookie: headers already sent in $filename on line $linenum" ); |
446
|
|
|
} |
447
|
3 |
|
} |
448
|
|
|
|
449
|
|
|
/** |
450
|
|
|
* Replacement for PHP's setcookie(), with support for SameSite cookie attribute |
451
|
|
|
* |
452
|
|
|
* @see https://github.com/GoogleChromeLabs/samesite-examples/blob/master/php.md |
453
|
|
|
* @see https://stackoverflow.com/a/59654832/36850 |
454
|
|
|
* @see https://3v4l.org/uKEtH for compat tests |
455
|
|
|
* @see https://www.php.net/manual/en/function.setcookie.php |
456
|
|
|
* |
457
|
|
|
* @since 1.7.7 |
458
|
|
|
* @param string $name cookie name |
459
|
|
|
* @param string $value cookie value |
460
|
|
|
* @param int $expire time the cookie expires as a Unix timestamp (number of seconds since the epoch) |
461
|
|
|
* @param string $path path on the server in which the cookie will be available on |
462
|
|
|
* @param string $domain (sub)domain that the cookie is available to |
463
|
|
|
* @param bool $secure if cookie should only be transmitted over a secure HTTPS connection |
464
|
|
|
* @param bool $httponly if cookie will be made accessible only through the HTTP protocol |
465
|
|
|
* @return bool setcookie() result : false if output sent before, true otherwise. This does not indicate whether the user accepted the cookie. |
466
|
|
|
*/ |
467
|
|
|
function yourls_setcookie($name, $value, $expire, $path, $domain, $secure, $httponly) { |
468
|
|
|
$samesite = yourls_apply_filter('setcookie_samesite', 'Lax' ); |
469
|
|
|
|
470
|
|
|
if (PHP_VERSION_ID < 70300) { |
471
|
|
|
return(setcookie($name, $value, $expire, "$path; samesite=$samesite", $domain, $secure, $httponly)); |
472
|
|
|
} |
473
|
|
|
else { |
474
|
|
|
return(setcookie($name, $value, array( |
475
|
|
|
'expires' => $expire, |
476
|
|
|
'path' => $path, |
477
|
|
|
'domain' => $domain, |
478
|
|
|
'samesite' => $samesite, |
479
|
|
|
'secure' => $secure, |
480
|
|
|
'httponly' => $httponly, |
481
|
|
|
))); |
482
|
|
|
} |
483
|
|
|
} |
484
|
|
|
|
485
|
|
|
/** |
486
|
|
|
* Set user name |
487
|
|
|
* |
488
|
|
|
*/ |
489
|
|
|
function yourls_set_user( $user ) { |
490
|
8 |
|
if( !defined( 'YOURLS_USER' ) ) |
491
|
1 |
|
define( 'YOURLS_USER', $user ); |
492
|
8 |
|
} |
493
|
|
|
|
494
|
|
|
/** |
495
|
|
|
* Get YOURLS_COOKIE_LIFE value (ie the life span of an auth cookie in seconds) |
496
|
|
|
* |
497
|
|
|
* Use this function instead of directly using the constant. This way, its value can be modified by plugins |
498
|
|
|
* on a per case basis |
499
|
|
|
* |
500
|
|
|
* @since 1.7.7 |
501
|
|
|
* @see includes/Config/Config.php |
502
|
|
|
* @return integer cookie life span, in seconds |
503
|
|
|
*/ |
504
|
|
|
function yourls_get_cookie_life() { |
505
|
5 |
|
return yourls_apply_filter( 'get_cookie_life', YOURLS_COOKIE_LIFE ); |
506
|
|
|
} |
507
|
|
|
|
508
|
|
|
/** |
509
|
|
|
* Get YOURLS_NONCE_LIFE value (ie life span of a nonce in seconds) |
510
|
|
|
* |
511
|
|
|
* Use this function instead of directly using the constant. This way, its value can be modified by plugins |
512
|
|
|
* on a per case basis |
513
|
|
|
* |
514
|
|
|
* @since 1.7.7 |
515
|
|
|
* @see includes/Config/Config.php |
516
|
|
|
* @see https://en.wikipedia.org/wiki/Cryptographic_nonce |
517
|
|
|
* @return integer nonce life span, in seconds |
518
|
|
|
*/ |
519
|
|
|
function yourls_get_nonce_life() { |
520
|
17 |
|
return yourls_apply_filter( 'get_nonce_life', YOURLS_NONCE_LIFE ); |
521
|
|
|
} |
522
|
|
|
|
523
|
|
|
/** |
524
|
|
|
* Get YOURLS cookie name |
525
|
|
|
* |
526
|
|
|
* The name is unique for each install, to prevent mismatch between sho.rt and very.sho.rt -- see #1673 |
527
|
|
|
* |
528
|
|
|
* TODO: when multi user is implemented, the whole cookie stuff should be reworked to allow storing multiple users |
529
|
|
|
* |
530
|
|
|
* @since 1.7.1 |
531
|
|
|
* @return string unique cookie name for a given YOURLS site |
532
|
|
|
*/ |
533
|
|
|
function yourls_cookie_name() { |
534
|
5 |
|
return yourls_apply_filter( 'cookie_name', 'yourls_' . yourls_salt( yourls_get_yourls_site() ) ); |
535
|
|
|
} |
536
|
|
|
|
537
|
|
|
/** |
538
|
|
|
* Get auth cookie value |
539
|
|
|
* |
540
|
|
|
* @since 1.7.7 |
541
|
|
|
* @param string $user user name |
542
|
|
|
* @return string cookie value |
543
|
|
|
*/ |
544
|
|
|
function yourls_cookie_value( $user ) { |
545
|
3 |
|
return yourls_apply_filter( 'set_cookie_value', yourls_salt( $user ), $user ); |
546
|
|
|
} |
547
|
|
|
|
548
|
|
|
/** |
549
|
|
|
* Return a time-dependent string for nonce creation |
550
|
|
|
* |
551
|
|
|
* Actually, this returns a float: ceil rounds up a value but is of type float, see https://www.php.net/ceil |
552
|
|
|
* |
553
|
|
|
*/ |
554
|
|
|
function yourls_tick() { |
555
|
7 |
|
return ceil( time() / yourls_get_nonce_life() ); |
556
|
|
|
} |
557
|
|
|
|
558
|
|
|
/** |
559
|
|
|
* Return salted string |
560
|
|
|
* |
561
|
|
|
*/ |
562
|
|
|
function yourls_salt( $string ) { |
563
|
17 |
|
$salt = defined('YOURLS_COOKIEKEY') ? YOURLS_COOKIEKEY : md5(__FILE__) ; |
564
|
17 |
|
return yourls_apply_filter( 'yourls_salt', md5 ($string . $salt), $string ); |
565
|
|
|
} |
566
|
|
|
|
567
|
|
|
/** |
568
|
|
|
* Create a time limited, action limited and user limited token |
569
|
|
|
* |
570
|
|
|
*/ |
571
|
|
|
function yourls_create_nonce( $action, $user = false ) { |
572
|
6 |
|
if( false == $user ) |
573
|
1 |
|
$user = defined( 'YOURLS_USER' ) ? YOURLS_USER : '-1'; |
574
|
6 |
|
$tick = yourls_tick(); |
575
|
6 |
|
$nonce = substr( yourls_salt($tick . $action . $user), 0, 10 ); |
576
|
|
|
// Allow plugins to alter the nonce |
577
|
6 |
|
return yourls_apply_filter( 'create_nonce', $nonce, $action, $user ); |
578
|
|
|
} |
579
|
|
|
|
580
|
|
|
/** |
581
|
|
|
* Create a nonce field for inclusion into a form |
582
|
|
|
* |
583
|
|
|
*/ |
584
|
|
|
function yourls_nonce_field( $action, $name = 'nonce', $user = false, $echo = true ) { |
585
|
1 |
|
$field = '<input type="hidden" id="'.$name.'" name="'.$name.'" value="'.yourls_create_nonce( $action, $user ).'" />'; |
586
|
1 |
|
if( $echo ) |
587
|
|
|
echo $field."\n"; |
588
|
1 |
|
return $field; |
589
|
|
|
} |
590
|
|
|
|
591
|
|
|
/** |
592
|
|
|
* Add a nonce to a URL. If URL omitted, adds nonce to current URL |
593
|
|
|
* |
594
|
|
|
*/ |
595
|
|
|
function yourls_nonce_url( $action, $url = false, $name = 'nonce', $user = false ) { |
596
|
2 |
|
$nonce = yourls_create_nonce( $action, $user ); |
597
|
2 |
|
return yourls_add_query_arg( $name, $nonce, $url ); |
598
|
|
|
} |
599
|
|
|
|
600
|
|
|
/** |
601
|
|
|
* Check validity of a nonce (ie time span, user and action match). |
602
|
|
|
* |
603
|
|
|
* Returns true if valid, dies otherwise (yourls_die() or die($return) if defined) |
604
|
|
|
* if $nonce is false or unspecified, it will use $_REQUEST['nonce'] |
605
|
|
|
* |
606
|
|
|
*/ |
607
|
|
|
function yourls_verify_nonce( $action, $nonce = false, $user = false, $return = '' ) { |
608
|
|
|
// get user |
609
|
2 |
|
if( false == $user ) |
610
|
|
|
$user = defined( 'YOURLS_USER' ) ? YOURLS_USER : '-1'; |
611
|
|
|
|
612
|
|
|
// get current nonce value |
613
|
2 |
|
if( false == $nonce && isset( $_REQUEST['nonce'] ) ) |
614
|
|
|
$nonce = $_REQUEST['nonce']; |
615
|
|
|
|
616
|
|
|
// Allow plugins to short-circuit the rest of the function |
617
|
2 |
|
$valid = yourls_apply_filter( 'verify_nonce', false, $action, $nonce, $user, $return ); |
618
|
2 |
|
if ($valid) { |
619
|
|
|
return true; |
620
|
|
|
} |
621
|
|
|
|
622
|
|
|
// what nonce should be |
623
|
2 |
|
$valid = yourls_create_nonce( $action, $user ); |
624
|
|
|
|
625
|
2 |
|
if( $nonce == $valid ) { |
626
|
1 |
|
return true; |
627
|
|
|
} else { |
628
|
1 |
|
if( $return ) |
629
|
|
|
die( $return ); |
630
|
1 |
|
yourls_die( yourls__( 'Unauthorized action or expired link' ), yourls__( 'Error' ), 403 ); |
631
|
|
|
} |
632
|
|
|
} |
633
|
|
|
|
It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.