|
1 <?php |
|
2 /**!info** |
|
3 { |
|
4 "Plugin Name" : "Kerberos authentication", |
|
5 "Plugin URI" : "http://enanocms.org/plugin/kerbauth", |
|
6 "Description" : "Allows authentication to Enano via Kerberos.", |
|
7 "Author" : "Dan Fuhry", |
|
8 "Version" : "1.0", |
|
9 "Author URI" : "http://enanocms.org/", |
|
10 "Auth plugin" : true |
|
11 } |
|
12 **!*/ |
|
13 |
|
14 /* |
|
15 * Kerberos authentication plugin for Enano |
|
16 * (C) 2010 Dan Fuhry |
|
17 * |
|
18 * This program is Free Software; you can redistribute and/or modify it under the terms of the GNU General Public License |
|
19 * as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. |
|
20 * |
|
21 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied |
|
22 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. |
|
23 */ |
|
24 |
|
25 if ( getConfig('kerb_enable', 0) == 1 ) |
|
26 { |
|
27 $plugins->attachHook('login_process_userdata_json', 'return kerb_auth_hook($userinfo, $req["level"], @$req["remember"]);'); |
|
28 } |
|
29 |
|
30 function kerb_auth_hook($userinfo, $level, $remember) |
|
31 { |
|
32 global $db, $session, $paths, $template, $plugins; // Common objects |
|
33 |
|
34 // First try to just authenticate the user in Kerberos |
|
35 require_once(ENANO_ROOT . '/plugins/kerbauth/libkrb5.php'); |
|
36 |
|
37 if ( strstr($userinfo['username'], '/') ) |
|
38 { |
|
39 return array( |
|
40 'mode' => 'error', |
|
41 'error' => 'You cannot log in with Kerberos principals containing slashes. This is due to both security reasons and Enano technical limitations.' |
|
42 ); |
|
43 } |
|
44 |
|
45 // We're ready to do a Kerberos auth attempt |
|
46 try |
|
47 { |
|
48 $auth_result = krb5_verify_creds($userinfo['username'], $userinfo['password']); |
|
49 } |
|
50 catch ( KerberosError $e ) |
|
51 { |
|
52 return array( |
|
53 'mode' => 'error', |
|
54 'error' => "The Kerberos interface returned a technical error." |
|
55 ); |
|
56 } |
|
57 |
|
58 if ( $auth_result ) |
|
59 { |
|
60 // Kerberos authentication was successful. |
|
61 $username = $db->escape(strtolower($userinfo['username'])); |
|
62 $q = $db->sql_query("SELECT user_id, password FROM " . table_prefix . "users WHERE " . ENANO_SQLFUNC_LOWERCASE . "(username) = '$username';"); |
|
63 if ( !$q ) |
|
64 $db->_die(); |
|
65 if ( $db->numrows() < 1 ) |
|
66 { |
|
67 // This user doesn't exist. |
|
68 // Is creating it our job? |
|
69 if ( getConfig('kerb_disable_local_auth', 0) == 1 ) |
|
70 { |
|
71 // Yep, register him |
|
72 $email = strtolower($userinfo['username']) . '@' . getConfig('kerb_email_domain', 'localhost'); |
|
73 $random_pass = md5(microtime() . mt_rand()); |
|
74 // load the language |
|
75 $session->register_guest_session(); |
|
76 $reg_result = $session->create_user($userinfo['username'], $random_pass, $email); |
|
77 if ( $reg_result != 'success' ) |
|
78 { |
|
79 // o_O |
|
80 // Registration failed. |
|
81 return array( |
|
82 'mode' => 'error', |
|
83 'error' => 'Your username and password were valid, but there was a problem instanciating your local user account.' |
|
84 ); |
|
85 } |
|
86 // Get user ID |
|
87 $q = $db->sql_query("SELECT user_id, password FROM " . table_prefix . "users WHERE " . ENANO_SQLFUNC_LOWERCASE . "(username) = '$username';"); |
|
88 if ( !$q ) |
|
89 $db->_die(); |
|
90 if ( $db->numrows() < 1 ) |
|
91 return array( |
|
92 'mode' => 'error', |
|
93 'error' => 'Your username and password were valid, but there was a problem getting your user ID.' |
|
94 ); |
|
95 $row = $db->fetchrow(); |
|
96 $db->free_result(); |
|
97 // Quick - lock the account |
|
98 $q = $db->sql_query('UPDATE ' . table_prefix . "users SET password = 'Locked by Kerberos plugin', password_salt = 'Locked by Kerberos plugin' WHERE user_id = {$row['user_id']};"); |
|
99 if ( !$q ) |
|
100 $db->_die(); |
|
101 |
|
102 $row['password'] = 'Locked by Kerberos plugin'; |
|
103 } |
|
104 else |
|
105 { |
|
106 // Nope. Just let Enano fail it properly. |
|
107 return null; |
|
108 } |
|
109 } |
|
110 else |
|
111 { |
|
112 $row = $db->fetchrow(); |
|
113 $db->free_result(); |
|
114 } |
|
115 |
|
116 $session->register_session(intval($row['user_id']), $userinfo['username'], $row['password'], intval($level), intval($remember)); |
|
117 return true; |
|
118 } |
|
119 else |
|
120 { |
|
121 // Kerberos authentication failed. |
|
122 |
|
123 // Are local logons allowed? |
|
124 if ( getConfig('kerb_disable_local_auth', 0) == 0 ) |
|
125 { |
|
126 // Yes, allow auth to continue |
|
127 return null; |
|
128 } |
|
129 |
|
130 // Block the login attempt unless the username is a local admin. |
|
131 $username = $db->escape(strtolower($userinfo['username'])); |
|
132 $q = $db->sql_query("SELECT user_level FROM " . table_prefix . "users WHERE " . ENANO_SQLFUNC_LOWERCASE . "(username) = '$username';"); |
|
133 if ( !$q ) |
|
134 $db->_die(); |
|
135 if ( $db->numrows() > 0 ) |
|
136 { |
|
137 // Well, the user exists... |
|
138 list($ul) = $db->fetchrow_num(); |
|
139 $db->free_result(); |
|
140 if ( $ul >= USER_LEVEL_ADMIN ) |
|
141 { |
|
142 // They're an admin, allow local logon |
|
143 return null; |
|
144 } |
|
145 } |
|
146 $db->free_result(); |
|
147 |
|
148 // User doesn't exist, or is not an admin, and users are not allowed to log on locally. Lock them out. |
|
149 $q = $db->sql_query('INSERT INTO ' . table_prefix . "lockout(ipaddr, timestamp, action, username)\n" |
|
150 . " VALUES('" . $db->escape($_SERVER['REMOTE_ADDR']) . "', " . time() . ", 'credential', '" . $db->escape($userinfo['username']) . "');"); |
|
151 if ( !$q ) |
|
152 $db->_die(); |
|
153 |
|
154 return array( |
|
155 'mode' => 'error', |
|
156 'error' => 'Invalid Kerberos authentication credentials.' |
|
157 ); |
|
158 } |
|
159 } |
|
160 |
|
161 // Registration blocking hook |
|
162 if ( getConfig('kerb_disable_local_auth', 0) == 1 ) |
|
163 { |
|
164 $plugins->attachHook('ucp_register_validate', 'kerb_auth_reg_block($error);'); |
|
165 } |
|
166 |
|
167 function kerb_auth_reg_block(&$error) |
|
168 { |
|
169 $error = 'Registration on this website is disabled because Kerberos authentication is configured. Please log in using a valid Kerberos principal (username) and password, and an account will be created for you automatically.'; |
|
170 } |
|
171 |
|
172 // |
|
173 // ADMIN |
|
174 // |
|
175 |
|
176 $plugins->attachHook('session_started', 'kerb_session_hook();'); |
|
177 |
|
178 if ( getConfig('kerb_disable_local_auth', 0) == 1 ) |
|
179 { |
|
180 $plugins->attachHook('common_post', 'kerb_tou_hook();'); |
|
181 } |
|
182 |
|
183 function kerb_session_hook() |
|
184 { |
|
185 global $db, $session, $paths, $template, $plugins; // Common objects |
|
186 |
|
187 // Register the admin page |
|
188 $paths->addAdminNode('adm_cat_security', 'Kerberos Authentication', 'KerberosConfig'); |
|
189 |
|
190 // Disable password change |
|
191 if ( getConfig('kerb_disable_local_auth', 0) == 1 && $session->user_level < USER_LEVEL_ADMIN ) |
|
192 { |
|
193 $link_text = getConfig('kerb_password_text', false); |
|
194 if ( empty($link_text) ) |
|
195 $link_text = false; |
|
196 $link_url = str_replace('%u', $session->username, getConfig('kerb_password_url', '')); |
|
197 if ( empty($link_url) ) |
|
198 $link_url = false; |
|
199 $session->disable_password_change($link_url, $link_text); |
|
200 } |
|
201 } |
|
202 |
|
203 function kerb_tou_hook() |
|
204 { |
|
205 global $db, $session, $paths, $template, $plugins; // Common objects |
|
206 |
|
207 // Are we pending TOU acceptance? |
|
208 if ( $session->user_logged_in && !$session->on_critical_page() && trim(getConfig('register_tou', '')) != '' ) |
|
209 { |
|
210 $q = $db->sql_query('SELECT account_active FROM ' . table_prefix . "users WHERE user_id = $session->user_id;"); |
|
211 if ( !$q ) |
|
212 $db->_die(); |
|
213 |
|
214 list($active) = $db->fetchrow_num(); |
|
215 $db->free_result(); |
|
216 if ( $active == 1 ) |
|
217 { |
|
218 // Pending TOU accept |
|
219 // Basically, what we do here is force the user to accept the TOU and record it by setting account_active to 2 instead of a 1 |
|
220 // A bit of a hack, but hey, it works, at least in 1.1.8. |
|
221 // In 1.1.7, it just breaks your whole account, and $session->on_critical_page() is broken in 1.1.7 so you won't even be able |
|
222 // to go the admin CP and re-activate yourself. Good times... erhm, sorry. |
|
223 |
|
224 if ( isset($_POST['tou_agreed']) && $_POST['tou_agreed'] === 'I accept the terms and conditions displayed on this site' ) |
|
225 { |
|
226 // Accepted |
|
227 $q = $db->sql_query('UPDATE ' . table_prefix . "users SET account_active = 2 WHERE user_id = $session->user_id;"); |
|
228 if ( !$q ) |
|
229 $db->_die(); |
|
230 |
|
231 return true; |
|
232 } |
|
233 |
|
234 global $output, $lang; |
|
235 $output->set_title('Terms of Use'); |
|
236 $output->header(); |
|
237 |
|
238 ?> |
|
239 <p>Please read and accept the following terms:</p> |
|
240 |
|
241 <div style="border: 1px solid #000000; height: 300px; width: 60%; clip: rect(0px,auto,auto,0px); overflow: auto; background-color: #FFF; margin: 0 auto; padding: 4px;"> |
|
242 <?php |
|
243 $terms = getConfig('register_tou', ''); |
|
244 echo RenderMan::render($terms); |
|
245 ?> |
|
246 </div> |
|
247 |
|
248 <form method="post"> |
|
249 <p style="text-align: center;"> |
|
250 <label> |
|
251 <input tabindex="7" type="checkbox" name="tou_agreed" value="I accept the terms and conditions displayed on this site" /> |
|
252 <b><?php echo $lang->get('user_reg_lbl_field_tou'); ?></b> |
|
253 </label> |
|
254 </p> |
|
255 <p style="text-align: center;"> |
|
256 <input type="submit" value="Continue" /> |
|
257 </p> |
|
258 </form> |
|
259 |
|
260 <?php |
|
261 |
|
262 $output->footer(); |
|
263 |
|
264 $db->close(); |
|
265 exit; |
|
266 } |
|
267 } |
|
268 } |
|
269 |
|
270 function page_Admin_KerberosConfig() |
|
271 { |
|
272 // Security check |
|
273 global $db, $session, $paths, $template, $plugins; // Common objects |
|
274 if ( $session->auth_level < USER_LEVEL_ADMIN ) |
|
275 return false; |
|
276 |
|
277 require_once(ENANO_ROOT . '/plugins/kerbauth/libkrb5.php'); |
|
278 |
|
279 if ( isset($_POST['submit']) ) |
|
280 { |
|
281 setConfig('kerb_enable', isset($_POST['kerb_enable']) ? '1' : '0'); |
|
282 setConfig('kerb_realm', $_POST['kerb_realm']); |
|
283 setConfig('kerb_admin_server', $_POST['kerb_admin_server']); |
|
284 setConfig('kerb_disable_local_auth', isset($_POST['kerb_disable_local_auth']) ? '1' : '0'); |
|
285 setConfig('kerb_password_text', $_POST['kerb_password_text']); |
|
286 setConfig('kerb_password_url', $_POST['kerb_password_url']); |
|
287 setConfig('kerb_email_domain', $_POST['kerb_email_domain']); |
|
288 |
|
289 echo '<div class="info-box">Your changes have been saved.</div>'; |
|
290 } |
|
291 |
|
292 acp_start_form(); |
|
293 ?> |
|
294 <div class="tblholder"> |
|
295 <table border="0" cellspacing="1" cellpadding="4"> |
|
296 <tr> |
|
297 <th colspan="2"> |
|
298 Kerberos Authentication Configuration |
|
299 </th> |
|
300 </tr> |
|
301 |
|
302 <!-- Kerberos enable --> |
|
303 |
|
304 <tr> |
|
305 <td class="row2" style="width: 50%;"> |
|
306 Enable Kerberos authentication: |
|
307 </td> |
|
308 <td class="row1" style="width: 50%;"> |
|
309 <label> |
|
310 <input type="checkbox" name="kerb_enable" <?php if ( getConfig('kerb_enable', 0) ) echo 'checked="checked" '; ?>/> |
|
311 Enabled |
|
312 </label> |
|
313 </td> |
|
314 </tr> |
|
315 |
|
316 <!-- Realm --> |
|
317 |
|
318 <tr> |
|
319 <td class="row2"> |
|
320 Kerberos realm:<br /> |
|
321 <small>Case sensitive. |
|
322 <?php |
|
323 if ( $realm = krb5_get_realm() ) |
|
324 { |
|
325 echo "Leave blank to use auto-detected value: <b>$realm</b>"; |
|
326 } |
|
327 ?></small> |
|
328 </td> |
|
329 <td class="row1"> |
|
330 <input type="text" name="kerb_realm" value="<?php echo htmlspecialchars(getConfig('kerb_realm', '')); ?>" size="40" /> |
|
331 </td> |
|
332 </tr> |
|
333 |
|
334 <!-- Server --> |
|
335 |
|
336 <tr> |
|
337 <td class="row2"> |
|
338 Kerberos admin server:<br /> |
|
339 <small>This should be your admin server, not KDC. We're working on getting true KDC support enabled. |
|
340 <?php |
|
341 if ( $server = krb5_detect_admin_server(getConfig('kerb_realm', $realm)) ) |
|
342 { |
|
343 echo "Leave blank to use auto-detected value: <b>$server</b>"; |
|
344 } |
|
345 ?></small> |
|
346 </td> |
|
347 <td class="row1"> |
|
348 <input type="text" name="kerb_admin_server" value="<?php echo htmlspecialchars(getConfig('kerb_admin_server', '')); ?>" size="40" /> |
|
349 </td> |
|
350 </tr> |
|
351 |
|
352 <!-- Block local auth --> |
|
353 |
|
354 <tr> |
|
355 <td class="row2"> |
|
356 Enforce Kerberos for single-sign-on:<br /> |
|
357 <small>Use this option to force Kerberos passwords and accounts to be used, regardless of local account status, except for administrators.</small> |
|
358 </td> |
|
359 <td class="row1"> |
|
360 <label> |
|
361 <input type="checkbox" name="kerb_disable_local_auth" <?php if ( getConfig('kerb_disable_local_auth', 0) ) echo 'checked="checked" '; ?>/> |
|
362 Enabled |
|
363 </label> |
|
364 </td> |
|
365 </tr> |
|
366 |
|
367 <!-- E-mail domain --> |
|
368 |
|
369 <tr> |
|
370 <td class="row2"> |
|
371 E-mail address domain for autoregistered users:<br /> |
|
372 <small>When a user is automatically registered, this domain will be used as the domain for their e-mail address. This way, activation e-mails will |
|
373 (ideally) reach the user.</small> |
|
374 </td> |
|
375 <td class="row1"> |
|
376 <input type="text" name="kerb_email_domain" value="<?php echo htmlspecialchars(getConfig('kerb_email_domain', '')); ?>" size="30" /> |
|
377 </td> |
|
378 </tr> |
|
379 |
|
380 <!-- Site password change link --> |
|
381 |
|
382 <tr> |
|
383 <td class="row2"> |
|
384 External password management link:<br /> |
|
385 <small>Enter a URL here to link to from Enano's Change Password page. Leave blank to not display a link. The text "%u" will be replaced with the user's username.</small> |
|
386 </td> |
|
387 <td class="row1"> |
|
388 Link text: <input type="text" name="kerb_password_text" value="<?php echo htmlspecialchars(getConfig('kerb_password_text', '')); ?>" size="30" /><br /> |
|
389 Link URL: <input type="text" name="kerb_password_url" value="<?php echo htmlspecialchars(getConfig('kerb_password_url', '')); ?>" size="30" /> |
|
390 </td> |
|
391 </tr> |
|
392 |
|
393 <tr> |
|
394 <th class="subhead" colspan="2"> |
|
395 <input type="submit" name="submit" value="Save changes" /> |
|
396 </th> |
|
397 </tr> |
|
398 </table> |
|
399 </div> |
|
400 <?php |
|
401 echo '</form>'; |
|
402 } |