| 1 |
<?php
|
| 2 |
// $Id: cram.module,v 1.6 2008/01/27 23:17:24 selmanj Exp $
|
| 3 |
|
| 4 |
/**
|
| 5 |
* Implementation of hook_menu().
|
| 6 |
*/
|
| 7 |
function cram_menu($may_cache) {
|
| 8 |
$items = array();
|
| 9 |
|
| 10 |
if ($may_cache) {
|
| 11 |
$items[] = array('path' => 'admin/settings/cram',
|
| 12 |
'title' => t('CRAM settings'),
|
| 13 |
'description' => t('Adjust settings for CRAM secure login.'),
|
| 14 |
'callback' => 'drupal_get_form',
|
| 15 |
'callback arguments' => array('cram_admin_settings'),
|
| 16 |
'access' => user_access('administer site configuration'),
|
| 17 |
'type' => MENU_NORMAL_ITEM,
|
| 18 |
);
|
| 19 |
}
|
| 20 |
|
| 21 |
return $items;
|
| 22 |
}
|
| 23 |
|
| 24 |
/**
|
| 25 |
* Cram settings page in the admin menu.
|
| 26 |
*/
|
| 27 |
function cram_admin_settings() {
|
| 28 |
$form = array();
|
| 29 |
$form['cram_default_enabled'] = array(
|
| 30 |
'#type' => 'radios',
|
| 31 |
'#title' => t('CRAM Enabled by default'),
|
| 32 |
'#default_value' => variable_get('cram_default_enabled', 0),
|
| 33 |
'#options' => array(t('Default to insecure login'), t('Default to CRAM login')),
|
| 34 |
);
|
| 35 |
return system_settings_form($form);
|
| 36 |
}
|
| 37 |
|
| 38 |
/**
|
| 39 |
* Implementation of hook_auth().
|
| 40 |
*/
|
| 41 |
function cram_auth($username, $pass, $server) {
|
| 42 |
// does the user already exist? we don't want to authenticate non-existant users
|
| 43 |
$user = user_load(array('name' => $username));
|
| 44 |
if ($user === FALSE) {
|
| 45 |
return FALSE;
|
| 46 |
}
|
| 47 |
$nonce_array = explode('.', $_POST['cram_nonce']);
|
| 48 |
// check to see if we have a valid nonce
|
| 49 |
$query = "SELECT nonce, issued, valid FROM {cram_nonce} WHERE nonce='%s' AND issued='%d' AND issued > '%d'";
|
| 50 |
if (db_num_rows(db_query($query, $nonce_array[0], $nonce_array[1], time()-60)) == 0) {
|
| 51 |
return FALSE;
|
| 52 |
}
|
| 53 |
|
| 54 |
if ($pass != cram_hmac_md5($user->pass, $_POST['cram_nonce'])) {
|
| 55 |
return FALSE;
|
| 56 |
}
|
| 57 |
// we found a match! immediately expire the nonce
|
| 58 |
db_query("UPDATE {cram_nonce} SET valid=0 WHERE nonce='%s' AND issued='%d'", $nonce_array[0], $nonce_array[1]);
|
| 59 |
return TRUE;
|
| 60 |
}
|
| 61 |
/**
|
| 62 |
* Implementation of hook_form_alter().
|
| 63 |
*/
|
| 64 |
function cram_form_alter($form_id, &$form) {
|
| 65 |
if ($form_id == 'user_login' || $form_id == 'user_login_block') {
|
| 66 |
if (_cram_enabled()) {
|
| 67 |
$module_path = drupal_get_path('module', 'cram');
|
| 68 |
// Make sure the md5.js file is available.
|
| 69 |
if (!file_exists("$module_path/md5.js")) {
|
| 70 |
watchdog('cram', "md5.js was not found in $module_path. See the INSTALL.txt file for details.", WATCHDOG_ERROR);
|
| 71 |
return;
|
| 72 |
}
|
| 73 |
// add our md5 and cram javascript to the page view
|
| 74 |
drupal_add_js("$module_path/md5.js", 'module');
|
| 75 |
drupal_add_js("$module_path/cram.js", 'module');
|
| 76 |
// add a hidden input for the nonce
|
| 77 |
$form['cram_nonce'] = array(
|
| 78 |
'#type' => 'hidden',
|
| 79 |
'#value' => cram_get_nonce(),
|
| 80 |
);
|
| 81 |
$form['#prefix'] = l('Click here to revert to plaintext login.', $_GET['q'], array(), 'cram_enabled=0');
|
| 82 |
} else {
|
| 83 |
$form['#prefix'] = l('Click here to log in using CRAM secure login.', $_GET['q'], array(), 'cram_enabled=1');
|
| 84 |
}
|
| 85 |
}
|
| 86 |
}
|
| 87 |
|
| 88 |
/**
|
| 89 |
* Helper function to determine whether or not to use CRAM.
|
| 90 |
*/
|
| 91 |
function _cram_enabled() {
|
| 92 |
// first, see if the user is requesting a change
|
| 93 |
if (isset($_GET['cram_enabled']) && ($_GET['cram_enabled'] == '1' || $_GET['cram_enabled'] == '0')) {
|
| 94 |
return $_GET['cram_enabled'];
|
| 95 |
}
|
| 96 |
// return the sites default settings
|
| 97 |
return variable_get('cram_default_enabled', FALSE);
|
| 98 |
}
|
| 99 |
|
| 100 |
/**
|
| 101 |
* Generates a new nonce.
|
| 102 |
*/
|
| 103 |
function cram_get_nonce() {
|
| 104 |
// generate our nonce
|
| 105 |
$nonce = md5(mt_rand() . getmypid() . $_SERVER['REMOTE_ADDR']);
|
| 106 |
$issued = time();
|
| 107 |
|
| 108 |
db_query("INSERT INTO {cram_nonce} (nonce, issued, valid) VALUES ( '%s', '%d', 1)", $nonce, $issued);
|
| 109 |
|
| 110 |
return "$nonce.$issued";
|
| 111 |
}
|
| 112 |
|
| 113 |
/**
|
| 114 |
* Implementation of hook_cron().
|
| 115 |
*/
|
| 116 |
function cram_cron() {
|
| 117 |
db_query("DELETE FROM {cram_nonce} WHERE issued < '%d' OR valid = 0", time()-60);
|
| 118 |
}
|
| 119 |
|
| 120 |
/**
|
| 121 |
* Calculate the HMAC of a message using MD5
|
| 122 |
*/
|
| 123 |
function cram_hmac_md5($key, $message) {
|
| 124 |
// make our key 16 bytes either by hashing or padding
|
| 125 |
if (strlen($key) > 64) {
|
| 126 |
$key = pack('H*', md5($key));
|
| 127 |
}
|
| 128 |
if (strlen($key) < 64) {
|
| 129 |
$key = str_pad($key, 64, "\0");
|
| 130 |
}
|
| 131 |
|
| 132 |
$opad = preg_split('//', $key, -1, PREG_SPLIT_NO_EMPTY);
|
| 133 |
$ipad = $opad;
|
| 134 |
for ($i = 0; $i < 64; $i++) {
|
| 135 |
$opad[$i] = chr(ord($opad[$i]) ^ 0x5c);
|
| 136 |
$ipad[$i] = chr(ord($ipad[$i]) ^ 0x36);
|
| 137 |
}
|
| 138 |
$opad = join($opad);
|
| 139 |
$ipad = join($ipad);
|
| 140 |
|
| 141 |
return md5($opad . pack('H*', md5($ipad . $message)));
|
| 142 |
}
|