]> CyberLeo.Net >> Repos - Github/YOURLS.git/blob - includes/functions-auth.php
Better auth filtering. Fixes issue 1229.
[Github/YOURLS.git] / includes / functions-auth.php
1 <?php\r
2 // Check for valid user. Returns true or an error message\r
3 function yourls_is_valid_user() {\r
4         static $valid = false;\r
5         \r
6         if( $valid )\r
7                 return true;\r
8 \r
9         // Logout request\r
10         if( isset( $_GET['action'] ) && $_GET['action'] == 'logout' ) {\r
11                 yourls_do_action( 'logout' );\r
12                 yourls_store_cookie( null );\r
13                 return 'Logged out successfully';\r
14         }\r
15         \r
16         // Check cookies or login request. Login form has precedence.\r
17         global $yourls_user_passwords;\r
18         \r
19         yourls_do_action( 'pre_login' );\r
20 \r
21         // Determine auth method and check credentials\r
22         if\r
23                 // API only: Secure (no login or pwd) and time limited token\r
24                 // ?timestamp=12345678&signature=md5(totoblah12345678)\r
25                 ( yourls_is_API() &&\r
26                   isset( $_REQUEST['timestamp'] ) && !empty($_REQUEST['timestamp'] ) &&\r
27                   isset( $_REQUEST['signature'] ) && !empty($_REQUEST['signature'] )\r
28                 )\r
29                 {\r
30                         yourls_do_action( 'pre_login_signature_timestamp' );\r
31                         $unfiltered_valid = yourls_check_signature_timestamp();\r
32                 }\r
33                 \r
34         elseif\r
35                 // API only: Secure (no login or pwd)\r
36                 // ?signature=md5(totoblah)\r
37                 ( yourls_is_API() &&\r
38                   !isset( $_REQUEST['timestamp'] ) &&\r
39                   isset( $_REQUEST['signature'] ) && !empty( $_REQUEST['signature'] )\r
40                 )\r
41                 {\r
42                         yourls_do_action( 'pre_login_signature' );\r
43                         $unfiltered_valid = yourls_check_signature();\r
44                 }\r
45         \r
46         elseif\r
47                 // API or normal: login with username & pwd\r
48                 ( isset( $_REQUEST['username'] ) && isset( $_REQUEST['password'] )\r
49                   && !empty( $_REQUEST['username'] ) && !empty( $_REQUEST['password']  ) )\r
50                 {\r
51                         yourls_do_action( 'pre_login_username_password' );\r
52                         $unfiltered_valid = yourls_check_username_password();\r
53                 }\r
54         \r
55         elseif\r
56                 // Normal only: cookies\r
57                 ( !yourls_is_API() && \r
58                   isset( $_COOKIE['yourls_username'] ) && isset( $_COOKIE['yourls_password'] ) )\r
59                 {\r
60                         yourls_do_action( 'pre_login_cookie' );\r
61                         $unfiltered_valid = yourls_check_auth_cookie();\r
62                 }\r
63 \r
64         $valid = yourls_apply_filter( 'is_valid_user', $unfiltered_valid );\r
65 \r
66         // Login for the win!\r
67         if ( $valid ) {\r
68                 yourls_do_action( 'login' );\r
69                 // (Re)store encrypted cookie if needed and tell it's ok\r
70                 if ( !yourls_is_API() && $unfiltered_valid ) \r
71                         yourls_store_cookie( YOURLS_USER );\r
72                 return true;\r
73         }\r
74         \r
75         // Login failed\r
76         yourls_do_action( 'login_failed' );\r
77 \r
78         if ( isset( $_REQUEST['username'] ) || isset( $_REQUEST['password'] ) ) {\r
79                 return 'Invalid username or password';\r
80         } else {\r
81                 return 'Please log in';\r
82         }\r
83 }\r
84 \r
85 // Check auth against list of login=>pwd. Sets user if applicable, returns bool\r
86 function yourls_check_username_password() {\r
87         global $yourls_user_passwords;\r
88         if( isset( $yourls_user_passwords[ $_REQUEST['username'] ] ) && yourls_check_password_hash( $yourls_user_passwords[ $_REQUEST['username'] ], $_REQUEST['password'] ) ) {\r
89                 yourls_set_user( $_REQUEST['username'] );\r
90                 return true;\r
91         }\r
92         return false;\r
93 }\r
94 \r
95 // Check a REQUEST password sent in plain text against stored password which can be a salted hash\r
96 function yourls_check_password_hash( $stored, $plaintext ) {\r
97         if ( substr( $stored, 0, 4 ) == 'md5:' and strlen( $stored ) == 42 ) {\r
98                 // Stored password is a salted hash: "md5:<$r = rand(10000,99999)>:<md5($r.'thepassword')>"\r
99                 // And 42. Of course. http://www.google.com/search?q=the+answer+to+life+the+universe+and+everything\r
100                 list( $temp, $salt, $md5 ) = split( ':', $stored );\r
101                 return( $stored == 'md5:'.$salt.':'.md5( $salt.$plaintext ) );\r
102         } else {\r
103                 // Password was sent in clear\r
104                 return( $stored == $plaintext );\r
105         }\r
106 }\r
107 \r
108 \r
109 // Check auth against encrypted COOKIE data. Sets user if applicable, returns bool\r
110 function yourls_check_auth_cookie() {\r
111         global $yourls_user_passwords;\r
112         foreach( $yourls_user_passwords as $valid_user => $valid_password ) {\r
113                 if( \r
114                         yourls_salt( $valid_user ) == $_COOKIE['yourls_username']\r
115                         && yourls_salt( $valid_password ) == $_COOKIE['yourls_password'] \r
116                 ) {\r
117                         yourls_set_user( $valid_user );\r
118                         return true;\r
119                 }\r
120         }\r
121         return false;\r
122 }\r
123 \r
124 // Check auth against signature and timestamp. Sets user if applicable, returns bool\r
125 function yourls_check_signature_timestamp() {\r
126         // Timestamp in PHP : time()\r
127         // Timestamp in JS: parseInt(new Date().getTime() / 1000)\r
128         global $yourls_user_passwords;\r
129         foreach( $yourls_user_passwords as $valid_user => $valid_password ) {\r
130                 if (\r
131                         (\r
132                                 md5( $_REQUEST['timestamp'].yourls_auth_signature( $valid_user ) ) == $_REQUEST['signature']\r
133                                 or\r
134                                 md5( yourls_auth_signature( $valid_user ).$_REQUEST['timestamp'] ) == $_REQUEST['signature']\r
135                         )\r
136                         &&\r
137                         yourls_check_timestamp( $_REQUEST['timestamp'] )\r
138                         ) {\r
139                         yourls_set_user( $valid_user );\r
140                         return true;\r
141                 }\r
142         }\r
143         return false;\r
144 }\r
145 \r
146 // Check auth against signature. Sets user if applicable, returns bool\r
147 function yourls_check_signature() {\r
148         global $yourls_user_passwords;\r
149         foreach( $yourls_user_passwords as $valid_user => $valid_password ) {\r
150                 if ( yourls_auth_signature( $valid_user ) == $_REQUEST['signature'] ) {\r
151                         yourls_set_user( $valid_user );\r
152                         return true;\r
153                 }\r
154         }\r
155         return false;\r
156 }\r
157 \r
158 // Generate secret signature hash\r
159 function yourls_auth_signature( $username = false ) {\r
160         if( !$username && defined('YOURLS_USER') ) {\r
161                 $username = YOURLS_USER;\r
162         }\r
163         return ( $username ? substr( yourls_salt( $username ), 0, 10 ) : 'Cannot generate auth signature: no username' );\r
164 }\r
165 \r
166 // Check if timestamp is not too old\r
167 function yourls_check_timestamp( $time ) {\r
168         $now = time();\r
169         // Allow timestamp to be a little in the future or the past -- see Issue 766\r
170         return yourls_apply_filter( 'check_timestamp', abs( $now - $time ) < YOURLS_NONCE_LIFE, $time );\r
171 }\r
172 \r
173 // Store new cookie. No $user will delete the cookie.\r
174 function yourls_store_cookie( $user = null ) {\r
175         if( !$user ) {\r
176                 $pass = null;\r
177                 $time = time() - 3600;\r
178         } else {\r
179                 global $yourls_user_passwords;\r
180                 if( isset($yourls_user_passwords[$user]) ) {\r
181                         $pass = $yourls_user_passwords[$user];\r
182                 } else {\r
183                         die( 'Stealing cookies?' ); // This should never happen\r
184                 }\r
185                 $time = time() + YOURLS_COOKIE_LIFE;\r
186         }\r
187         \r
188         $domain   = yourls_apply_filter( 'setcookie_domain',   parse_url( YOURLS_SITE, 1 ) );\r
189         $secure   = yourls_apply_filter( 'setcookie_secure',   yourls_is_ssl() );\r
190         $httponly = yourls_apply_filter( 'setcookie_httponly', true );\r
191                 \r
192         if ( !headers_sent() ) {\r
193                 // Set httponly if the php version is >= 5.2.0\r
194                 if( version_compare( phpversion(), '5.2.0', 'ge' ) ) {\r
195                         setcookie('yourls_username', yourls_salt( $user ), $time, '/', $domain, $secure, $httponly );\r
196                         setcookie('yourls_password', yourls_salt( $pass ), $time, '/', $domain, $secure, $httponly );\r
197                 } else {\r
198                         setcookie('yourls_username', yourls_salt( $user ), $time, '/', $domain, $secure );\r
199                         setcookie('yourls_password', yourls_salt( $pass ), $time, '/', $domain, $secure );\r
200                 }\r
201         }\r
202 }\r
203 \r
204 // Set user name\r
205 function yourls_set_user( $user ) {\r
206         if( !defined( 'YOURLS_USER' ) )\r
207                 define( 'YOURLS_USER', $user );\r
208 }\r